diff options
Diffstat (limited to 'runtime/msg.c')
-rw-r--r-- | runtime/msg.c | 1060 |
1 files changed, 759 insertions, 301 deletions
diff --git a/runtime/msg.c b/runtime/msg.c index a5c5281..d911b8b 100644 --- a/runtime/msg.c +++ b/runtime/msg.c @@ -7,7 +7,7 @@ * 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-2013 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007-2014 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * @@ -41,9 +41,9 @@ #endif #include <netdb.h> #include <libestr.h> -#include <json/json.h> +#include <json.h> /* For struct json_object_iter, should not be necessary in future versions */ -#include <json/json_object_private.h> +#include <json_object_private.h> #if HAVE_MALLOC_H # include <malloc.h> #endif @@ -65,6 +65,13 @@ #include "net.h" #include "var.h" #include "rsconf.h" +#include "parserif.h" + +/* TODO: move the global variable root to the config object - had no time to to it + * right now before vacation -- rgerhards, 2013-07-22 + */ +static pthread_rwlock_t glblVars_rwlock; +struct json_object *global_var_root = NULL; /* static data */ DEFobjStaticHelpers @@ -75,6 +82,8 @@ DEFobjCurrIf(prop) DEFobjCurrIf(net) DEFobjCurrIf(var) +static char *one_digit[10] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; + static char *two_digits[100] = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", @@ -87,10 +96,36 @@ static char *two_digits[100] = { "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95", "96", "97", "98", "99"}; +static char *wdayNames[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + +/* The following is a table of supported years. This permits us + * to avoid dynamic memory allocation. Note that the time-based + * algos need to be upgraded after the year 2099 in any case. + * Quite honestly, I don't expect that this is a real problem ;) + */ +static char *years[] = { + "1967", "1968", "1969", "1970", "1971", "1972", "1973", "1974", + "1975", "1976", "1977", "1978", "1979", "1980", "1981", "1982", + "1983", "1984", "1985", "1986", "1987", "1988", "1989", "1990", + "1991", "1992", "1993", "1994", "1995", "1996", "1997", "1998", + "1999", "2000", "2001", "2002", "2003", "2004", "2005", "2006", + "2007", "2008", "2009", "2010", "2011", "2012", "2013", "2014", + "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", + "2023", "2024", "2025", "2026", "2027", "2028", "2029", "2030", + "2031", "2032", "2033", "2034", "2035", "2036", "2037", "2038", + "2039", "2040", "2041", "2042", "2043", "2044", "2045", "2046", + "2047", "2048", "2049", "2050", "2051", "2052", "2053", "2054", + "2055", "2056", "2057", "2058", "2059", "2060", "2061", "2062", + "2063", "2064", "2065", "2066", "2067", "2068", "2069", "2070", + "2071", "2072", "2073", "2074", "2075", "2076", "2077", "2078", + "2079", "2080", "2081", "2082", "2083", "2084", "2085", "2086", + "2087", "2088", "2089", "2090", "2091", "2092", "2093", "2094", + "2095", "2096", "2097", "2098", "2099" }; + static struct { uchar *pszName; short lenName; -} syslog_pri_names[192] = { +} syslog_pri_names[200] = { { UCHAR_CONSTANT("0"), 3}, { UCHAR_CONSTANT("1"), 3}, { UCHAR_CONSTANT("2"), 3}, @@ -282,22 +317,30 @@ static struct { { UCHAR_CONSTANT("188"), 5}, { UCHAR_CONSTANT("189"), 5}, { UCHAR_CONSTANT("190"), 5}, - { UCHAR_CONSTANT("191"), 5} + { UCHAR_CONSTANT("191"), 5}, + { UCHAR_CONSTANT("192"), 5}, + { UCHAR_CONSTANT("193"), 5}, + { UCHAR_CONSTANT("194"), 5}, + { UCHAR_CONSTANT("195"), 5}, + { UCHAR_CONSTANT("196"), 5}, + { UCHAR_CONSTANT("197"), 5}, + { UCHAR_CONSTANT("198"), 5}, + { UCHAR_CONSTANT("199"), 5} }; static char hexdigit[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /*syslog facility names (as of RFC5424) */ -static char *syslog_fac_names[24] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", +static char *syslog_fac_names[LOG_NFACILITIES] = { "kern", "user", "mail", "daemon", "auth", "syslog", "lpr", "news", "uucp", "cron", "authpriv", "ftp", "ntp", "audit", "alert", "clock", "local0", "local1", "local2", "local3", - "local4", "local5", "local6", "local7" }; + "local4", "local5", "local6", "local7", "invld" }; /* length of the facility names string (for optimizatiions) */ -static short len_syslog_fac_names[24] = { 4, 4, 4, 6, 4, 6, 3, +static short len_syslog_fac_names[LOG_NFACILITIES] = { 4, 4, 4, 6, 4, 6, 3, 4, 4, 4, 8, 3, 3, 5, 5, 5, 6, 6, 6, 6, - 6, 6, 6, 6 }; + 6, 6, 6, 6, 5 }; /* table of severity names (in numerical order)*/ static char *syslog_severity_names[8] = { "emerg", "alert", "crit", "err", "warning", "notice", "info", "debug" }; @@ -307,8 +350,8 @@ static short len_syslog_severity_names[8] = { 5, 5, 4, 3, 7, 6, 4, 5 }; * and facility values to a numerical string... -- rgerhars, 2009-06-17 */ -static char *syslog_number_names[24] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", - "15", "16", "17", "18", "19", "20", "21", "22", "23" }; +static char *syslog_number_names[LOG_NFACILITIES] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", + "15", "16", "17", "18", "19", "20", "21", "22", "23", "24" }; /* global variables */ #if defined(HAVE_MALLOC_TRIM) && !defined(HAVE_ATOMIC_BUILTINS) @@ -316,8 +359,8 @@ static pthread_mutex_t mutTrimCtr; /* mutex to handle malloc trim */ #endif /* some forward declarations */ -static int getAPPNAMELen(msg_t *pM, sbool bLockMutex); -static rsRetVal jsonPathFindParent(msg_t *pM, uchar *name, uchar *leaf, struct json_object **parent, int bCreate); +static int getAPPNAMELen(msg_t * const pM, sbool bLockMutex); +static rsRetVal jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf, struct json_object **parent, int bCreate); static uchar * jsonPathGetLeaf(uchar *name, int lenName); static struct json_object *jsonDeepCopy(struct json_object *src); @@ -372,23 +415,16 @@ void MsgSetRcvFromWithoutAddRef(msg_t *pThis, prop_t *new) * If ruleset cannot be found, no update is done. */ static void -MsgSetRulesetByName(msg_t *pMsg, cstr_t *rulesetName) +MsgSetRulesetByName(msg_t * const pMsg, cstr_t *rulesetName) { rulesetGetRuleset(runConf, &(pMsg->pRuleset), rsCStrGetSzStrNoNULL(rulesetName)); } - -static inline int getProtocolVersion(msg_t *pM) -{ - return(pM->iProtocolVersion); -} - - /* do a DNS reverse resolution, if not already done, reflect status * rgerhards, 2009-11-16 */ static inline rsRetVal -resolveDNS(msg_t *pMsg) { +resolveDNS(msg_t * const pMsg) { rsRetVal localRet; prop_t *propFromHost = NULL; prop_t *ip; @@ -419,7 +455,7 @@ finalize_it: static inline void -getInputName(msg_t *pM, uchar **ppsz, int *plen) +getInputName(msg_t * const pM, uchar **ppsz, int *plen) { BEGINfunc if(pM == NULL || pM->pInputName == NULL) { @@ -433,7 +469,7 @@ getInputName(msg_t *pM, uchar **ppsz, int *plen) static inline uchar* -getRcvFromIP(msg_t *pM) +getRcvFromIP(msg_t * const pM) { uchar *psz; int len; @@ -452,14 +488,12 @@ getRcvFromIP(msg_t *pM) } -/* map a property name (C string) to a property ID */ +/* map a property name (string) to a property ID */ rsRetVal -propNameStrToID(uchar *pName, propid_t *pPropID) +propNameToID(uchar *pName, propid_t *pPropID) { DEFiRet; - assert(pName != NULL); - /* sometimes there are aliases to the original MonitoWare * property names. These come after || in the ifs below. */ if(!strcmp((char*) pName, "msg")) { @@ -507,6 +541,8 @@ propNameStrToID(uchar *pName, propid_t *pPropID) *pPropID = PROP_PROCID; } else if(!strcmp((char*) pName, "msgid")) { *pPropID = PROP_MSGID; + } else if(!strcmp((char*) pName, "jsonmesg")) { + *pPropID = PROP_JSONMESG; } else if(!strcmp((char*) pName, "parsesuccess")) { *pPropID = PROP_PARSESUCCESS; #ifdef USE_LIBUUID @@ -534,13 +570,18 @@ propNameStrToID(uchar *pName, propid_t *pPropID) *pPropID = PROP_SYS_MYHOSTNAME; } else if(!strcmp((char*) pName, "$!all-json")) { *pPropID = PROP_CEE_ALL_JSON; - } else if(!strncmp((char*) pName, "$!", 2)) { - *pPropID = PROP_CEE; } else if(!strcmp((char*) pName, "$bom")) { *pPropID = PROP_SYS_BOM; } else if(!strcmp((char*) pName, "$uptime")) { *pPropID = PROP_SYS_UPTIME; + } else if(!strncmp((char*) pName, "$!", 2) || pName[0] == '!') { + *pPropID = PROP_CEE; + } else if(!strncmp((char*) pName, "$.", 2) || pName[0] == '.') { + *pPropID = PROP_LOCAL_VAR; + } else if(!strncmp((char*) pName, "$/", 2) || pName[0] == '/') { + *pPropID = PROP_GLOBAL_VAR; } else { + DBGPRINTF("PROP_INVALID for name '%s'\n", pName); *pPropID = PROP_INVALID; iRet = RS_RET_VAR_NOT_FOUND; } @@ -549,21 +590,6 @@ propNameStrToID(uchar *pName, propid_t *pPropID) } -/* map a property name (string) to a property ID */ -rsRetVal -propNameToID(cstr_t *pCSPropName, propid_t *pPropID) -{ - uchar *pName; - DEFiRet; - - assert(pCSPropName != NULL); - assert(pPropID != NULL); - pName = rsCStrGetSzStrNoNULL(pCSPropName); - iRet = propNameStrToID(pName, pPropID); - RETiRet; -} - - /* map a property ID to a name string (useful for displaying) */ uchar *propIDToName(propid_t propID) { @@ -612,6 +638,8 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("procid"); case PROP_MSGID: return UCHAR_CONSTANT("msgid"); + case PROP_JSONMESG: + return UCHAR_CONSTANT("jsonmesg"); case PROP_PARSESUCCESS: return UCHAR_CONSTANT("parsesuccess"); case PROP_SYS_NOW: @@ -634,6 +662,8 @@ uchar *propIDToName(propid_t propID) return UCHAR_CONSTANT("$MYHOSTNAME"); case PROP_CEE: return UCHAR_CONSTANT("*CEE-based property*"); + case PROP_LOCAL_VAR: + return UCHAR_CONSTANT("*LOCAL_VARIABLE*"); case PROP_CEE_ALL_JSON: return UCHAR_CONSTANT("$!all-json"); case PROP_SYS_BOM: @@ -678,8 +708,8 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->flowCtlType = 0; pM->bParseSuccess = 0; pM->iRefCount = 1; - pM->iSeverity = -1; - pM->iFacility = -1; + pM->iSeverity = LOG_DEBUG; + pM->iFacility = LOG_INVLD; pM->iLenPROGNAME = -1; pM->offAfterPRI = 0; pM->offMSG = -1; @@ -699,7 +729,7 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->pszTIMESTAMP3339 = NULL; pM->pszTIMESTAMP_MySQL = NULL; pM->pszTIMESTAMP_PgSQL = NULL; - pM->pCSStrucData = NULL; + pM->pszStrucData = NULL; pM->pCSAPPNAME = NULL; pM->pCSPROCID = NULL; pM->pCSMSGID = NULL; @@ -708,6 +738,8 @@ static inline rsRetVal msgBaseConstruct(msg_t **ppThis) pM->rcvFrom.pRcvFrom = NULL; pM->pRuleset = NULL; pM->json = NULL; + pM->localvars = NULL; + pM->dfltTZ[0] = '\0'; memset(&pM->tRcvdAt, 0, sizeof(pM->tRcvdAt)); memset(&pM->tTIMESTAMP, 0, sizeof(pM->tTIMESTAMP)); pM->TAG.pszTAG = NULL; @@ -841,10 +873,9 @@ CODESTARTobjDestruct(msg) free(pThis->pszRcvdAt_PgSQL); free(pThis->pszTIMESTAMP_MySQL); free(pThis->pszTIMESTAMP_PgSQL); + free(pThis->pszStrucData); if(pThis->iLenPROGNAME >= CONF_PROGNAME_BUFSIZE) free(pThis->PROGNAME.ptr); - if(pThis->pCSStrucData != NULL) - rsCStrDestruct(&pThis->pCSStrucData); if(pThis->pCSAPPNAME != NULL) rsCStrDestruct(&pThis->pCSAPPNAME); if(pThis->pCSPROCID != NULL) @@ -853,6 +884,8 @@ CODESTARTobjDestruct(msg) rsCStrDestruct(&pThis->pCSMSGID); if(pThis->json != NULL) json_object_put(pThis->json); + if(pThis->localvars != NULL) + json_object_put(pThis->localvars); if(pThis->pszUUID != NULL) free(pThis->pszUUID); # ifndef HAVE_ATOMIC_BUILTINS @@ -994,14 +1027,21 @@ msg_t* MsgDup(msg_t* pOld) tmpCOPYSZ(HOSTNAME); } } + if(pOld->pszStrucData == NULL) { + pNew->pszStrucData = NULL; + } else { + pNew->pszStrucData = (uchar*)strdup((char*)pOld->pszStrucData); + pNew->lenStrucData = pOld->lenStrucData; + } - tmpCOPYCSTR(StrucData); tmpCOPYCSTR(APPNAME); tmpCOPYCSTR(PROCID); tmpCOPYCSTR(MSGID); if(pOld->json != NULL) pNew->json = jsonDeepCopy(pOld->json); + if(pOld->localvars != NULL) + pNew->localvars = jsonDeepCopy(pOld->localvars); /* 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. @@ -1056,12 +1096,17 @@ static rsRetVal MsgSerialize(msg_t *pThis, strm_t *pStrm) CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFrom"), PROPTYPE_PSZ, (void*) psz)); psz = getRcvFromIP(pThis); CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvFromIP"), PROPTYPE_PSZ, (void*) psz)); + psz = pThis->pszStrucData; + CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("pszRcvStrucData"), PROPTYPE_PSZ, (void*) psz)); if(pThis->json != NULL) { psz = (uchar*) json_object_get_string(pThis->json); CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("json"), PROPTYPE_PSZ, (void*) psz)); } + if(pThis->localvars != NULL) { + psz = (uchar*) json_object_get_string(pThis->localvars); + CHKiRet(obj.SerializeProp(pStrm, UCHAR_CONSTANT("localvars"), PROPTYPE_PSZ, (void*) psz)); + } - objSerializePTR(pStrm, pCSStrucData, CSTR); objSerializePTR(pStrm, pCSAPPNAME, CSTR); objSerializePTR(pStrm, pCSPROCID, CSTR); objSerializePTR(pStrm, pCSMSGID, CSTR); @@ -1107,13 +1152,12 @@ reinitVar(var_t *pVar) */ #define isProp(name) !rsCStrSzStrCmp(pVar->pcsName, (uchar*) name, sizeof(name) - 1) rsRetVal -MsgDeserialize(msg_t *pMsg, strm_t *pStrm) +MsgDeserialize(msg_t * const pMsg, strm_t *pStrm) { prop_t *myProp; prop_t *propRcvFrom = NULL; prop_t *propRcvFromIP = NULL; struct json_tokener *tokener; - struct json_object *json; var_t *pVar = NULL; DEFiRet; @@ -1197,12 +1241,21 @@ MsgDeserialize(msg_t *pMsg, strm_t *pStrm) } if(isProp("json")) { tokener = json_tokener_new(); - json = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr), + pMsg->json = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr), cstrLen(pVar->val.pStr)); + json_tokener_free(tokener); reinitVar(pVar); CHKiRet(objDeserializeProperty(pVar, pStrm)); } - if(isProp("pCSStrucData")) { + if(isProp("localvars")) { + tokener = json_tokener_new(); + pMsg->localvars = json_tokener_parse_ex(tokener, (char*)rsCStrGetSzStrNoNULL(pVar->val.pStr), + cstrLen(pVar->val.pStr)); + json_tokener_free(tokener); + reinitVar(pVar); + CHKiRet(objDeserializeProperty(pVar, pStrm)); + } + if(isProp("pszStrucData")) { MsgSetStructuredData(pMsg, (char*) rsCStrGetSzStrNoNULL(pVar->val.pStr)); reinitVar(pVar); CHKiRet(objDeserializeProperty(pVar, pStrm)); @@ -1255,7 +1308,7 @@ finalize_it: * * pSecondMsgPointer = MsgAddRef(pOrgMsgPointer); */ -msg_t *MsgAddRef(msg_t *pM) +msg_t *MsgAddRef(msg_t * const pM) { assert(pM != NULL); # ifdef HAVE_ATOMIC_BUILTINS @@ -1281,7 +1334,7 @@ msg_t *MsgAddRef(msg_t *pM) * rgerhards, 2005-11-24 * THIS MUST be called with the message lock locked. */ -static rsRetVal aquirePROCIDFromTAG(msg_t *pM) +static rsRetVal aquirePROCIDFromTAG(msg_t * const pM) { register int i; uchar *pszTag; @@ -1292,7 +1345,7 @@ static rsRetVal aquirePROCIDFromTAG(msg_t *pM) if(pM->pCSPROCID != NULL) return RS_RET_OK; /* we are already done ;) */ - if(getProtocolVersion(pM) != 0) + if(msgGetProtocolVersion(pM) != 0) return RS_RET_OK; /* we can only emulate if we have legacy format */ pszTag = (uchar*) ((pM->iLenTAG < CONF_TAG_BUFSIZE) ? pM->TAG.szBuf : pM->TAG.pszTAG); @@ -1346,7 +1399,7 @@ finalize_it: * rgerhards, 2005-10-19 */ static inline rsRetVal -aquireProgramName(msg_t *pM) +aquireProgramName(msg_t * const pM) { int i; uchar *pszTag, *pszProgName; @@ -1376,7 +1429,7 @@ finalize_it: /* Access methods - dumb & easy, not a comment for each ;) */ -void setProtocolVersion(msg_t *pM, int iNewVersion) +void setProtocolVersion(msg_t * const pM, int iNewVersion) { assert(pM != NULL); if(iNewVersion != 0 && iNewVersion != 1) { @@ -1387,7 +1440,7 @@ void setProtocolVersion(msg_t *pM, int iNewVersion) } /* note: string is taken from constant pool, do NOT free */ -char *getProtocolVersionString(msg_t *pM) +char *getProtocolVersionString(msg_t * const pM) { assert(pM != NULL); return(pM->iProtocolVersion ? "1" : "0"); @@ -1397,7 +1450,7 @@ char *getProtocolVersionString(msg_t *pM) /* note: libuuid seems not to be thread-safe, so we need * to get some safeguards in place. */ -static void msgSetUUID(msg_t *pM) +static void msgSetUUID(msg_t * const pM) { size_t lenRes = sizeof(uuid_t) * 2 + 1; char hex_char [] = "0123456789ABCDEF"; @@ -1405,7 +1458,7 @@ static void msgSetUUID(msg_t *pM) uuid_t uuid; static pthread_mutex_t mutUUID = PTHREAD_MUTEX_INITIALIZER; - dbgprintf("[MsgSetUUID] START\n"); + dbgprintf("[MsgSetUUID] START, lenRes %llu\n", (long long unsigned) lenRes); assert(pM != NULL); if((pM->pszUUID = (uchar*) MALLOC(lenRes)) == NULL) { @@ -1419,13 +1472,13 @@ static void msgSetUUID(msg_t *pM) pM->pszUUID[byte_nbr * 2 + 1] = hex_char[uuid [byte_nbr] & 15]; } + pM->pszUUID[lenRes-1] = '\0'; dbgprintf("[MsgSetUUID] UUID : %s LEN: %d \n", pM->pszUUID, (int)lenRes); - pM->pszUUID[lenRes] = '\0'; } dbgprintf("[MsgSetUUID] END\n"); } -void getUUID(msg_t *pM, uchar **pBuf, int *piLen) +void getUUID(msg_t * const pM, uchar **pBuf, int *piLen) { dbgprintf("[getUUID] START\n"); if(pM == NULL) { @@ -1451,7 +1504,7 @@ void getUUID(msg_t *pM, uchar **pBuf, int *piLen) #endif void -getRawMsg(msg_t *pM, uchar **pBuf, int *piLen) +getRawMsg(msg_t * const pM, uchar **pBuf, int *piLen) { if(pM == NULL) { *pBuf= UCHAR_CONSTANT(""); @@ -1471,17 +1524,17 @@ getRawMsg(msg_t *pM, uchar **pBuf, int *piLen) /* note: setMSGLen() is only for friends who really know what they * do. Setting an invalid length can be desasterous! */ -void setMSGLen(msg_t *pM, int lenMsg) +void setMSGLen(msg_t * const pM, int lenMsg) { pM->iLenMSG = lenMsg; } -int getMSGLen(msg_t *pM) +int getMSGLen(msg_t * const pM) { return((pM == NULL) ? 0 : pM->iLenMSG); } -uchar *getMSG(msg_t *pM) +uchar *getMSG(msg_t * const pM) { uchar *ret; if(pM == NULL) @@ -1497,16 +1550,19 @@ uchar *getMSG(msg_t *pM) /* Get PRI value as integer */ -static int getPRIi(msg_t *pM) +static int getPRIi(msg_t * const pM) { - return (pM->iFacility << 3) + (pM->iSeverity); + int pri = (pM->iFacility << 3) + (pM->iSeverity); + if(pri > 191) + pri = LOG_PRI_INVLD; + return pri; } /* Get PRI value in text form */ char * -getPRI(msg_t *pM) +getPRI(msg_t * const pM) { /* PRI is a number in the range 0..191. Thus, we use a simple lookup table to obtain the * string value. It looks a bit clumpsy here in code ;) @@ -1522,7 +1578,7 @@ getPRI(msg_t *pM) char * -getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) +getTimeReported(msg_t * const pM, enum tplFormatTypes eFmt) { BEGINfunc if(pM == NULL) @@ -1587,12 +1643,37 @@ getTimeReported(msg_t *pM, enum tplFormatTypes eFmt) MsgUnlock(pM); } return(pM->pszTIMESTAMP_SecFrac); + case tplFmtWDayName: + return wdayNames[getWeekdayNbr(&pM->tTIMESTAMP)]; + case tplFmtWDay: + return one_digit[getWeekdayNbr(&pM->tTIMESTAMP)]; + case tplFmtMonth: + return two_digits[(int)pM->tTIMESTAMP.month]; + case tplFmtYear: + if(pM->tTIMESTAMP.year >= 1967 && pM->tTIMESTAMP.year <= 2099) + return years[pM->tTIMESTAMP.year - 1967]; + else + return "YEAR OUT OF RANGE(1967-2099)"; + case tplFmtDay: + return two_digits[(int)pM->tTIMESTAMP.day]; + case tplFmtHour: + return two_digits[(int)pM->tTIMESTAMP.hour]; + case tplFmtMinute: + return two_digits[(int)pM->tTIMESTAMP.minute]; + case tplFmtSecond: + return two_digits[(int)pM->tTIMESTAMP.second]; + case tplFmtTZOffsHour: + return two_digits[(int)pM->tTIMESTAMP.OffsetHour]; + case tplFmtTZOffsMin: + return two_digits[(int)pM->tTIMESTAMP.OffsetMinute]; + case tplFmtTZOffsDirection: + return (pM->tTIMESTAMP.OffsetMode == '+')? "+" : "-"; } ENDfunc return "INVALID eFmt OPTION!"; } -static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) +static char *getTimeGenerated(msg_t * const pM, enum tplFormatTypes eFmt) { BEGINfunc if(pM == NULL) @@ -1673,13 +1754,38 @@ static inline char *getTimeGenerated(msg_t *pM, enum tplFormatTypes eFmt) MsgUnlock(pM); } return(pM->pszRcvdAt_SecFrac); + case tplFmtWDayName: + return wdayNames[getWeekdayNbr(&pM->tRcvdAt)]; + case tplFmtWDay: + return one_digit[getWeekdayNbr(&pM->tRcvdAt)]; + case tplFmtMonth: + return two_digits[(int)pM->tRcvdAt.month]; + case tplFmtYear: + if(pM->tRcvdAt.year >= 1967 && pM->tRcvdAt.year <= 2099) + return years[pM->tRcvdAt.year - 1967]; + else + return "YEAR OUT OF RANGE(1967-2099)"; + case tplFmtDay: + return two_digits[(int)pM->tRcvdAt.day]; + case tplFmtHour: + return two_digits[(int)pM->tRcvdAt.hour]; + case tplFmtMinute: + return two_digits[(int)pM->tRcvdAt.minute]; + case tplFmtSecond: + return two_digits[(int)pM->tRcvdAt.second]; + case tplFmtTZOffsHour: + return two_digits[(int)pM->tRcvdAt.OffsetHour]; + case tplFmtTZOffsMin: + return two_digits[(int)pM->tRcvdAt.OffsetMinute]; + case tplFmtTZOffsDirection: + return (pM->tRcvdAt.OffsetMode == '+')? "+" : "-"; } ENDfunc return "INVALID eFmt OPTION!"; } -static inline char *getSeverity(msg_t *pM) +static inline char *getSeverity(msg_t * const pM) { char *name = NULL; @@ -1696,7 +1802,7 @@ static inline char *getSeverity(msg_t *pM) } -static inline char *getSeverityStr(msg_t *pM) +static inline char *getSeverityStr(msg_t * const pM) { char *name = NULL; @@ -1712,7 +1818,7 @@ static inline char *getSeverityStr(msg_t *pM) return name; } -static inline char *getFacility(msg_t *pM) +static inline char *getFacility(msg_t * const pM) { char *name = NULL; @@ -1728,7 +1834,7 @@ static inline char *getFacility(msg_t *pM) return name; } -static inline char *getFacilityStr(msg_t *pM) +static inline char *getFacilityStr(msg_t * const pM) { char *name = NULL; @@ -1752,7 +1858,7 @@ static inline char *getFacilityStr(msg_t *pM) * rgerhards, 2008-03-14 */ rsRetVal -MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl) +MsgSetFlowControlType(msg_t * const pMsg, flowControl_t eFlowCtl) { DEFiRet; assert(pMsg != NULL); @@ -1767,7 +1873,7 @@ MsgSetFlowControlType(msg_t *pMsg, flowControl_t eFlowCtl) * rgerhards, 2009-06-16 */ rsRetVal -MsgSetAfterPRIOffs(msg_t *pMsg, short offs) +MsgSetAfterPRIOffs(msg_t * const pMsg, short offs) { assert(pMsg != NULL); pMsg->offAfterPRI = offs; @@ -1781,7 +1887,7 @@ MsgSetAfterPRIOffs(msg_t *pMsg, short offs) * which already obtained the lock. So in general, this function here must * only be called when it it safe to do so without it aquiring a lock. */ -rsRetVal MsgSetAPPNAME(msg_t *pMsg, char* pszAPPNAME) +rsRetVal MsgSetAPPNAME(msg_t *__restrict__ const pMsg, const char* pszAPPNAME) { DEFiRet; assert(pMsg != NULL); @@ -1799,7 +1905,7 @@ finalize_it: /* rgerhards 2004-11-24: set PROCID in msg object */ -rsRetVal MsgSetPROCID(msg_t *pMsg, char* pszPROCID) +rsRetVal MsgSetPROCID(msg_t *__restrict__ const pMsg, const char* pszPROCID) { DEFiRet; ISOBJ_TYPE_assert(pMsg, msg); @@ -1820,7 +1926,7 @@ finalize_it: * This must be called WITHOUT the message lock being held. * rgerhards, 2009-06-26 */ -static inline void preparePROCID(msg_t *pM, sbool bLockMutex) +static inline void preparePROCID(msg_t * const pM, sbool bLockMutex) { if(pM->pCSPROCID == NULL) { if(bLockMutex == LOCK_MUTEX) @@ -1848,7 +1954,7 @@ static inline int getPROCIDLen(msg_t *pM, sbool bLockMutex) /* rgerhards, 2005-11-24 */ -char *getPROCID(msg_t *pM, sbool bLockMutex) +char *getPROCID(msg_t * const pM, sbool bLockMutex) { uchar *pszRet; @@ -1868,7 +1974,7 @@ char *getPROCID(msg_t *pM, sbool bLockMutex) /* rgerhards 2004-11-24: set MSGID in msg object */ -rsRetVal MsgSetMSGID(msg_t *pMsg, char* pszMSGID) +rsRetVal MsgSetMSGID(msg_t * const pMsg, const char* pszMSGID) { DEFiRet; ISOBJ_TYPE_assert(pMsg, msg); @@ -1887,7 +1993,7 @@ finalize_it: /* Return state of last parser. If it had success, "OK" is returned, else * "FAIL". All from the constant pool. */ -static inline char *getParseSuccess(msg_t *pM) +static inline char *getParseSuccess(msg_t * const pM) { return (pM->bParseSuccess) ? "OK" : "FAIL"; } @@ -1895,7 +2001,7 @@ static inline char *getParseSuccess(msg_t *pM) /* al, 2011-07-26: LockMsg to avoid race conditions */ -static inline char *getMSGID(msg_t *pM) +static inline char *getMSGID(msg_t * const pM) { if (pM->pCSMSGID == NULL) { return "-"; @@ -1910,15 +2016,104 @@ static inline char *getMSGID(msg_t *pM) /* rgerhards 2012-03-15: set parser success (an integer, acutally bool) */ -void MsgSetParseSuccess(msg_t *pMsg, int bSuccess) +void MsgSetParseSuccess(msg_t * const pMsg, int bSuccess) { assert(pMsg != NULL); pMsg->bParseSuccess = bSuccess; } + +/* return full message as a json string */ +const uchar* +msgGetJSONMESG(msg_t *__restrict__ const pMsg) +{ + struct json_object *json; + struct json_object *jval; + uchar *pRes; /* result pointer */ + rs_size_t bufLen = -1; /* length of string or -1, if not known */ + + json = json_object_new_object(); + + jval = json_object_new_string((char*)getMSG(pMsg)); + json_object_object_add(json, "msg", jval); + + getRawMsg(pMsg, &pRes, &bufLen); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "rawmsg", jval); + + pRes = (uchar*)getTimeReported(pMsg, tplFmtRFC3339Date); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "timereported", jval); + + jval = json_object_new_string(getHOSTNAME(pMsg)); + json_object_object_add(json, "hostname", jval); + + getTAG(pMsg, &pRes, &bufLen); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "syslogtag", jval); + + getInputName(pMsg, &pRes, &bufLen); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "inputname", jval); + + jval = json_object_new_string((char*)getRcvFrom(pMsg)); + json_object_object_add(json, "fromhost", jval); + + jval = json_object_new_string((char*)getRcvFromIP(pMsg)); + json_object_object_add(json, "fromhost-ip", jval); + + jval = json_object_new_string(getPRI(pMsg)); + json_object_object_add(json, "pri", jval); + + jval = json_object_new_string(getFacility(pMsg)); + json_object_object_add(json, "syslogfacility", jval); + + jval = json_object_new_string(getSeverity(pMsg)); + json_object_object_add(json, "syslogseverity", jval); + + pRes = (uchar*)getTimeGenerated(pMsg, tplFmtRFC3339Date); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "timegenerated", jval); + + jval = json_object_new_string((char*)getProgramName(pMsg, LOCK_MUTEX)); + json_object_object_add(json, "programname", jval); + + jval = json_object_new_string(getProtocolVersionString(pMsg)); + json_object_object_add(json, "protocol-version", jval); + + MsgGetStructuredData(pMsg, &pRes, &bufLen); + jval = json_object_new_string((char*)pRes); + json_object_object_add(json, "structured-data", jval); + + jval = json_object_new_string(getAPPNAME(pMsg, LOCK_MUTEX)); + json_object_object_add(json, "app-name", jval); + + jval = json_object_new_string(getPROCID(pMsg, LOCK_MUTEX)); + json_object_object_add(json, "procid", jval); + + jval = json_object_new_string(getMSGID(pMsg)); + json_object_object_add(json, "msgid", jval); + +#ifdef USE_LIBUUID + if(pMsg->pszUUID == NULL) { + jval = NULL; + } else { + getUUID(pMsg, &pRes, &bufLen); + jval = json_object_new_string((char*)pRes); + } + json_object_object_add(json, "uuid", jval); +#endif + + json_object_object_add(json, "$!", pMsg->json); + + pRes = (uchar*) strdup(json_object_get_string(json)); + json_object_put(json); + return pRes; +} + /* rgerhards 2009-06-12: set associated ruleset */ -void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset) +void MsgSetRuleset(msg_t * const pMsg, ruleset_t *pRuleset) { assert(pMsg != NULL); pMsg->pRuleset = pRuleset; @@ -1928,7 +2123,7 @@ void MsgSetRuleset(msg_t *pMsg, ruleset_t *pRuleset) /* set TAG in msg object * (rewritten 2009-06-18 rgerhards) */ -void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf) +void MsgSetTAG(msg_t *__restrict__ const pMsg, const uchar* pszBuf, const size_t lenBuf) { uchar *pBuf; assert(pMsg != NULL); @@ -1961,7 +2156,7 @@ void MsgSetTAG(msg_t *pMsg, uchar* pszBuf, size_t lenBuf) * if there is a TAG and, if not, if it can emulate it. * rgerhards, 2005-11-24 */ -static inline void tryEmulateTAG(msg_t *pM, sbool bLockMutex) +static inline void tryEmulateTAG(msg_t * const pM, sbool bLockMutex) { size_t lenTAG; uchar bufTAG[CONF_TAG_MAXSIZE]; @@ -1975,7 +2170,7 @@ static inline void tryEmulateTAG(msg_t *pM, sbool bLockMutex) return; /* done, no need to emulate */ } - if(getProtocolVersion(pM) == 1) { + if(msgGetProtocolVersion(pM) == 1) { if(!strcmp(getPROCID(pM, MUTEX_ALREADY_LOCKED), "-")) { /* no process ID, use APP-NAME only */ MsgSetTAG(pM, (uchar*) getAPPNAME(pM, MUTEX_ALREADY_LOCKED), getAPPNAMELen(pM, MUTEX_ALREADY_LOCKED)); @@ -1993,7 +2188,7 @@ static inline void tryEmulateTAG(msg_t *pM, sbool bLockMutex) void -getTAG(msg_t *pM, uchar **ppBuf, int *piLen) +getTAG(msg_t * const pM, uchar **ppBuf, int *piLen) { if(pM == NULL) { *ppBuf = UCHAR_CONSTANT(""); @@ -2012,7 +2207,7 @@ getTAG(msg_t *pM, uchar **ppBuf, int *piLen) } -int getHOSTNAMELen(msg_t *pM) +int getHOSTNAMELen(msg_t * const pM) { if(pM == NULL) return 0; @@ -2028,7 +2223,7 @@ int getHOSTNAMELen(msg_t *pM) } -char *getHOSTNAME(msg_t *pM) +char *getHOSTNAME(msg_t * const pM) { if(pM == NULL) return ""; @@ -2049,7 +2244,7 @@ char *getHOSTNAME(msg_t *pM) } -uchar *getRcvFrom(msg_t *pM) +uchar *getRcvFrom(msg_t * const pM) { uchar *psz; int len; @@ -2071,52 +2266,37 @@ uchar *getRcvFrom(msg_t *pM) /* rgerhards 2004-11-24: set STRUCTURED DATA in msg object */ -rsRetVal MsgSetStructuredData(msg_t *pMsg, char* pszStrucData) +rsRetVal MsgSetStructuredData(msg_t * const pMsg, const char* pszStrucData) { DEFiRet; ISOBJ_TYPE_assert(pMsg, msg); - if(pMsg->pCSStrucData == NULL) { - /* we need to obtain the object first */ - CHKiRet(rsCStrConstruct(&pMsg->pCSStrucData)); - } - /* if we reach this point, we have the object */ - iRet = rsCStrSetSzStr(pMsg->pCSStrucData, (uchar*) pszStrucData); - + free(pMsg->pszStrucData); + CHKmalloc(pMsg->pszStrucData = (uchar*)strdup(pszStrucData)); + pMsg->lenStrucData = strlen(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 - */ -static inline char *getStructuredData(msg_t *pM) +/* get the "STRUCTURED-DATA" as sz string, including length */ +void +MsgGetStructuredData(msg_t * const pM, uchar **pBuf, rs_size_t *len) { - uchar *pszRet; - MsgLock(pM); - if(pM->pCSStrucData == NULL) - pszRet = UCHAR_CONSTANT("-"); - else - pszRet = rsCStrGetSzStrNoNULL(pM->pCSStrucData); + if(pM->pszStrucData == NULL) { + *pBuf = UCHAR_CONSTANT("-"), + *len = 1; + } else { + *pBuf = pM->pszStrucData, + *len = pM->lenStrucData; + } MsgUnlock(pM); - return (char*) pszRet; } /* get the "programname" as sz string * rgerhards, 2005-10-19 */ -uchar *getProgramName(msg_t *pM, sbool bLockMutex) +uchar *getProgramName(msg_t * const pM, sbool bLockMutex) { if(pM->iLenPROGNAME == -1) { if(bLockMutex == LOCK_MUTEX) { @@ -2139,13 +2319,13 @@ uchar *getProgramName(msg_t *pM, sbool bLockMutex) * now would like to send out the same one via syslog-protocol. * MUST be called with the Msg Lock locked! */ -static void tryEmulateAPPNAME(msg_t *pM) +static void tryEmulateAPPNAME(msg_t * const pM) { assert(pM != NULL); if(pM->pCSAPPNAME != NULL) return; /* we are already done */ - if(getProtocolVersion(pM) == 0) { + if(msgGetProtocolVersion(pM) == 0) { /* only then it makes sense to emulate */ MsgSetAPPNAME(pM, (char*)getProgramName(pM, MUTEX_ALREADY_LOCKED)); } @@ -2157,7 +2337,7 @@ static void tryEmulateAPPNAME(msg_t *pM) * This must be called WITHOUT the message lock being held. * rgerhards, 2009-06-26 */ -static inline void prepareAPPNAME(msg_t *pM, sbool bLockMutex) +static inline void prepareAPPNAME(msg_t * const pM, sbool bLockMutex) { if(pM->pCSAPPNAME == NULL) { if(bLockMutex == LOCK_MUTEX) @@ -2174,7 +2354,7 @@ static inline void prepareAPPNAME(msg_t *pM, sbool bLockMutex) /* rgerhards, 2005-11-24 */ -char *getAPPNAME(msg_t *pM, sbool bLockMutex) +char *getAPPNAME(msg_t * const pM, sbool bLockMutex) { uchar *pszRet; @@ -2193,7 +2373,7 @@ char *getAPPNAME(msg_t *pM, sbool bLockMutex) /* rgerhards, 2005-11-24 */ -static int getAPPNAMELen(msg_t *pM, sbool bLockMutex) +static int getAPPNAMELen(msg_t * const pM, sbool bLockMutex) { assert(pM != NULL); prepareAPPNAME(pM, bLockMutex); @@ -2215,6 +2395,15 @@ void MsgSetInputName(msg_t *pThis, prop_t *inputName) pThis->pInputName = inputName; } +/* Set default TZ. Note that at most 7 chars are set, as we would + * otherwise overrun our buffer! + */ +void MsgSetDfltTZ(msg_t *pThis, char *tz) +{ + strncpy(pThis->dfltTZ, tz, 7); + pThis->dfltTZ[7] = '\0'; /* ensure 0-Term in case of overflow! */ +} + /* Set the pfrominet socket store, so that we can obtain the peer at some * later time. Note that we do not check if pRcvFrom is already set, so this @@ -2237,7 +2426,6 @@ finalize_it: RETiRet; } - /* rgerhards 2008-09-10: set RcvFrom name in msg object. This calls AddRef() * on the property, because this must be done in all current cases and there * is no case expected where this may not be necessary. @@ -2258,7 +2446,7 @@ void MsgSetRcvFrom(msg_t *pThis, prop_t *new) * name (but it works only for the immediate previous). * rgerhards, 2009-06-31 */ -void MsgSetRcvFromStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp) +void MsgSetRcvFromStr(msg_t * const pThis, const uchar *psz, const int len, prop_t **ppProp) { assert(pThis != NULL); assert(ppProp != NULL); @@ -2293,7 +2481,7 @@ rsRetVal MsgSetRcvFromIP(msg_t *pThis, prop_t *new) * name (but it works only for the immediate previous). * rgerhards, 2009-06-31 */ -rsRetVal MsgSetRcvFromIPStr(msg_t *pThis, uchar *psz, int len, prop_t **ppProp) +rsRetVal MsgSetRcvFromIPStr(msg_t *const pThis, const uchar *psz, const int len, prop_t **ppProp) { DEFiRet; assert(pThis != NULL); @@ -2316,7 +2504,7 @@ finalize_it: * we need it. The rest of the code already knows how to handle an * unset HOSTNAME. */ -void MsgSetHOSTNAME(msg_t *pThis, uchar* pszHOSTNAME, int lenHOSTNAME) +void MsgSetHOSTNAME(msg_t *pThis, const uchar* pszHOSTNAME, const int lenHOSTNAME) { assert(pThis != NULL); @@ -2342,7 +2530,7 @@ void MsgSetHOSTNAME(msg_t *pThis, uchar* pszHOSTNAME, int lenHOSTNAME) * (exactly by one). This can happen if we have a message that does not * contain any MSG part. */ -void MsgSetMSGoffs(msg_t *pMsg, short offs) +void MsgSetMSGoffs(msg_t * const pMsg, short offs) { ISOBJ_TYPE_assert(pMsg, msg); pMsg->offMSG = offs; @@ -2367,7 +2555,7 @@ void MsgSetMSGoffs(msg_t *pMsg, short offs) * the caller is responsible for freeing it. * rgerhards, 2009-06-23 */ -rsRetVal MsgReplaceMSG(msg_t *pThis, uchar* pszMSG, int lenMSG) +rsRetVal MsgReplaceMSG(msg_t *pThis, const uchar* pszMSG, int lenMSG) { int lenNew; uchar *bufNew; @@ -2400,12 +2588,14 @@ finalize_it: * terminated by '\0'. * rgerhards, 2009-06-16 */ -void MsgSetRawMsg(msg_t *pThis, char* pszRawMsg, size_t lenMsg) +void MsgSetRawMsg(msg_t *pThis, const char* pszRawMsg, size_t lenMsg) { + int deltaSize; assert(pThis != NULL); if(pThis->pszRawMsg != pThis->szRawMsg) free(pThis->pszRawMsg); + deltaSize = lenMsg - pThis->iLenRawMsg; pThis->iLenRawMsg = lenMsg; if(pThis->iLenRawMsg < CONF_RAWMSG_BUFSIZE) { /* small enough: use fixed buffer (faster!) */ @@ -2418,6 +2608,11 @@ void MsgSetRawMsg(msg_t *pThis, char* pszRawMsg, size_t lenMsg) memcpy(pThis->pszRawMsg, pszRawMsg, pThis->iLenRawMsg); pThis->pszRawMsg[pThis->iLenRawMsg] = '\0'; /* this also works with truncation! */ + /* correct other information */ + if(pThis->iLenRawMsg > pThis->offMSG) + pThis->iLenMSG += deltaSize; + else + pThis->iLenMSG = 0; } @@ -2426,7 +2621,7 @@ void MsgSetRawMsg(msg_t *pThis, char* pszRawMsg, size_t lenMsg) * try to remove it altogether). * rgerhards, 2009-06-16 */ -void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg) +void MsgSetRawMsgWOSize(msg_t * const pMsg, char* pszRawMsg) { MsgSetRawMsg(pMsg, pszRawMsg, strlen(pszRawMsg)); } @@ -2442,11 +2637,11 @@ void MsgSetRawMsgWOSize(msg_t *pMsg, char* pszRawMsg) char *textpri(char *pRes, int pri) { assert(pRes != NULL); - memcpy(pRes, syslog_fac_names[LOG_FAC(pri)], len_syslog_fac_names[LOG_FAC(pri)]); - pRes[len_syslog_fac_names[LOG_FAC(pri)]] = '.'; - memcpy(pRes+len_syslog_fac_names[LOG_FAC(pri)]+1, - syslog_severity_names[LOG_PRI(pri)], - len_syslog_severity_names[LOG_PRI(pri)]+1 /* for \0! */); + memcpy(pRes, syslog_fac_names[pri2fac(pri)], len_syslog_fac_names[pri2fac(pri)]); + pRes[len_syslog_fac_names[pri2fac(pri)]] = '.'; + memcpy(pRes+len_syslog_fac_names[pri2fac(pri)]+1, + syslog_severity_names[pri2sev(pri)], + len_syslog_severity_names[pri2sev(pri)]+1 /* for \0! */); return pRes; } @@ -2463,11 +2658,17 @@ typedef enum ENOWType { NOW_NOW, NOW_YEAR, NOW_MONTH, NOW_DAY, NOW_HOUR, NOW_HHO static uchar *getNOW(eNOWType eNow, struct syslogTime *t) { uchar *pBuf; + struct syslogTime tt; if((pBuf = (uchar*) MALLOC(sizeof(uchar) * tmpBUFSIZE)) == NULL) { return NULL; } + if(t == NULL) { /* can happen if called via script engine */ + datetime.getCurrTime(&tt, NULL); + t = &tt; + } + if(t->year == 0) { /* not yet set! */ datetime.getCurrTime(t, NULL); } @@ -2495,10 +2696,10 @@ static uchar *getNOW(eNOWType eNow, struct syslogTime *t) memcpy(pBuf, two_digits[(int)t->hour], 3); break; case NOW_HHOUR: - memcpy(pBuf, two_digits[t->hour/30], 3); + memcpy(pBuf, two_digits[t->minute/30], 3); break; case NOW_QHOUR: - memcpy(pBuf, two_digits[t->hour/15], 3); + memcpy(pBuf, two_digits[t->minute/15], 3); break; case NOW_MINUTE: memcpy(pBuf, two_digits[(int)t->minute], 3); @@ -2510,12 +2711,12 @@ static uchar *getNOW(eNOWType eNow, struct syslogTime *t) #undef tmpBUFSIZE /* clean up */ -/* Get a CEE-Property as string value*/ +/* Get a JSON-Property as string value (used for various types of JSON-based vars) */ rsRetVal -getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed) +getJSONPropVal(msg_t * const pMsg, msgPropDescr_t *pProp, uchar **pRes, rs_size_t *buflen, unsigned short *pbMustBeFreed) { - uchar *name = NULL; uchar *leaf; + struct json_object *jroot; struct json_object *parent; struct json_object *field; DEFiRet; @@ -2523,15 +2724,26 @@ getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, un if(*pbMustBeFreed) free(*pRes); *pRes = NULL; - // TODO: mutex? - if(pM->json == NULL) goto finalize_it; - if(!es_strbufcmp(propName, (uchar*)"!", 1)) { - field = pM->json; + if(pProp->id == PROP_CEE) { + jroot = pMsg->json; + } else if(pProp->id == PROP_LOCAL_VAR) { + jroot = pMsg->localvars; + } else if(pProp->id == PROP_GLOBAL_VAR) { + pthread_rwlock_rdlock(&glblVars_rwlock); + jroot = global_var_root; } else { - name = (uchar*)es_str2cstr(propName, NULL); - leaf = jsonPathGetLeaf(name, ustrlen(name)); - CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1)); + DBGPRINTF("msgGetJSONPropVal; invalid property id %d\n", + pProp->id); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + if(jroot == NULL) goto finalize_it; + + if(!strcmp((char*)pProp->name, "!")) { + field = jroot; + } else { + leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen); + CHKiRet(jsonPathFindParent(jroot, pProp->name, leaf, &parent, 1)); field = json_object_object_get(parent, (char*)leaf); } if(field != NULL) { @@ -2541,7 +2753,8 @@ getCEEPropVal(msg_t *pM, es_str_t *propName, uchar **pRes, rs_size_t *buflen, un } finalize_it: - free(name); + if(pProp->id == PROP_GLOBAL_VAR) + pthread_rwlock_unlock(&glblVars_rwlock); if(*pRes == NULL) { /* could not find any value, so set it to empty */ *pRes = (unsigned char*)""; @@ -2551,55 +2764,71 @@ finalize_it: } -/* Get a CEE-Property as native json object - */ +/* Get a JSON-based-variable as native json object */ rsRetVal -msgGetCEEPropJSON(msg_t *pM, es_str_t *propName, struct json_object **pjson) +msgGetJSONPropJSON(msg_t * const pMsg, msgPropDescr_t *pProp, struct json_object **pjson) { - uchar *name = NULL; + struct json_object *jroot; uchar *leaf; struct json_object *parent; DEFiRet; - // TODO: mutex? - if(pM->json == NULL) { + if(pProp->id == PROP_CEE) { + jroot = pMsg->json; + } else if(pProp->id == PROP_LOCAL_VAR) { + jroot = pMsg->localvars; + } else if(pProp->id == PROP_GLOBAL_VAR) { + pthread_rwlock_rdlock(&glblVars_rwlock); + jroot = global_var_root; + } else { + DBGPRINTF("msgGetJSONPropJSON; invalid property id %d\n", + pProp->id); + ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + if(jroot == NULL) { + DBGPRINTF("msgGetJSONPropJSON; jroot empty for property %s\n", + pProp->name); ABORT_FINALIZE(RS_RET_NOT_FOUND); } - if(!es_strbufcmp(propName, (uchar*)"!", 1)) { - *pjson = pM->json; + if(!strcmp((char*)pProp->name, "!")) { + *pjson = jroot; FINALIZE; } - name = (uchar*)es_str2cstr(propName, NULL); - leaf = jsonPathGetLeaf(name, ustrlen(name)); - CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1)); + leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen); + CHKiRet(jsonPathFindParent(jroot, pProp->name, leaf, &parent, 1)); *pjson = json_object_object_get(parent, (char*)leaf); if(*pjson == NULL) { ABORT_FINALIZE(RS_RET_NOT_FOUND); } finalize_it: - free(name); + if(pProp->id == PROP_GLOBAL_VAR) + pthread_rwlock_unlock(&glblVars_rwlock); RETiRet; } /* Encode a JSON value and add it to provided string. Note that * the string object may be NULL. In this case, it is created - * if and only if escaping is needed. + * if and only if escaping is needed. if escapeAll is false, previously + * escaped strings are left as is */ static rsRetVal -jsonAddVal(uchar *pSrc, unsigned buflen, es_str_t **dst) +jsonAddVal(uchar *pSrc, unsigned buflen, es_str_t **dst, int escapeAll) { unsigned char c; es_size_t i; char numbuf[4]; + unsigned ni; + unsigned char nc; int j; DEFiRet; for(i = 0 ; i < buflen ; ++i) { c = pSrc[i]; - if( (c >= 0x23 && c <= 0x5b) + if( (c >= 0x23 && c <= 0x2e) + || (c >= 0x30 && c <= 0x5b) || (c >= 0x5d /* && c <= 0x10FFFF*/) || c == 0x20 || c == 0x21) { /* no need to escape */ @@ -2629,6 +2858,23 @@ jsonAddVal(uchar *pSrc, unsigned buflen, es_str_t **dst) es_addBuf(dst, "\\/", 2); break; case '\\': + if (escapeAll == RSFALSE) { + ni = i + 1; + if (ni <= buflen) { + nc = pSrc[ni]; + + /* Attempt to not double encode */ + if ( nc == '"' || nc == '/' || nc == '\\' || nc == 'b' || nc == 'f' + || nc == 'n' || nc == 'r' || nc == 't' || nc == 'u') { + + es_addChar(dst, c); + es_addChar(dst, nc); + i = ni; + break; + } + } + } + es_addBuf(dst, "\\\\", 2); break; case '\010': @@ -2674,7 +2920,7 @@ finalize_it: * rgerhards, 2012-03-16 */ static rsRetVal -jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen) +jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen, int escapeAll) { unsigned buflen; uchar *pSrc; @@ -2683,7 +2929,7 @@ jsonEncode(uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen) pSrc = *ppRes; buflen = (*pBufLen == -1) ? ustrlen(pSrc) : *pBufLen; - CHKiRet(jsonAddVal(pSrc, buflen, &dst)); + CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll)); if(dst != NULL) { /* we updated the string and need to replace the @@ -2712,7 +2958,7 @@ finalize_it: * something to consider at a later stage. rgerhards, 2012-04-19 */ static rsRetVal -jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen) +jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFreed, int *pBufLen, int escapeAll) { unsigned buflen; uchar *pSrc; @@ -2726,7 +2972,7 @@ jsonField(struct templateEntry *pTpe, uchar **ppRes, unsigned short *pbMustBeFre es_addChar(&dst, '"'); es_addBuf(&dst, (char*)pTpe->fieldName, pTpe->lenFieldName); es_addBufConstcstr(&dst, "\":\""); - CHKiRet(jsonAddVal(pSrc, buflen, &dst)); + CHKiRet(jsonAddVal(pSrc, buflen, &dst, escapeAll)); es_addChar(&dst, '"'); if(*pbMustBeFreed) @@ -2783,9 +3029,9 @@ finalize_it: #define RET_OUT_OF_MEMORY { *pbMustBeFreed = 0;\ *pPropLen = sizeof("**OUT OF MEMORY**") - 1; \ return(UCHAR_CONSTANT("**OUT OF MEMORY**"));} -uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, - propid_t propid, es_str_t *propName, rs_size_t *pPropLen, - unsigned short *pbMustBeFreed, struct syslogTime *ttNow) +uchar *MsgGetProp(msg_t *__restrict__ const pMsg, struct templateEntry *__restrict__ const pTpe, + msgPropDescr_t *pProp, rs_size_t *__restrict__ const pPropLen, + unsigned short *__restrict__ const pbMustBeFreed, struct syslogTime * const ttNow) { uchar *pRes; /* result pointer */ rs_size_t bufLen = -1; /* length of string or -1, if not known */ @@ -2807,7 +3053,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, *pbMustBeFreed = 0; - switch(propid) { + switch(pProp->id) { case PROP_MSG: pRes = getMSG(pMsg); bufLen = getMSGLen(pMsg); @@ -2880,7 +3126,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, pRes = (uchar*)getProtocolVersionString(pMsg); break; case PROP_STRUCTURED_DATA: - pRes = (uchar*)getStructuredData(pMsg); + MsgGetStructuredData(pMsg, &pRes, &bufLen); break; case PROP_APP_NAME: pRes = (uchar*)getAPPNAME(pMsg, LOCK_MUTEX); @@ -2891,6 +3137,10 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, case PROP_MSGID: pRes = (uchar*)getMSGID(pMsg); break; + case PROP_JSONMESG: + pRes = (uchar*)msgGetJSONMESG(pMsg); + *pbMustBeFreed = 1; + break; #ifdef USE_LIBUUID case PROP_UUID: getUUID(pMsg, &pRes, &bufLen); @@ -2968,8 +3218,6 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, break; case PROP_CEE_ALL_JSON: if(pMsg->json == NULL) { - if(*pbMustBeFreed == 1) - free(pRes); pRes = (uchar*) "{}"; bufLen = 2; *pbMustBeFreed = 0; @@ -2979,35 +3227,59 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, } break; case PROP_CEE: - getCEEPropVal(pMsg, propName, &pRes, &bufLen, pbMustBeFreed); + case PROP_LOCAL_VAR: + case PROP_GLOBAL_VAR: + getJSONPropVal(pMsg, pProp, &pRes, &bufLen, pbMustBeFreed); break; case PROP_SYS_BOM: - if(*pbMustBeFreed == 1) - free(pRes); pRes = (uchar*) "\xEF\xBB\xBF"; *pbMustBeFreed = 0; break; case PROP_SYS_UPTIME: # ifndef HAVE_SYSINFO_UPTIME - /* An alternative on some systems (eg Solaris) is to scan - * /var/adm/utmpx for last boot time. - */ + /* An alternative on some systems (eg Solaris) is to scan + * /var/adm/utmpx for last boot time. + */ pRes = (uchar*) "UPTIME NOT available on this system"; *pbMustBeFreed = 0; + +# elif defined(__FreeBSD__) + + { + struct timespec tp; + + if((pRes = (uchar*) MALLOC(sizeof(uchar) * 32)) == NULL) { + RET_OUT_OF_MEMORY; + } + + if(clock_gettime(CLOCK_UPTIME, &tp) == -1) { + free(pRes); + *pPropLen = sizeof("**SYSCALL FAILED**") - 1; + return(UCHAR_CONSTANT("**SYSCALL FAILED**")); + } + + *pbMustBeFreed = 1; + + snprintf((char*) pRes, sizeof(uchar) * 32, "%ld", tp.tv_sec); + } + # else + { struct sysinfo s_info; if((pRes = (uchar*) MALLOC(sizeof(uchar) * 32)) == NULL) { RET_OUT_OF_MEMORY; } - *pbMustBeFreed = 1; if(sysinfo(&s_info) < 0) { + free(pRes); *pPropLen = sizeof("**SYSCALL FAILED**") - 1; return(UCHAR_CONSTANT("**SYSCALL FAILED**")); } + *pbMustBeFreed = 1; + snprintf((char*) pRes, sizeof(uchar) * 32, "%ld", s_info.uptime); } # endif @@ -3016,7 +3288,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, /* there is no point in continuing, we may even otherwise render the * error message unreadable. rgerhards, 2007-07-10 */ - dbgprintf("invalid property id: '%d'\n", propid); + dbgprintf("invalid property id: '%d'\n", pProp->id); *pbMustBeFreed = 0; *pPropLen = sizeof("**INVALID PROPERTY NAME**") - 1; return UCHAR_CONSTANT("**INVALID PROPERTY NAME**"); @@ -3075,7 +3347,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, /* 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)); + pBufStart = pBuf = MALLOC((iLen + 1) * sizeof(uchar)); if(pBuf == NULL) { if(*pbMustBeFreed == 1) free(pRes); @@ -3253,7 +3525,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, if(iTo > bufLen) /* iTo is very large, if no to-position is set in the template! */ iTo = bufLen; iLen = iTo - iFrom + 1; /* the +1 is for an actual char, NOT \0! */ - pBufStart = pBuf = MALLOC((iLen + 1) * sizeof(char)); + pBufStart = pBuf = MALLOC((iLen + 1) * sizeof(uchar)); if(pBuf == NULL) { if(*pbMustBeFreed == 1) free(pRes); @@ -3307,7 +3579,7 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, uchar *pBStart; uchar *pB; uchar *pSrc; - pBStart = pB = MALLOC((bufLen + 1) * sizeof(char)); + pBStart = pB = MALLOC((bufLen + 1) * sizeof(uchar)); if(pB == NULL) { if(*pbMustBeFreed == 1) free(pRes); @@ -3612,9 +3884,13 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, bufLen = -1; *pbMustBeFreed = 1; } else if(pTpe->data.field.options.bJSON) { - jsonEncode(&pRes, pbMustBeFreed, &bufLen); + jsonEncode(&pRes, pbMustBeFreed, &bufLen, RSTRUE); } else if(pTpe->data.field.options.bJSONf) { - jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen); + jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen, RSTRUE); + } else if(pTpe->data.field.options.bJSONr) { + jsonEncode(&pRes, pbMustBeFreed, &bufLen, RSFALSE); + } else if(pTpe->data.field.options.bJSONfr) { + jsonField(pTpe, &pRes, pbMustBeFreed, &bufLen, RSFALSE); } *pPropLen = (bufLen == -1) ? ustrlen(pRes) : bufLen; @@ -3624,66 +3900,6 @@ uchar *MsgGetProp(msg_t *pMsg, struct templateEntry *pTpe, } -/* The function returns a cee variable suitable for use with RainerScript. - * Note: caller must free the returned string. - * Note that we need to do a lot of conversions between es_str_t and cstr -- this will go away once - * we have moved larger parts of rsyslog to es_str_t. Acceptable for the moment, especially as we intend - * to rewrite the script engine as well! - * rgerhards, 2010-12-03 - */ -es_str_t* -msgGetCEEVarNew(msg_t *pMsg, char *name) -{ - uchar *leaf; - char *val; - es_str_t *estr = NULL; - struct json_object *json, *parent; - - ISOBJ_TYPE_assert(pMsg, msg); - - if(pMsg->json == NULL) { - estr = es_newStr(1); - goto done; - } - leaf = jsonPathGetLeaf((uchar*)name, strlen(name)); - if(jsonPathFindParent(pMsg, (uchar*)name, leaf, &parent, 1) != RS_RET_OK) { - estr = es_newStr(1); - goto done; - } - json = json_object_object_get(parent, (char*)leaf); - val = (char*)json_object_get_string(json); - estr = es_newStrFromCStr(val, strlen(val)); -done: - return estr; -} - - -/* Return an es_str_t for given message property. - */ -es_str_t* -msgGetMsgVarNew(msg_t *pThis, uchar *name) -{ - rs_size_t propLen; - uchar *pszProp = NULL; - propid_t propid; - unsigned short bMustBeFreed = 0; - es_str_t *estr; - - ISOBJ_TYPE_assert(pThis, msg); - - /* always call MsgGetProp() without a template specifier */ - /* TODO: optimize propNameToID() call -- rgerhards, 2009-06-26 */ - propNameStrToID(name, &propid); - pszProp = (uchar*) MsgGetProp(pThis, NULL, propid, NULL, &propLen, &bMustBeFreed, NULL); - - estr = es_newStrFromCStr((char*)pszProp, propLen); - if(bMustBeFreed) - free(pszProp); - - return estr; -} - - /* 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 @@ -3734,7 +3950,7 @@ rsRetVal MsgSetProperty(msg_t *pThis, var_t *pProp) prop.Destruct(&propRcvFrom); } else if(isProp("pszHOSTNAME")) { MsgSetHOSTNAME(pThis, rsCStrGetSzStrNoNULL(pProp->val.pStr), rsCStrLen(pProp->val.pStr)); - } else if(isProp("pCSStrucData")) { + } else if(isProp("pszStrucData")) { MsgSetStructuredData(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); } else if(isProp("pCSAPPNAME")) { MsgSetAPPNAME(pThis, (char*) rsCStrGetSzStrNoNULL(pProp->val.pStr)); @@ -3769,12 +3985,142 @@ finalize_it: #undef isProp +/* Set a single property based on the JSON object provided. The + * property name is extracted from the JSON object. + */ +static rsRetVal +msgSetPropViaJSON(msg_t *__restrict__ const pMsg, const char *name, struct json_object *json) +{ + const char *psz; + int val; + prop_t *propFromHost = NULL; + prop_t *propRcvFromIP = NULL; + DEFiRet; + + // TODO: think if we need to lock the message mutex. For some updates + // we probably need to! + + /* note: json_object_get_string() manages the memory of the returned + * string. So we MUST NOT free it! + */ + dbgprintf("DDDD: msgSetPropViaJSON key: '%s'\n", name); + if(!strcmp(name, "rawmsg")) { + psz = json_object_get_string(json); + MsgSetRawMsg(pMsg, psz, strlen(psz)); + } else if(!strcmp(name, "msg")) { + psz = json_object_get_string(json); + MsgReplaceMSG(pMsg, (const uchar*)psz, strlen(psz)); + } else if(!strcmp(name, "syslogtag")) { + psz = json_object_get_string(json); + MsgSetTAG(pMsg, (const uchar*)psz, strlen(psz)); + } else if(!strcmp(name, "syslogfacility")) { + val = json_object_get_int(json); + if(val >= 0 && val <= 24) + pMsg->iFacility = val; + else + DBGPRINTF("mmexternal: invalid fac %d requested -- ignored\n", val); + } else if(!strcmp(name, "syslogseverity")) { + val = json_object_get_int(json); + if(val >= 0 && val <= 7) + pMsg->iSeverity = val; + else + DBGPRINTF("mmexternal: invalid fac %d requested -- ignored\n", val); + } else if(!strcmp(name, "procid")) { + psz = json_object_get_string(json); + MsgSetPROCID(pMsg, psz); + } else if(!strcmp(name, "msgid")) { + psz = json_object_get_string(json); + MsgSetMSGID(pMsg, psz); + } else if(!strcmp(name, "structured-data")) { + psz = json_object_get_string(json); + MsgSetStructuredData(pMsg, psz); + } else if(!strcmp(name, "hostname") || !strcmp(name, "source")) { + psz = json_object_get_string(json); + MsgSetHOSTNAME(pMsg, (const uchar*)psz, strlen(psz)); + } else if(!strcmp(name, "fromhost")) { + psz = json_object_get_string(json); + MsgSetRcvFromStr(pMsg, (const uchar*) psz, 0, &propFromHost); + } else if(!strcmp(name, "fromhost-ip")) { + psz = json_object_get_string(json); + MsgSetRcvFromIPStr(pMsg, (const uchar*)psz, strlen(psz), &propRcvFromIP); + } else if(!strcmp(name, "$!")) { + msgAddJSON(pMsg, (uchar*)"!", json); + } else { + /* we ignore unknown properties */ + DBGPRINTF("msgSetPropViaJSON: unkonwn property ignored: %s\n", + name); + } + RETiRet; +} + + +/* set message properties based on JSON string. This function does it all, + * including parsing the JSON string. If an error is detected, the operation + * is aborted at the time of error. Any modifications made before the + * error ocurs are still PERSISTED. + * This function is meant to support the external message modifiction module + * interface. As such, replacing properties is expressively permited. Note that + * properties which were derived from the message during parsing are NOT + * updated if the underlying (raw)msg property is changed. + */ +rsRetVal +MsgSetPropsViaJSON(msg_t *__restrict__ const pMsg, const uchar *__restrict__ const jsonstr) +{ + struct json_tokener *tokener = NULL; + struct json_object *json; + const char *errMsg; + DEFiRet; + + DBGPRINTF("DDDDDD: JSON string for message mod: '%s'\n", jsonstr); + if(!strcmp((char*)jsonstr, "{}")) /* shortcut for a common case */ + FINALIZE; + + tokener = json_tokener_new(); + + json = json_tokener_parse_ex(tokener, (char*)jsonstr, ustrlen(jsonstr)); + if(Debug) { + errMsg = NULL; + if(json == NULL) { + enum json_tokener_error err; + + err = tokener->err; + if(err != json_tokener_continue) +# if HAVE_JSON_TOKENER_ERROR_DESC + errMsg = json_tokener_error_desc(err); +# else + errMsg = json_tokener_errors[err]; +# endif + else + errMsg = "Unterminated input"; + } else if(!json_object_is_type(json, json_type_object)) + errMsg = "JSON value is not an object"; + if(errMsg != NULL) { + DBGPRINTF("MsgSetPropsViaJSON: Error parsing JSON '%s': %s\n", + jsonstr, errMsg); + } + } + if(json == NULL || !json_object_is_type(json, json_type_object)) { + ABORT_FINALIZE(RS_RET_JSON_PARSE_ERR); + } + + json_object_object_foreach(json, name, val) { + msgSetPropViaJSON(pMsg, name, val); + } + json_object_put(json); + +finalize_it: + if(tokener != NULL) + json_tokener_free(tokener); + RETiRet; +} + + /* get the severity - this is an entry point that * satisfies the base object class getSeverity semantics. * rgerhards, 2008-01-14 */ rsRetVal -MsgGetSeverity(msg_t *pMsg, int *piSeverity) +MsgGetSeverity(msg_t * const pMsg, int *piSeverity) { *piSeverity = pMsg->iSeverity; return RS_RET_OK; @@ -3785,16 +4131,22 @@ static uchar * jsonPathGetLeaf(uchar *name, int lenName) { int i; - for(i = lenName ; name[i] != '!' && i >= 0 ; --i) - /* just skip */; - if(name[i] == '!') + for(i = lenName ; i >= 0 ; --i) + if(i == 0) { + if(name[0] == '!' || name[0] == '.' || name[0] == '/') + break; + } else { + if(name[i] == '!') + break; + } + if(name[i] == '!' || name[i] == '.' || name[i] == '/') ++i; return name + i; } static rsRetVal -jsonPathFindNext(struct json_object *root, uchar **name, uchar *leaf, +jsonPathFindNext(struct json_object *root, uchar *namestart, uchar **name, uchar *leaf, struct json_object **found, int bCreate) { uchar namebuf[1024]; @@ -3803,13 +4155,12 @@ jsonPathFindNext(struct json_object *root, uchar **name, uchar *leaf, uchar *p = *name; DEFiRet; - if(*p == '!') + if(*p == '!' || (*name == namestart && (*p == '.' || *p == '/'))) ++p; - for(i = 0 ; *p && *p != '!' && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p) + for(i = 0 ; *p && !(p == namestart && (*p == '.' || *p == '/')) && *p != '!' && p != leaf && i < sizeof(namebuf)-1 ; ++i, ++p) namebuf[i] = *p; if(i > 0) { namebuf[i] = '\0'; - dbgprintf("AAAA: next JSONPath elt: '%s'\n", namebuf); json = json_object_object_get(root, (char*)namebuf); } else json = root; @@ -3829,12 +4180,14 @@ finalize_it: } static rsRetVal -jsonPathFindParent(msg_t *pM, uchar *name, uchar *leaf, struct json_object **parent, int bCreate) +jsonPathFindParent(struct json_object *jroot, uchar *name, uchar *leaf, struct json_object **parent, int bCreate) { + uchar *namestart; DEFiRet; - *parent = pM->json; + namestart = name; + *parent = jroot; while(name < leaf-1) { - jsonPathFindNext(*parent, &name, leaf, parent, bCreate); + jsonPathFindNext(*parent, namestart, &name, leaf, parent, bCreate); } RETiRet; } @@ -3847,7 +4200,6 @@ jsonMerge(struct json_object *existing, struct json_object *json) struct json_object_iter it; json_object_object_foreachC(json, it) { -DBGPRINTF("AAAA jsonMerge adds '%s'\n", it.key); json_object_object_add(existing, it.key, json_object_get(it.val)); } @@ -3861,63 +4213,75 @@ DBGPRINTF("AAAA jsonMerge adds '%s'\n", it.key); /* find a JSON structure element (field or container doesn't matter). */ rsRetVal -jsonFind(msg_t *pM, es_str_t *propName, struct json_object **jsonres) +jsonFind(struct json_object *jroot, msgPropDescr_t *pProp, struct json_object **jsonres) { - uchar *name = NULL; uchar *leaf; struct json_object *parent; struct json_object *field; DEFiRet; - if(pM->json == NULL) { + if(jroot == NULL) { field = NULL; goto finalize_it; } - if(!es_strbufcmp(propName, (uchar*)"!", 1)) { - field = pM->json; + if(!strcmp((char*)pProp->name, "!")) { + field = jroot; } else { - name = (uchar*)es_str2cstr(propName, NULL); - leaf = jsonPathGetLeaf(name, ustrlen(name)); - CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 0)); + leaf = jsonPathGetLeaf(pProp->name, pProp->nameLen); + CHKiRet(jsonPathFindParent(jroot, pProp->name, leaf, &parent, 0)); field = json_object_object_get(parent, (char*)leaf); } *jsonres = field; finalize_it: - free(name); RETiRet; } rsRetVal -msgAddJSON(msg_t *pM, uchar *name, struct json_object *json) +msgAddJSON(msg_t * const pM, uchar *name, struct json_object *json) { /* TODO: error checks! This is a quick&dirty PoC! */ + struct json_object **pjroot; struct json_object *parent, *leafnode; uchar *leaf; DEFiRet; MsgLock(pM); - if(name[0] == '!' && name[1] == '\0') { - if(pM->json == NULL) - pM->json = json; + if(name[0] == '!') { + pjroot = &pM->json; + } else if(name[0] == '.') { + pjroot = &pM->localvars; + } else { /* globl var */ + pthread_rwlock_wrlock(&glblVars_rwlock); + pjroot = &global_var_root; + } + + if(name[1] == '\0') { /* full tree? */ + if(*pjroot == NULL) + *pjroot = json; else - CHKiRet(jsonMerge(pM->json, json)); + CHKiRet(jsonMerge(*pjroot, json)); } else { - if(pM->json == NULL) { + if(*pjroot == NULL) { /* now we need a root obj */ - pM->json = json_object_new_object(); + *pjroot = json_object_new_object(); } leaf = jsonPathGetLeaf(name, ustrlen(name)); - CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1)); + CHKiRet(jsonPathFindParent(*pjroot, name, leaf, &parent, 1)); + if (json_object_get_type(parent) != json_type_object) { + DBGPRINTF("msgAddJSON: not a container in json path," + "name is '%s'\n", name); + json_object_put(json); + ABORT_FINALIZE(RS_RET_INVLD_SETOP); + } leafnode = json_object_object_get(parent, (char*)leaf); if(leafnode == NULL) { json_object_object_add(parent, (char*)leaf, json); } else { if(json_object_get_type(json) == json_type_object) { - CHKiRet(jsonMerge(pM->json, json)); + CHKiRet(jsonMerge(*pjroot, json)); } else { -//dbgprintf("AAAA: leafnode already exists, type is %d, update with %d\n", (int)json_object_get_type(leafnode), (int)json_object_get_type(json)); /* TODO: improve the code below, however, the current * state is not really bad */ if(json_object_get_type(leafnode) == json_type_object) { @@ -3941,35 +4305,52 @@ msgAddJSON(msg_t *pM, uchar *name, struct json_object *json) } finalize_it: + if(name[0] == '/') + pthread_rwlock_unlock(&glblVars_rwlock); MsgUnlock(pM); RETiRet; } + rsRetVal -msgDelJSON(msg_t *pM, uchar *name) +msgDelJSON(msg_t * const pM, uchar *name) { + struct json_object **jroot; struct json_object *parent, *leafnode; uchar *leaf; DEFiRet; -dbgprintf("AAAA: unset variable '%s'\n", name); MsgLock(pM); - if(name[0] == '!' && name[1] == '\0') { - /* strange, but I think we should permit this. After all, + + if(name[0] == '!') { + jroot = &pM->json; + } else if(name[0] == '.') { + jroot = &pM->localvars; + } else { /* globl var */ + pthread_rwlock_wrlock(&glblVars_rwlock); + jroot = &global_var_root; + } + if(jroot == NULL) { + DBGPRINTF("msgDelJSONVar; jroot empty in unset for property %s\n", + name); + FINALIZE; + } + + if(name[1] == '\0') { + /* full tree! Strange, but I think we should permit this. After all, * we trust rsyslog.conf to be written by the admin. */ DBGPRINTF("unsetting JSON root object\n"); - json_object_put(pM->json); - pM->json = NULL; + json_object_put(*jroot); + *jroot = NULL; } else { - if(pM->json == NULL) { + if(*jroot == NULL) { /* now we need a root obj */ - pM->json = json_object_new_object(); + *jroot = json_object_new_object(); } leaf = jsonPathGetLeaf(name, ustrlen(name)); - CHKiRet(jsonPathFindParent(pM, name, leaf, &parent, 1)); + CHKiRet(jsonPathFindParent(*jroot, name, leaf, &parent, 1)); leafnode = json_object_object_get(parent, (char*)leaf); -DBGPRINTF("AAAA: unset found JSON value path '%s', " "leaf '%s', leafnode %p\n", name, leaf, leafnode); if(leafnode == NULL) { DBGPRINTF("unset JSON: could not find '%s'\n", name); ABORT_FINALIZE(RS_RET_JNAME_NOTFOUND); @@ -3982,6 +4363,8 @@ DBGPRINTF("AAAA: unset found JSON value path '%s', " "leaf '%s', leafnode %p\n", } finalize_it: + if(name[0] == '/') + pthread_rwlock_unlock(&glblVars_rwlock); MsgUnlock(pM); RETiRet; } @@ -4003,7 +4386,11 @@ jsonDeepCopy(struct json_object *src) dst = json_object_new_double(json_object_get_double(src)); break; case json_type_int: +#ifdef HAVE_JSON_OBJECT_NEW_INT64 + dst = json_object_new_int64(json_object_get_int64(src)); +#else /* HAVE_JSON_OBJECT_NEW_INT64 */ dst = json_object_new_int(json_object_get_int(src)); +#endif /* HAVE_JSON_OBJECT_NEW_INT64 */ break; case json_type_string: dst = json_object_new_string(json_object_get_string(src)); @@ -4034,7 +4421,7 @@ done: return dst; rsRetVal -msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *v) +msgSetJSONFromVar(msg_t * const pMsg, uchar *varname, struct var *v) { struct json_object *json = NULL; char *cstr; @@ -4046,7 +4433,11 @@ msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *v) free(cstr); break; case 'N':/* number (integer) */ +#ifdef HAVE_JSON_OBJECT_NEW_INT64 + json = json_object_new_int64(v->d.n); +#else /* HAVE_JSON_OBJECT_NEW_INT64 */ json = json_object_new_int((int) v->d.n); +#endif /* HAVE_JSON_OBJECT_NEW_INT64 */ break; case 'J':/* native JSON */ json = jsonDeepCopy(v->d.json); @@ -4055,11 +4446,76 @@ msgSetJSONFromVar(msg_t *pMsg, uchar *varname, struct var *v) v->datatype); ABORT_FINALIZE(RS_RET_ERR); } - msgAddJSON(pMsg, varname+1, json); + + msgAddJSON(pMsg, varname, json); finalize_it: RETiRet; } +rsRetVal +MsgAddToStructuredData(msg_t * const pMsg, uchar *toadd, rs_size_t len) +{ + uchar *newptr; + rs_size_t newlen; + DEFiRet; + newlen = (pMsg->pszStrucData[0] == '-') ? len : pMsg->lenStrucData + len; + CHKmalloc(newptr = (uchar*) realloc(pMsg->pszStrucData, newlen+1)); + pMsg->pszStrucData = newptr; + if(pMsg->pszStrucData[0] == '-') { /* empty? */ + memcpy(pMsg->pszStrucData, toadd, len); + } else { + memcpy(pMsg->pszStrucData+pMsg->lenStrucData, toadd, len); + } + pMsg->pszStrucData[newlen] = '\0'; + pMsg->lenStrucData = newlen; +finalize_it: + RETiRet; +} + + +/* Fill a message propert description. Space must already be alloced + * by the caller. This is for efficiency, as we expect this to happen + * as part of a larger structure alloc. + * Note that CEE/LOCAL_VAR properties can come in either as + * "$!xx"/"$.xx" or "!xx"/".xx" - we will unify them here. + */ +rsRetVal +msgPropDescrFill(msgPropDescr_t *pProp, uchar *name, int nameLen) +{ + propid_t id; + int offs; + DEFiRet; + if(propNameToID(name, &id) != RS_RET_OK) { + parser_errmsg("invalid property '%s'", name); + ABORT_FINALIZE(RS_RET_INVLD_PROP); + } + if(id == PROP_CEE || id == PROP_LOCAL_VAR || id == PROP_GLOBAL_VAR) { + /* in these cases, we need the field name for later processing */ + /* normalize name: remove $ if present */ + offs = (name[0] == '$') ? 1 : 0; + pProp->name = ustrdup(name + offs); + pProp->nameLen = nameLen - offs; + /* we patch the root name, so that support functions do not need to + * check for different root chars. */ + pProp->name[0] = '!'; + } + pProp->id = id; +finalize_it: + RETiRet; +} + +void +msgPropDescrDestruct(msgPropDescr_t *pProp) +{ + if(pProp != NULL) { + if(pProp->id == PROP_CEE || + pProp->id == PROP_LOCAL_VAR || + pProp->id == PROP_GLOBAL_VAR) + free(pProp->name); + } +} + + /* dummy */ rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } @@ -4068,6 +4524,8 @@ rsRetVal msgQueryInterface(void) { return RS_RET_NOT_IMPLEMENTED; } * rgerhards, 2008-01-04 */ BEGINObjClassInit(msg, 1, OBJ_IS_CORE_MODULE) + pthread_rwlock_init(&glblVars_rwlock, NULL); + /* request objects we use */ CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); |