/* imrelp.c * * This is the implementation of the RELP input module. * * File begun on 2008-03-13 by RGerhards * * Copyright 2008-2014 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "rsyslog.h" #include "dirty.h" #include "errmsg.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" #include "msg.h" #include "unicode-helper.h" #include "prop.h" #include "ruleset.h" #include "glbl.h" #include "statsobj.h" MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP MODULE_CNFNAME("imrelp") /* static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(net) DEFobjCurrIf(prop) DEFobjCurrIf(errmsg) DEFobjCurrIf(ruleset) DEFobjCurrIf(glbl) DEFobjCurrIf(statsobj) /* forward definitions */ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); /* Module static data */ /* config vars for legacy config system */ static relpEngine_t *pRelpEngine; /* our relp engine */ static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */ static struct configSettings_s { uchar *pszBindRuleset; /* name of Ruleset to bind to */ } cs; struct instanceConf_s { uchar *pszBindPort; /* port to bind to */ sbool bKeepAlive; /* support keep-alive packets */ sbool bEnableTLS; sbool bEnableTLSZip; int dhBits; uchar *pristring; /* GnuTLS priority string (NULL if not to be provided) */ uchar *authmode; /* TLS auth mode */ uchar *caCertFile; uchar *myCertFile; uchar *myPrivKeyFile; int iKeepAliveIntvl; int iKeepAliveProbes; int iKeepAliveTime; struct { int nmemb; uchar **name; } permittedPeers; struct instanceConf_s *next; /* with librelp, this module does not have any own specific session * or listener active data item. As a "work-around", we keep some * data items inside the configuration object. To keep things * decently clean, we put them all into their dedicated struct. So * it is easy to judge what is actual configuration and what is * dynamic runtime data. -- rgerhards, 2013-06-18 */ struct { statsobj_t *stats; /* listener stats */ STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) } data; }; struct modConfData_s { rsconf_t *pConf; /* our overall config object */ instanceConf_t *root, *tail; uchar *pszBindRuleset; /* name of Ruleset to bind to */ ruleset_t *pBindRuleset; /* due to librelp limitation, we need to bind all listerns to the same set */ }; 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 load process */ /* module-global parameters */ static struct cnfparamdescr modpdescr[] = { { "ruleset", eCmdHdlrGetWord, 0 }, }; static struct cnfparamblk modpblk = { CNFPARAMBLK_VERSION, sizeof(modpdescr)/sizeof(struct cnfparamdescr), modpdescr }; /* input instance parameters */ static struct cnfparamdescr inppdescr[] = { { "port", eCmdHdlrString, CNFPARAM_REQUIRED }, { "keepalive", eCmdHdlrBinary, 0 }, { "keepalive.probes", eCmdHdlrInt, 0 }, { "keepalive.time", eCmdHdlrInt, 0 }, { "keepalive.interval", eCmdHdlrInt, 0 }, { "tls", eCmdHdlrBinary, 0 }, { "tls.permittedpeer", eCmdHdlrArray, 0 }, { "tls.authmode", eCmdHdlrString, 0 }, { "tls.dhbits", eCmdHdlrInt, 0 }, { "tls.prioritystring", eCmdHdlrString, 0 }, { "tls.cacert", eCmdHdlrString, 0 }, { "tls.mycert", eCmdHdlrString, 0 }, { "tls.myprivkey", eCmdHdlrString, 0 }, { "tls.compression", eCmdHdlrBinary, 0 } }; static struct cnfparamblk inppblk = { CNFPARAMBLK_VERSION, sizeof(inppdescr)/sizeof(struct cnfparamdescr), inppdescr }; /* ------------------------------ callbacks ------------------------------ */ static void onErr(void *pUsr, char *objinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) { instanceConf_t *inst = (instanceConf_t*) pUsr; errmsg.LogError(0, RS_RET_RELP_AUTH_FAIL, "imrelp[%s]: error '%s', object " " '%s' - input may not work as intended", inst->pszBindPort, errmesg, objinfo); } static void onGenericErr(char *objinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) { errmsg.LogError(0, RS_RET_RELP_ERR, "imrelp: librelp error '%s', object " " '%s' - input may not work as intended", errmesg, objinfo); } static void onAuthErr(void *pUsr, char *authinfo, char* errmesg, __attribute__((unused)) relpRetVal errcode) { instanceConf_t *inst = (instanceConf_t*) pUsr; errmsg.LogError(0, RS_RET_RELP_AUTH_FAIL, "imrelp[%s]: authentication error '%s', peer " "is '%s'", inst->pszBindPort, errmesg, authinfo); } /* callback for receiving syslog messages. This function is invoked from the * RELP engine when a syslog message arrived. It must return a relpRetVal, * with anything else but RELP_RET_OK terminating the relp session. Please note * that RELP_RET_OK is equal to RS_RET_OK and the other libRELP error codes * are different from our rsRetVal. So we can simply use our own iRet system * to fulfill the requirement. * rgerhards, 2008-03-21 * Note: librelp 1.0.0 is required in order to receive the IP address, otherwise * we will only see the hostname (twice). -- rgerhards, 2009-10-14 */ static relpRetVal onSyslogRcv(void *pUsr, uchar *pHostname, uchar *pIP, uchar *msg, size_t lenMsg) { prop_t *pProp = NULL; msg_t *pMsg; instanceConf_t *inst = (instanceConf_t*) pUsr; DEFiRet; CHKiRet(msgConstruct(&pMsg)); MsgSetInputName(pMsg, pInputName); MsgSetRawMsg(pMsg, (char*)msg, lenMsg); MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY); MsgSetRuleset(pMsg, runModConf->pBindRuleset); pMsg->msgFlags = PARSE_HOSTNAME | NEEDS_PARSING; /* TODO: optimize this, we can store it inside the session, requires * changes to librelp --> next librelp iteration?. rgerhards, 2012-10-29 */ MsgSetRcvFromStr(pMsg, pHostname, ustrlen(pHostname), &pProp); CHKiRet(prop.Destruct(&pProp)); CHKiRet(MsgSetRcvFromIPStr(pMsg, pIP, ustrlen(pIP), &pProp)); CHKiRet(prop.Destruct(&pProp)); CHKiRet(submitMsg2(pMsg)); STATSCOUNTER_INC(inst->data.ctrSubmit, inst->data.mutCtrSubmit); finalize_it: RETiRet; } /* ------------------------------ end callbacks ------------------------------ */ /* create input instance, set default paramters, and * add it to the list of instances. */ static rsRetVal createInstance(instanceConf_t **pinst) { instanceConf_t *inst; DEFiRet; CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); inst->next = NULL; inst->pszBindPort = NULL; inst->bKeepAlive = 0; inst->bEnableTLS = 0; inst->bEnableTLSZip = 0; inst->dhBits = 0; inst->pristring = NULL; inst->authmode = NULL; inst->permittedPeers.nmemb = 0; inst->caCertFile = NULL; inst->myCertFile = NULL; inst->myPrivKeyFile = NULL; /* node created, let's add to config */ if(loadModConf->tail == NULL) { loadModConf->tail = loadModConf->root = inst; } else { loadModConf->tail->next = inst; loadModConf->tail = inst; } *pinst = inst; finalize_it: RETiRet; } /* modified to work for module, not instance (as usual) */ static inline void std_checkRuleset_genErrMsg(modConfData_t *modConf, __attribute__((unused)) instanceConf_t *inst) { errmsg.LogError(0, NO_ERRCODE, "imrelp: ruleset '%s' not found - " "using default ruleset instead", modConf->pszBindRuleset); } /* This function is called when a new listener instance shall be added to * the current config object via the legacy config system. It just shuffles * all parameters to the listener in-memory instance. * rgerhards, 2011-05-04 */ static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) { instanceConf_t *inst; DEFiRet; CHKiRet(createInstance(&inst)); if(pNewVal == NULL || *pNewVal == '\0') { errmsg.LogError(0, NO_ERRCODE, "imrelp: port number must be specified, listener ignored"); } inst->pszBindPort = pNewVal; finalize_it: RETiRet; } static rsRetVal addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst) { relpSrv_t *pSrv; int relpRet; uchar statname[64]; int i; DEFiRet; if(pRelpEngine == NULL) { CHKiRet(relpEngineConstruct(&pRelpEngine)); CHKiRet(relpEngineSetDbgprint(pRelpEngine, dbgprintf)); CHKiRet(relpEngineSetFamily(pRelpEngine, glbl.GetDefPFFamily())); CHKiRet(relpEngineSetEnableCmd(pRelpEngine, (uchar*) "syslog", eRelpCmdState_Required)); CHKiRet(relpEngineSetSyslogRcv2(pRelpEngine, onSyslogRcv)); CHKiRet(relpEngineSetOnErr(pRelpEngine, onErr)); CHKiRet(relpEngineSetOnGenericErr(pRelpEngine, onGenericErr)); CHKiRet(relpEngineSetOnAuthErr(pRelpEngine, onAuthErr)); if (!glbl.GetDisableDNS()) { CHKiRet(relpEngineSetDnsLookupMode(pRelpEngine, 1)); } } CHKiRet(relpEngineListnerConstruct(pRelpEngine, &pSrv)); CHKiRet(relpSrvSetLstnPort(pSrv, inst->pszBindPort)); /* support statistics gathering */ CHKiRet(statsobj.Construct(&(inst->data.stats))); snprintf((char*)statname, sizeof(statname), "imrelp(%s)", inst->pszBindPort); statname[sizeof(statname)-1] = '\0'; /* just to be on the save side... */ CHKiRet(statsobj.SetName(inst->data.stats, statname)); STATSCOUNTER_INIT(inst->data.ctrSubmit, inst->data.mutCtrSubmit); CHKiRet(statsobj.AddCounter(inst->data.stats, UCHAR_CONSTANT("submitted"), ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(inst->data.ctrSubmit))); CHKiRet(statsobj.ConstructFinalize(inst->data.stats)); /* end stats counters */ relpSrvSetUsrPtr(pSrv, inst); relpSrvSetKeepAlive(pSrv, inst->bKeepAlive, inst->iKeepAliveIntvl, inst->iKeepAliveProbes, inst->iKeepAliveTime); if(inst->bEnableTLS) { relpRet = relpSrvEnableTLS2(pSrv); if(relpRet == RELP_RET_ERR_NO_TLS) { errmsg.LogError(0, RS_RET_RELP_NO_TLS, "imrelp: could not activate relp TLS, librelp " "does not support it (most probably GnuTLS lib " "is too old)!"); ABORT_FINALIZE(RS_RET_RELP_NO_TLS); } else if(relpRet != RELP_RET_OK) { errmsg.LogError(0, RS_RET_RELP_ERR, "imrelp: could not activate relp TLS, code %d", relpRet); ABORT_FINALIZE(RS_RET_RELP_ERR); } if(inst->bEnableTLSZip) { relpSrvEnableTLSZip2(pSrv); } if(inst->dhBits) { relpSrvSetDHBits(pSrv, inst->dhBits); } relpSrvSetGnuTLSPriString(pSrv, (char*)inst->pristring); if(relpSrvSetAuthMode(pSrv, (char*)inst->authmode) != RELP_RET_OK) { errmsg.LogError(0, RS_RET_RELP_ERR, "imrelp: invalid auth mode '%s'", inst->authmode); ABORT_FINALIZE(RS_RET_RELP_ERR); } if(relpSrvSetCACert(pSrv, (char*) inst->caCertFile) != RELP_RET_OK) ABORT_FINALIZE(RS_RET_RELP_ERR); if(relpSrvSetOwnCert(pSrv, (char*) inst->myCertFile) != RELP_RET_OK) ABORT_FINALIZE(RS_RET_RELP_ERR); if(relpSrvSetPrivKey(pSrv, (char*) inst->myPrivKeyFile) != RELP_RET_OK) ABORT_FINALIZE(RS_RET_RELP_ERR); for(i = 0 ; i < inst->permittedPeers.nmemb ; ++i) { relpSrvAddPermittedPeer(pSrv, (char*)inst->permittedPeers.name[i]); } } relpRet = relpEngineListnerConstructFinalize(pRelpEngine, pSrv); if(relpRet != RELP_RET_OK) { errmsg.LogError(0, RS_RET_RELP_ERR, "imrelp: could not activate relp listner, code %d", relpRet); ABORT_FINALIZE(RS_RET_RELP_ERR); } finalize_it: RETiRet; } BEGINnewInpInst struct cnfparamvals *pvals; instanceConf_t *inst; int i,j; CODESTARTnewInpInst DBGPRINTF("newInpInst (imrelp)\n"); pvals = nvlstGetParams(lst, &inppblk, NULL); if(pvals == NULL) { errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "imrelp: required parameter are missing\n"); ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } if(Debug) { dbgprintf("input param blk in imrelp:\n"); cnfparamsPrint(&inppblk, pvals); } CHKiRet(createInstance(&inst)); for(i = 0 ; i < inppblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(inppblk.descr[i].name, "port")) { inst->pszBindPort = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "keepalive")) { inst->bKeepAlive = (sbool) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "keepalive.probes")) { inst->iKeepAliveProbes = (int) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "keepalive.time")) { inst->iKeepAliveTime = (int) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "keepalive.interval")) { inst->iKeepAliveIntvl = (int) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "tls")) { inst->bEnableTLS = (unsigned) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "tls.dhbits")) { inst->dhBits = (unsigned) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "tls.prioritystring")) { inst->pristring = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "tls.authmode")) { inst->authmode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "tls.compression")) { inst->bEnableTLSZip = (unsigned) pvals[i].val.d.n; } else if(!strcmp(inppblk.descr[i].name, "tls.cacert")) { inst->caCertFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "tls.mycert")) { inst->myCertFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "tls.myprivkey")) { inst->myPrivKeyFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else if(!strcmp(inppblk.descr[i].name, "tls.permittedpeer")) { inst->permittedPeers.nmemb = pvals[i].val.d.ar->nmemb; CHKmalloc(inst->permittedPeers.name = malloc(sizeof(uchar*) * inst->permittedPeers.nmemb)); for(j = 0 ; j < pvals[i].val.d.ar->nmemb ; ++j) { inst->permittedPeers.name[j] = (uchar*)es_str2cstr(pvals[i].val.d.ar->arr[j], NULL); } } else { dbgprintf("imrelp: program error, non-handled " "param '%s'\n", inppblk.descr[i].name); } } finalize_it: CODE_STD_FINALIZERnewInpInst cnfparamvalsDestruct(pvals, &inppblk); ENDnewInpInst BEGINbeginCnfLoad CODESTARTbeginCnfLoad loadModConf = pModConf; pModConf->pConf = pConf; pModConf->pszBindRuleset = NULL; pModConf->pBindRuleset = NULL; /* init legacy config variables */ cs.pszBindRuleset = NULL; ENDbeginCnfLoad BEGINsetModCnf struct cnfparamvals *pvals = NULL; int i; CODESTARTsetModCnf pvals = nvlstGetParams(lst, &modpblk, NULL); if(pvals == NULL) { errmsg.LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module " "config parameters [module(...)]"); ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); } if(Debug) { dbgprintf("module (global) param blk for imrelp:\n"); cnfparamsPrint(&modpblk, pvals); } for(i = 0 ; i < modpblk.nParams ; ++i) { if(!pvals[i].bUsed) continue; if(!strcmp(modpblk.descr[i].name, "ruleset")) { loadModConf->pszBindRuleset = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); } else { dbgprintf("imrelp: program error, non-handled " "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); } } finalize_it: if(pvals != NULL) cnfparamvalsDestruct(pvals, &modpblk); ENDsetModCnf BEGINendCnfLoad CODESTARTendCnfLoad if(loadModConf->pszBindRuleset == NULL) { if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { loadModConf->pszBindRuleset = NULL; } else { CHKmalloc(loadModConf->pszBindRuleset = ustrdup(cs.pszBindRuleset)); } } else { if((cs.pszBindRuleset != NULL) && (cs.pszBindRuleset[0] != '\0')) { errmsg.LogError(0, RS_RET_DUP_PARAM, "imrelp: warning: ruleset " "set via legacy directive ignored"); } } finalize_it: free(cs.pszBindRuleset); loadModConf = NULL; /* done loading */ ENDendCnfLoad BEGINcheckCnf rsRetVal localRet; ruleset_t *pRuleset; CODESTARTcheckCnf /* we emulate the standard "ruleset query" code provided by the framework * for *instances* (which we can currently not support due to librelp). */ if(pModConf->pszBindRuleset == NULL) { pModConf->pBindRuleset = NULL; } else { DBGPRINTF("imrelp: using ruleset '%s'\n", pModConf->pszBindRuleset); localRet = ruleset.GetRuleset(pModConf->pConf, &pRuleset, pModConf->pszBindRuleset); if(localRet == RS_RET_NOT_FOUND) { std_checkRuleset_genErrMsg(pModConf, NULL); } CHKiRet(localRet); pModConf->pBindRuleset = pRuleset; } finalize_it: ENDcheckCnf BEGINactivateCnfPrePrivDrop instanceConf_t *inst; CODESTARTactivateCnfPrePrivDrop runModConf = pModConf; for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { addListner(pModConf, inst); } if(pRelpEngine == NULL) ABORT_FINALIZE(RS_RET_NO_RUN); finalize_it: ENDactivateCnfPrePrivDrop BEGINactivateCnf CODESTARTactivateCnf ENDactivateCnf BEGINfreeCnf instanceConf_t *inst, *del; int i; CODESTARTfreeCnf for(inst = pModConf->root ; inst != NULL ; ) { free(inst->pszBindPort); free(inst->pristring); free(inst->authmode); statsobj.Destruct(&(inst->data.stats)); for(i = 0 ; i < inst->permittedPeers.nmemb ; ++i) { free(inst->permittedPeers.name[i]); } del = inst; inst = inst->next; free(del); } free(pModConf->pszBindRuleset); ENDfreeCnf /* This is used to terminate the plugin. Note that the signal handler blocks * other activity on the thread. As such, it is safe to request the stop. When * we terminate, relpEngine is called, and it's select() loop interrupted. But * only *after this function is done*. So we do not have a race! */ static void doSIGTTIN(int __attribute__((unused)) sig) { DBGPRINTF("imrelp: termination requested via SIGTTIN - telling RELP engine\n"); relpEngineSetStop(pRelpEngine); } /* This function is called to gather input. */ BEGINrunInput sigset_t sigSet; struct sigaction sigAct; CODESTARTrunInput /* we want to support non-cancel input termination. To do so, we must signal librelp * when to stop. As we run on the same thread, we need to register as SIGTTIN handler, * which will be used to put the terminating condition into librelp. */ sigfillset(&sigSet); pthread_sigmask(SIG_BLOCK, &sigSet, NULL); sigemptyset(&sigSet); sigaddset(&sigSet, SIGTTIN); pthread_sigmask(SIG_UNBLOCK, &sigSet, NULL); memset(&sigAct, 0, sizeof (sigAct)); sigemptyset(&sigAct.sa_mask); sigAct.sa_handler = doSIGTTIN; sigaction(SIGTTIN, &sigAct, NULL); iRet = relpEngineRun(pRelpEngine); ENDrunInput BEGINwillRun CODESTARTwillRun ENDwillRun BEGINafterRun CODESTARTafterRun /* do cleanup here */ ENDafterRun BEGINmodExit CODESTARTmodExit if(pRelpEngine != NULL) iRet = relpEngineDestruct(&pRelpEngine); /* global variable cleanup */ if(pInputName != NULL) prop.Destruct(&pInputName); /* release objects we used */ objRelease(statsobj, CORE_COMPONENT); objRelease(ruleset, CORE_COMPONENT); objRelease(glbl, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); objRelease(net, LM_NET_FILENAME); objRelease(errmsg, CORE_COMPONENT); ENDmodExit static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { free(cs.pszBindRuleset); cs.pszBindRuleset = NULL; return RS_RET_OK; } BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature if(eFeat == sFEATURENonCancelInputTermination) iRet = RS_RET_OK; ENDisCompatibleWithFeature BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES CODEqueryEtryPt_STD_CONF2_QUERIES CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES CODEqueryEtryPt_STD_CONF2_IMOD_QUERIES CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr pRelpEngine = NULL; /* request objects we use */ CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); CHKiRet(objUse(ruleset, CORE_COMPONENT)); CHKiRet(objUse(statsobj, CORE_COMPONENT)); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverbindruleset", 0, eCmdHdlrGetWord, NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord, addInstance, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); /* we need to create the inputName property (only once during our lifetime) */ CHKiRet(prop.Construct(&pInputName)); CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imrelp"), sizeof("imrelp") - 1)); CHKiRet(prop.ConstructFinalize(pInputName)); ENDmodInit /* vim:set ai: */