/* mmpstrucdata.c * Parse all fields of the message into structured data inside the * JSON tree. * * Copyright 2013-2018 Adiscon GmbH. * * This file is part of rsyslog. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * -or- * see COPYING.ASL20 in the source distribution * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "config.h" #include "rsyslog.h" #include #include #include #include #include #include #include #include #include #include #include "conf.h" #include "syslogd-types.h" #include "srUtils.h" #include "template.h" #include "module-template.h" #include "errmsg.h" #include "parserif.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP MODULE_CNFNAME("mmpstrucdata") DEF_OMOD_STATIC_DATA /* config variables */ typedef struct _instanceData { uchar *jsonRoot; /**< container where to store fields */ int lowercase_SD_ID; } instanceData; typedef struct wrkrInstanceData { instanceData *pData; } wrkrInstanceData_t; struct modConfData_s { rsconf_t *pConf; /* our overall config object */ }; static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */ /* tables for interfacing with the v6 config system */ /* action (instance) parameters */ static struct cnfparamdescr actpdescr[] = { { "jsonroot", eCmdHdlrString, 0 }, { "sd_name.lowercase", eCmdHdlrBinary, 0 } }; static struct cnfparamblk actpblk = { CNFPARAMBLK_VERSION, sizeof(actpdescr)/sizeof(struct cnfparamdescr), actpdescr }; BEGINbeginCnfLoad CODESTARTbeginCnfLoad loadModConf = pModConf; pModConf->pConf = pConf; ENDbeginCnfLoad BEGINendCnfLoad CODESTARTendCnfLoad ENDendCnfLoad BEGINcheckCnf CODESTARTcheckCnf ENDcheckCnf BEGINactivateCnf CODESTARTactivateCnf runModConf = pModConf; ENDactivateCnf BEGINfreeCnf CODESTARTfreeCnf ENDfreeCnf BEGINcreateInstance CODESTARTcreateInstance ENDcreateInstance BEGINcreateWrkrInstance CODESTARTcreateWrkrInstance ENDcreateWrkrInstance BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature ENDisCompatibleWithFeature BEGINfreeInstance CODESTARTfreeInstance free(pData->jsonRoot); ENDfreeInstance BEGINfreeWrkrInstance CODESTARTfreeWrkrInstance ENDfreeWrkrInstance static inline void setInstParamDefaults(instanceData *pData) { pData->jsonRoot = NULL; pData->lowercase_SD_ID = 1; } BEGINnewActInst struct cnfparamvals *pvals; int i; CODESTARTnewActInst DBGPRINTF("newActInst (mmpstrucdata)\n"); if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } CODE_STD_STRING_REQUESTnewActInst(1) CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG)); CHKiRet(createInstance(&pData)); setInstParamDefaults(pData); for(i = 0 ; i < actpblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(actpblk.descr[i].name, "jsonroot")) { size_t lenvar = es_strlen(pvals[i].val.d.estr); pData->jsonRoot = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); if(pData->jsonRoot[0] == '$') { /* pre 8.35, the jsonRoot name needed to be specified without * the leading $. This was confusing, so we now require a full * variable name. Nevertheless, we still need to support the * version without $. -- rgerhards, 2018-05-16 */ /* copy lenvar size because of \0 string terminator */ memmove(pData->jsonRoot, pData->jsonRoot+1, lenvar); --lenvar; } if( (lenvar == 0) || ( !( pData->jsonRoot[0] == '!' || pData->jsonRoot[0] == '.' || pData->jsonRoot[0] == '/' ) ) ) { parser_errmsg("mmpstrucdata: invalid jsonRoot name '%s', name must " "start with either '$!', '$.', or '$/'", pData->jsonRoot); ABORT_FINALIZE(RS_RET_INVALID_VAR); } } else if(!strcmp(actpblk.descr[i].name, "sd_name.lowercase")) { pData->lowercase_SD_ID = pvals[i].val.d.n; } else { LogError(0, RS_RET_INTERNAL_ERROR, "mmpstrucdata: internal program error, non-handled " "param '%s'\n", actpblk.descr[i].name); } } if(pData->jsonRoot == NULL) { CHKmalloc(pData->jsonRoot = (uchar*) strdup("!")); } CODE_STD_FINALIZERnewActInst cnfparamvalsDestruct(pvals, &actpblk); ENDnewActInst BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo ENDdbgPrintInstInfo BEGINtryResume CODESTARTtryResume ENDtryResume static rsRetVal parsePARAM_VALUE(uchar *sdbuf, int lenbuf, int *curridx, uchar *fieldbuf) { int i, j; DEFiRet; i = *curridx; j = 0; while(i < lenbuf && sdbuf[i] != '"') { if(sdbuf[i] == '\\') { if(++i == lenbuf) { fieldbuf[j++] = '\\'; } else { if(sdbuf[i] == '"') { fieldbuf[j++] = '"'; } else if(sdbuf[i] == '\\') { fieldbuf[j++] = '\\'; } else if(sdbuf[i] == ']') { fieldbuf[j++] = ']'; } else { fieldbuf[j++] = '\\'; fieldbuf[j++] = sdbuf[i]; } ++i; } } else { fieldbuf[j++] = sdbuf[i++]; } } fieldbuf[j] = '\0'; *curridx = i; RETiRet; } static rsRetVal ATTR_NONNULL() parseSD_NAME(instanceData *const pData, uchar *sdbuf, int lenbuf, int *curridx, uchar *namebuf) { int i, j; DEFiRet; i = *curridx; for(j = 0 ; i < lenbuf && j < 32; ++j) { if( sdbuf[i] == '=' || sdbuf[i] == '"' || sdbuf[i] == ']' || sdbuf[i] == ' ') break; namebuf[j] = pData->lowercase_SD_ID ? tolower(sdbuf[i]) : sdbuf[i]; ++i; } namebuf[j] = '\0'; *curridx = i; RETiRet; } static rsRetVal ATTR_NONNULL() parseSD_PARAM(instanceData *const pData, uchar *sdbuf, int lenbuf, int *curridx, struct json_object *jroot) { int i; uchar pName[33]; uchar pVal[32*1024]; struct json_object *jval; DEFiRet; i = *curridx; CHKiRet(parseSD_NAME(pData, sdbuf, lenbuf, &i, pName)); if(sdbuf[i] != '=') { ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; if(sdbuf[i] != '"') { ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; CHKiRet(parsePARAM_VALUE(sdbuf, lenbuf, &i, pVal)); if(sdbuf[i] != '"') { ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; jval = json_object_new_string((char*)pVal); json_object_object_add(jroot, (char*)pName, jval); *curridx = i; finalize_it: RETiRet; } static rsRetVal ATTR_NONNULL() parseSD_ELEMENT(instanceData *const pData, uchar *sdbuf, int lenbuf, int *curridx, struct json_object *jroot) { int i; uchar sd_id[33]; struct json_object *json = NULL; DEFiRet; i = *curridx; if(sdbuf[i] != '[') { ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; /* eat '[' */ CHKiRet(parseSD_NAME(pData, sdbuf, lenbuf, &i, sd_id)); json = json_object_new_object(); while(i < lenbuf) { if(sdbuf[i] == ']') { break; } else if(sdbuf[i] != ' ') { ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; while(i < lenbuf && sdbuf[i] == ' ') ++i; CHKiRet(parseSD_PARAM(pData, sdbuf, lenbuf, &i, json)); } if(sdbuf[i] != ']') { DBGPRINTF("mmpstrucdata: SD-ELEMENT does not terminate with " "']': '%s'\n", sdbuf+i); ABORT_FINALIZE(RS_RET_STRUC_DATA_INVLD); } ++i; /* eat ']' */ *curridx = i; json_object_object_add(jroot, (char*)sd_id, json); finalize_it: if(iRet != RS_RET_OK && json != NULL) json_object_put(json); RETiRet; } static rsRetVal ATTR_NONNULL() parse_sd(instanceData *const pData, smsg_t *const pMsg) { struct json_object *json, *jroot; uchar *sdbuf; int lenbuf; int i = 0; DEFiRet; json = json_object_new_object(); if(json == NULL) { ABORT_FINALIZE(RS_RET_ERR); } MsgGetStructuredData(pMsg, &sdbuf,&lenbuf); while(i < lenbuf) { CHKiRet(parseSD_ELEMENT(pData, sdbuf, lenbuf, &i, json)); } jroot = json_object_new_object(); if(jroot == NULL) { ABORT_FINALIZE(RS_RET_ERR); } json_object_object_add(jroot, "rfc5424-sd", json); msgAddJSON(pMsg, pData->jsonRoot, jroot, 0, 0); finalize_it: if(iRet != RS_RET_OK && json != NULL) json_object_put(json); RETiRet; } BEGINdoAction_NoStrings smsg_t **ppMsg = (smsg_t **) pMsgData; smsg_t *pMsg = ppMsg[0]; CODESTARTdoAction DBGPRINTF("mmpstrucdata: enter\n"); if(!MsgHasStructuredData(pMsg)) { DBGPRINTF("mmpstrucdata: message does not have structured data\n"); FINALIZE; } /* don't check return code - we never want rsyslog to retry * or suspend this action! */ parse_sd(pWrkrData->pData, pMsg); finalize_it: ENDdoAction BEGINmodExit CODESTARTmodExit ENDmodExit NO_LEGACY_CONF_parseSelectorAct BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES CODEqueryEtryPt_STD_OMOD8_QUERIES CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES CODEqueryEtryPt_STD_CONF2_QUERIES ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr DBGPRINTF("mmpstrucdata: module compiled with rsyslog version %s.\n", VERSION); ENDmodInit