/* mmnormalize.c * This is a message modification module. It normalizes the input message with * the help of liblognorm. The message's JSON variables are updated. * * NOTE: read comments in module-template.h for details on the calling interface! * * File begun on 2010-01-01 by RGerhards * * Copyright 2010-2015 Rainer Gerhards and Adiscon GmbH. * * This file is part of rsyslog. * * Rsyslog is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Rsyslog 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Rsyslog. If not, see . * * A copy of the GPL can be found in the file "COPYING" in this distribution. */ #include "config.h" #include "rsyslog.h" #include #include #include #include #include #include #include #include #include #include #include #include "conf.h" #include "syslogd-types.h" #include "template.h" #include "module-template.h" #include "errmsg.h" #include "cfsysline.h" #include "dirty.h" #include "unicode-helper.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP MODULE_CNFNAME("mmnormalize") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); /* static data */ /* internal structures */ DEF_OMOD_STATIC_DATA static struct cnfparamdescr modpdescr[] = { { "allowregex", eCmdHdlrBinary, 0 } }; static struct cnfparamblk modpblk = { CNFPARAMBLK_VERSION, sizeof(modpdescr)/sizeof(struct cnfparamdescr), modpdescr }; typedef struct _instanceData { sbool bUseRawMsg; /**< use %rawmsg% instead of %msg% */ uchar *rule; /* rule to use */ uchar *rulebase; /**< name of rulebase to use */ ln_ctx ctxln; /**< context to be used for liblognorm */ char *pszPath; /**< path of normalized data */ msgPropDescr_t *varDescr;/**< name of variable to use */ } instanceData; typedef struct wrkrInstanceData { instanceData *pData; } wrkrInstanceData_t; typedef struct configSettings_s { uchar *rulebase; /**< name of normalization rulebase to use */ uchar *rule; int bUseRawMsg; /**< use %rawmsg% instead of %msg% */ } configSettings_t; static configSettings_t cs; /* tables for interfacing with the v6 config system */ /* action (instance) parameters */ static struct cnfparamdescr actpdescr[] = { { "rulebase", eCmdHdlrGetWord, 0 }, { "rule", eCmdHdlrArray, 0 }, { "path", eCmdHdlrGetWord, 0 }, { "userawmsg", eCmdHdlrBinary, 0 }, { "variable", eCmdHdlrGetWord, 0 } }; static struct cnfparamblk actpblk = { CNFPARAMBLK_VERSION, sizeof(actpdescr)/sizeof(struct cnfparamdescr), actpdescr }; struct modConfData_s { rsconf_t *pConf; /* our overall config object */ int allow_regex; }; 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 */ /* callback for liblognorm error messages */ static void errCallBack(void __attribute__((unused)) *cookie, const char *msg, size_t __attribute__((unused)) lenMsg) { LogError(0, RS_RET_ERR_LIBLOGNORM, "liblognorm error: %s", msg); } /* to be called to build the liblognorm part of the instance ONCE ALL PARAMETERS ARE CORRECT * (and set within pData!). */ static rsRetVal buildInstance(instanceData *pData) { DEFiRet; if((pData->ctxln = ln_initCtx()) == NULL) { LogError(0, RS_RET_ERR_LIBLOGNORM_INIT, "error: could not initialize " "liblognorm ctx, cannot activate action"); ABORT_FINALIZE(RS_RET_ERR_LIBLOGNORM_INIT); } ln_setCtxOpts(pData->ctxln, loadModConf->allow_regex); ln_setErrMsgCB(pData->ctxln, errCallBack, NULL); if(pData->rule !=NULL && pData->rulebase == NULL) { if(ln_loadSamplesFromString(pData->ctxln, (char*) pData->rule) !=0) { LogError(0, RS_RET_NO_RULEBASE, "error: normalization rule '%s' " "could not be loaded cannot activate action", pData->rule); ln_exitCtx(pData->ctxln); ABORT_FINALIZE(RS_RET_ERR_LIBLOGNORM_SAMPDB_LOAD); } free(pData->rule); pData->rule = NULL; } else if(pData->rule ==NULL && pData->rulebase != NULL) { if(ln_loadSamples(pData->ctxln, (char*) pData->rulebase) != 0) { LogError(0, RS_RET_NO_RULEBASE, "error: normalization rulebase '%s' " "could not be loaded cannot activate action", pData->rulebase); ln_exitCtx(pData->ctxln); ABORT_FINALIZE(RS_RET_ERR_LIBLOGNORM_SAMPDB_LOAD); } } finalize_it: RETiRet; } BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars resetConfigVariables(NULL, NULL); ENDinitConfVars BEGINcreateInstance CODESTARTcreateInstance ENDcreateInstance BEGINcreateWrkrInstance CODESTARTcreateWrkrInstance ENDcreateWrkrInstance BEGINbeginCnfLoad CODESTARTbeginCnfLoad loadModConf = pModConf; pModConf->pConf = pConf; ENDbeginCnfLoad BEGINendCnfLoad CODESTARTendCnfLoad loadModConf = NULL; /* done loading */ /* free legacy config vars */ free(cs.rulebase); free(cs.rule); cs.rulebase = NULL; cs.rule = NULL; ENDendCnfLoad BEGINcheckCnf CODESTARTcheckCnf ENDcheckCnf BEGINactivateCnf CODESTARTactivateCnf runModConf = pModConf; ENDactivateCnf BEGINfreeCnf CODESTARTfreeCnf ENDfreeCnf BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature ENDisCompatibleWithFeature BEGINfreeInstance CODESTARTfreeInstance free(pData->rulebase); free(pData->rule); ln_exitCtx(pData->ctxln); free(pData->pszPath); msgPropDescrDestruct(pData->varDescr); free(pData->varDescr); ENDfreeInstance BEGINfreeWrkrInstance CODESTARTfreeWrkrInstance ENDfreeWrkrInstance BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo dbgprintf("mmnormalize\n"); dbgprintf("\tvariable='%s'\n", pData->varDescr->name); dbgprintf("\trulebase='%s'\n", pData->rulebase); dbgprintf("\trule='%s'\n", pData->rule); dbgprintf("\tpath='%s'\n", pData->pszPath); dbgprintf("\tbUseRawMsg='%d'\n", pData->bUseRawMsg); ENDdbgPrintInstInfo BEGINtryResume CODESTARTtryResume ENDtryResume BEGINdoAction_NoStrings smsg_t **ppMsg = (smsg_t **) pMsgData; smsg_t *pMsg = ppMsg[0]; uchar *buf; rs_size_t len; int r; struct json_object *json = NULL; unsigned short freeBuf = 0; CODESTARTdoAction if(pWrkrData->pData->bUseRawMsg) { getRawMsg(pMsg, &buf, &len); } else if (pWrkrData->pData->varDescr) { buf = MsgGetProp(pMsg, NULL, pWrkrData->pData->varDescr, &len, &freeBuf, NULL); } else { buf = getMSG(pMsg); len = getMSGLen(pMsg); } r = ln_normalize(pWrkrData->pData->ctxln, (char*)buf, len, &json); if (freeBuf) { free(buf); buf = NULL; } if(r != 0) { DBGPRINTF("error %d during ln_normalize\n", r); MsgSetParseSuccess(pMsg, 0); } else { MsgSetParseSuccess(pMsg, 1); } msgAddJSON(pMsg, (uchar*)pWrkrData->pData->pszPath + 1, json, 0, 0); ENDdoAction static void setInstParamDefaults(instanceData *pData) { pData->rulebase = NULL; pData->rule = NULL; pData->bUseRawMsg = 0; pData->pszPath = strdup("$!"); pData->varDescr = NULL; } BEGINsetModCnf struct cnfparamvals *pvals = NULL; int i; CODESTARTsetModCnf pvals = nvlstGetParams(lst, &modpblk, NULL); if(pvals == NULL) { LogError(0, RS_RET_MISSING_CNFPARAMS, "mmnormalize: error processing module " "config parameters missing [module(...)]"); ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } if(Debug) { dbgprintf("module (global) param blk for mmnormalize:\n"); cnfparamsPrint(&modpblk, pvals); } for(i = 0 ; i < modpblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(modpblk.descr[i].name, "allowregex")) { loadModConf->allow_regex = (int) pvals[i].val.d.n; } else { dbgprintf("mmnormalize: program error, non-handled " "param '%s' in setModCnf\n", modpblk.descr[i].name); } } finalize_it: if(pvals != NULL) cnfparamvalsDestruct(pvals, &modpblk); ENDsetModCnf BEGINnewActInst struct cnfparamvals *pvals; int i; int bDestructPValsOnExit; char *cstr; char *varName = NULL; char *buffer; char *tStr; int size = 0; CODESTARTnewActInst DBGPRINTF("newActInst (mmnormalize)\n"); bDestructPValsOnExit = 0; pvals = nvlstGetParams(lst, &actpblk, NULL); if(pvals == NULL) { LogError(0, RS_RET_MISSING_CNFPARAMS, "mmnormalize: error reading " "config parameters"); ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } bDestructPValsOnExit = 1; if(Debug) { dbgprintf("action param blk in mmnormalize:\n"); cnfparamsPrint(&actpblk, pvals); } CHKiRet(createInstance(&pData)); setInstParamDefaults(pData); for(i = 0 ; i < actpblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(actpblk.descr[i].name, "rulebase")) { pData->rulebase = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(actpblk.descr[i].name, "rule")) { for(int j=0; j < pvals[i].val.d.ar->nmemb; ++j) { tStr = (char*)es_str2cstr(pvals[i].val.d.ar->arr[j], NULL); size += strlen(tStr); free(tStr); } buffer = malloc(size + pvals[i].val.d.ar->nmemb + 1); tStr = (char*)es_str2cstr(pvals[i].val.d.ar->arr[0], NULL); strcpy(buffer, tStr); free(tStr); strcat(buffer, "\n"); for(int j=1; j < pvals[i].val.d.ar->nmemb; ++j) { tStr = (char*)es_str2cstr(pvals[i].val.d.ar->arr[j], NULL); strcat(buffer, tStr); free(tStr); strcat(buffer, "\n"); } strcat(buffer, "\0"); pData->rule = (uchar*)buffer; } else if(!strcmp(actpblk.descr[i].name, "userawmsg")) { pData->bUseRawMsg = (int) pvals[i].val.d.n; } else if(!strcmp(actpblk.descr[i].name, "variable")) { varName = es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(actpblk.descr[i].name, "path")) { cstr = es_str2cstr(pvals[i].val.d.estr, NULL); if (strlen(cstr) < 2) { LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "mmnormalize: valid path name should be at least " "2 symbols long, got %s", cstr); free(cstr); } else if (cstr[0] != '$') { LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "mmnormalize: valid path name should start with $," "got %s", cstr); free(cstr); } else { free(pData->pszPath); pData->pszPath = cstr; } continue; } else { DBGPRINTF("mmnormalize: program error, non-handled " "param '%s'\n", actpblk.descr[i].name); } } if (varName) { if(pData->bUseRawMsg) { LogError(0, RS_RET_CONFIG_ERROR, "mmnormalize: 'variable' param can't be used with 'useRawMsg'. " "Ignoring 'variable', will use raw message."); } else { CHKmalloc(pData->varDescr = malloc(sizeof(msgPropDescr_t))); CHKiRet(msgPropDescrFill(pData->varDescr, (uchar*) varName, strlen(varName))); } free(varName); varName = NULL; } if(!pData->rulebase) { if(!pData->rule) { LogError(0, RS_RET_CONFIG_ERROR, "mmnormalize: rulebase needed. " "Use option rulebase or rule."); } } if(pData->rulebase) { if(pData->rule) { LogError(0, RS_RET_CONFIG_ERROR, "mmnormalize: only one rulebase possible, rulebase " "can't be used with rule"); } } CODE_STD_STRING_REQUESTnewActInst(1) CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG)); iRet = buildInstance(pData); CODE_STD_FINALIZERnewActInst if(bDestructPValsOnExit) cnfparamvalsDestruct(pvals, &actpblk); ENDnewActInst BEGINparseSelectorAct CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) /* first check if this config line is actually for us */ if(strncmp((char*) p, ":mmnormalize:", sizeof(":mmnormalize:") - 1)) { ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); } if(cs.rulebase == NULL && cs.rule == NULL) { LogError(0, RS_RET_NO_RULEBASE, "error: no normalization rulebase was specified, use " "$MMNormalizeSampleDB directive first!"); ABORT_FINALIZE(RS_RET_NO_RULEBASE); } /* ok, if we reach this point, we have something for us */ p += sizeof(":mmnormalize:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ CHKiRet(createInstance(&pData)); pData->rulebase = cs.rulebase; pData->rule = cs.rule; pData->bUseRawMsg = cs.bUseRawMsg; pData->pszPath = strdup("$!"); /* old interface does not support this feature */ /* all config vars auto-reset! */ cs.bUseRawMsg = 0; cs.rulebase = NULL; /* we used it up! */ cs.rule = NULL; /* check if a non-standard template is to be applied */ if(*(p-1) == ';') --p; /* we call the function below because we need to call it via our interface definition. However, * the format specified (if any) is always ignored. */ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat")); CHKiRet(buildInstance(pData)); CODE_STD_FINALIZERparseSelectorAct ENDparseSelectorAct BEGINmodExit CODESTARTmodExit ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES CODEqueryEtryPt_STD_OMOD8_QUERIES CODEqueryEtryPt_STD_CONF2_QUERIES CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt /* Reset config variables for this module to default values. */ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { DEFiRet; cs.rulebase = NULL; cs.rule = NULL; cs.bUseRawMsg = 0; RETiRet; } /* set the rulebase name */ static rsRetVal setRuleBase(void __attribute__((unused)) *pVal, uchar *pszName) { DEFiRet; cs.rulebase = pszName; pszName = NULL; RETiRet; } BEGINmodInit() rsRetVal localRet; rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts); unsigned long opts; int bMsgPassingSupported; CODESTARTmodInit INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr DBGPRINTF("mmnormalize: module compiled with rsyslog version %s.\n", VERSION); /* check if the rsyslog core supports parameter passing code */ bMsgPassingSupported = 0; localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", &pomsrGetSupportedTplOpts); if(localRet == RS_RET_OK) { /* found entry point, so let's see if core supports msg passing */ CHKiRet((*pomsrGetSupportedTplOpts)(&opts)); if(opts & OMSR_TPL_AS_MSG) bMsgPassingSupported = 1; } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) { ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */ } if(!bMsgPassingSupported) { DBGPRINTF("mmnormalize: msg-passing is not supported by rsyslog core, " "can not continue.\n"); ABORT_FINALIZE(RS_RET_NO_MSG_PASSING); } CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizerulebase", 0, eCmdHdlrGetWord, setRuleBase, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizerule", 0, eCmdHdlrGetWord, NULL, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmnormalizeuserawmsg", 0, eCmdHdlrBinary, NULL, &cs.bUseRawMsg, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit /* vi:set ai: */