/* module-template.h * This header contains macros that can be used to implement the * plumbing of modules. * * File begun on 2007-07-25 by RGerhards * * Copyright 2007 Rainer Gerhards and Adiscon GmbH. * * This file is part of the rsyslog runtime library. * * The rsyslog runtime library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The rsyslog runtime library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the rsyslog runtime library. If not, see . * * A copy of the GPL can be found in the file "COPYING" in this distribution. * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. */ #ifndef MODULE_TEMPLATE_H_INCLUDED #define MODULE_TEMPLATE_H_INCLUDED 1 #include "modules.h" #include "obj.h" #include "objomsr.h" #include "threads.h" /* macro to define standard output-module static data members */ #define DEF_MOD_STATIC_DATA \ static __attribute__((unused)) rsRetVal (*omsdRegCFSLineHdlr)(); #define DEF_OMOD_STATIC_DATA \ DEF_MOD_STATIC_DATA \ DEFobjCurrIf(obj) \ static __attribute__((unused)) int bCoreSupportsBatching; #define DEF_IMOD_STATIC_DATA \ DEF_MOD_STATIC_DATA \ DEFobjCurrIf(obj) #define DEF_LMOD_STATIC_DATA \ DEF_MOD_STATIC_DATA /* Macro to define the module type. Each module can only have a single type. If * a module provides multiple types, several separate modules must be created which * then should share a single library containing the majority of code. This macro * must be present in each module. -- rgerhards, 2007-12-14 * Note that MODULE_TYPE_TESTBENCH is reserved for testbenches, but * declared in their own header files (because the rest does not need these * defines). -- rgerhards, 2008-06-13 */ #define MODULE_TYPE(x)\ static rsRetVal modGetType(eModType_t *modType) \ { \ *modType = x; \ return RS_RET_OK;\ } #define MODULE_TYPE_INPUT MODULE_TYPE(eMOD_IN) #define MODULE_TYPE_OUTPUT MODULE_TYPE(eMOD_OUT) #define MODULE_TYPE_LIB \ DEF_LMOD_STATIC_DATA \ MODULE_TYPE(eMOD_LIB) /* macro to define a unique module id. This must be able to fit in a void*. The * module id must be unique inside a running rsyslogd application. It is used to * track ownership of several objects. Most importantly, when the module is * unloaded the module id value is used to find what needs to be destroyed. * We currently use a pointer to modExit() as the module id. This sounds to be * reasonable save, as each module must have this entry point AND there is no valid * reason for twice this entry point being in memory. * rgerhards, 2007-11-21 */ #define STD_LOADABLE_MODULE_ID ((void*) modExit) /* macro to implement the "modGetID()" interface function * rgerhards 2007-11-21 */ #define DEFmodGetID \ static rsRetVal modGetID(void **pID) \ { \ *pID = STD_LOADABLE_MODULE_ID;\ return RS_RET_OK;\ } /* to following macros are used to generate function headers and standard * functionality. It works as follows (described on the sample case of * createInstance()): * * BEGINcreateInstance * ... custom variable definitions (on stack) ... (if any) * CODESTARTcreateInstance * ... custom code ... (if any) * ENDcreateInstance */ /* createInstance() */ #define BEGINcreateInstance \ static rsRetVal createInstance(instanceData **ppData)\ {\ DEFiRet; /* store error code here */\ instanceData *pData; /* use this to point to data elements */ #define CODESTARTcreateInstance \ if((pData = calloc(1, sizeof(instanceData))) == NULL) {\ *ppData = NULL;\ ENDfunc \ return RS_RET_OUT_OF_MEMORY;\ } #define ENDcreateInstance \ *ppData = pData;\ RETiRet;\ } /* freeInstance() * This is the cleanup function for the module instance. It is called immediately before * the module instance is destroyed (unloaded). The module should do any cleanup * here, e.g. close file, free instantance heap memory and the like. Control will * not be passed back to the module once this function is finished. Keep in mind, * however, that other instances may still be loaded and used. So do not destroy * anything that may be used by another instance. If you have such a ressource, you * currently need to do the instance counting yourself. */ #define BEGINfreeInstance \ static rsRetVal freeInstance(void* pModData)\ {\ DEFiRet;\ instanceData *pData; #define CODESTARTfreeInstance \ pData = (instanceData*) pModData; #define ENDfreeInstance \ if(pData != NULL)\ free(pData); /* we need to free this in any case */\ RETiRet;\ } /* isCompatibleWithFeature() */ #define BEGINisCompatibleWithFeature \ static rsRetVal isCompatibleWithFeature(syslogFeature __attribute__((unused)) eFeat)\ {\ rsRetVal iRet = RS_RET_INCOMPATIBLE; \ BEGINfunc #define CODESTARTisCompatibleWithFeature #define ENDisCompatibleWithFeature \ RETiRet;\ } /* beginTransaction() * introduced in v4.3.3 -- rgerhards, 2009-04-27 */ #define BEGINbeginTransaction \ static rsRetVal beginTransaction(instanceData __attribute__((unused)) *pData)\ {\ DEFiRet; #define CODESTARTbeginTransaction /* currently empty, but may be extended */ #define ENDbeginTransaction \ RETiRet;\ } /* endTransaction() * introduced in v4.3.3 -- rgerhards, 2009-04-27 */ #define BEGINendTransaction \ static rsRetVal endTransaction(instanceData __attribute__((unused)) *pData)\ {\ DEFiRet; #define CODESTARTendTransaction /* currently empty, but may be extended */ #define ENDendTransaction \ RETiRet;\ } /* doAction() */ #define BEGINdoAction \ static rsRetVal doAction(uchar __attribute__((unused)) **ppString, unsigned __attribute__((unused)) iMsgOpts, instanceData __attribute__((unused)) *pData)\ {\ DEFiRet; #define CODESTARTdoAction \ /* ppString may be NULL if the output module requested no strings */ #define ENDdoAction \ RETiRet;\ } /* dbgPrintInstInfo() * Extra comments: * Print debug information about this instance. */ #define BEGINdbgPrintInstInfo \ static rsRetVal dbgPrintInstInfo(void *pModData)\ {\ DEFiRet;\ instanceData *pData = NULL; #define CODESTARTdbgPrintInstInfo \ pData = (instanceData*) pModData; #define ENDdbgPrintInstInfo \ RETiRet;\ } /* parseSelectorAct() * Extra comments: * try to process a selector action line. Checks if the action * applies to this module and, if so, processed it. If not, it * is left untouched. The driver will then call another module. * On exit, ppModData must point to instance data. Also, a string * request object must be created and filled. A macro is defined * for that. * For the most usual case, we have defined a macro below. * If more than one string is requested, the macro can be used together * with own code that overwrites the entry count. In this case, the * macro must come before the own code. It is recommended to be * placed right after CODESTARTparseSelectorAct. */ #define BEGINparseSelectorAct \ static rsRetVal parseSelectorAct(uchar **pp, void **ppModData, omodStringRequest_t **ppOMSR)\ {\ DEFiRet;\ uchar *p;\ instanceData *pData = NULL; #define CODESTARTparseSelectorAct \ assert(pp != NULL);\ assert(ppModData != NULL);\ assert(ppOMSR != NULL);\ p = *pp; #define CODE_STD_STRING_REQUESTparseSelectorAct(NumStrReqEntries) \ CHKiRet(OMSRconstruct(ppOMSR, NumStrReqEntries)); #define CODE_STD_FINALIZERparseSelectorAct \ finalize_it:\ if(iRet == RS_RET_OK || iRet == RS_RET_SUSPENDED) {\ *ppModData = pData;\ *pp = p;\ } else {\ /* cleanup, we failed */\ if(*ppOMSR != NULL) {\ OMSRdestruct(*ppOMSR);\ *ppOMSR = NULL;\ }\ if(pData != NULL) {\ freeInstance(pData);\ } \ } #define ENDparseSelectorAct \ RETiRet;\ } /* tryResume() * This entry point is called to check if a module can resume operations. This * happens when a module requested that it be suspended. In suspended state, * the engine periodically tries to resume the module. If that succeeds, normal * processing continues. If not, the module will not be called unless a * tryResume() call succeeds. * Returns RS_RET_OK, if resumption succeeded, RS_RET_SUSPENDED otherwise * rgerhard, 2007-08-02 */ #define BEGINtryResume \ static rsRetVal tryResume(instanceData __attribute__((unused)) *pData)\ {\ DEFiRet; #define CODESTARTtryResume \ assert(pData != NULL); #define ENDtryResume \ RETiRet;\ } /* queryEtryPt() */ #define BEGINqueryEtryPt \ DEFmodGetID \ static rsRetVal queryEtryPt(uchar *name, rsRetVal (**pEtryPoint)())\ {\ DEFiRet; #define CODESTARTqueryEtryPt \ if((name == NULL) || (pEtryPoint == NULL)) {\ ENDfunc \ return RS_RET_PARAM_ERROR;\ } \ *pEtryPoint = NULL; #define ENDqueryEtryPt \ if(iRet == RS_RET_OK)\ if(*pEtryPoint == NULL) { \ dbgprintf("entry point '%s' not present in module\n", name); \ iRet = RS_RET_MODULE_ENTRY_POINT_NOT_FOUND;\ } \ RETiRet;\ } /* the following definition is the standard block for queryEtryPt for all types * of modules. It should be included in any module, and typically is so by calling * the module-type specific macros. */ #define CODEqueryEtryPt_STD_MOD_QUERIES \ if(!strcmp((char*) name, "modExit")) {\ *pEtryPoint = modExit;\ } else if(!strcmp((char*) name, "modGetID")) {\ *pEtryPoint = modGetID;\ } else if(!strcmp((char*) name, "getType")) {\ *pEtryPoint = modGetType;\ } /* the following definition is the standard block for queryEtryPt for output * modules. This can be used if no specific handling (e.g. to cover version * differences) is needed. */ #define CODEqueryEtryPt_STD_OMOD_QUERIES \ CODEqueryEtryPt_STD_MOD_QUERIES \ else if(!strcmp((char*) name, "doAction")) {\ *pEtryPoint = doAction;\ } else if(!strcmp((char*) name, "dbgPrintInstInfo")) {\ *pEtryPoint = dbgPrintInstInfo;\ } else if(!strcmp((char*) name, "freeInstance")) {\ *pEtryPoint = freeInstance;\ } else if(!strcmp((char*) name, "parseSelectorAct")) {\ *pEtryPoint = parseSelectorAct;\ } else if(!strcmp((char*) name, "isCompatibleWithFeature")) {\ *pEtryPoint = isCompatibleWithFeature;\ } else if(!strcmp((char*) name, "tryResume")) {\ *pEtryPoint = tryResume;\ } /* the following definition is queryEtryPt block that must be added * if an output module supports the transactional interface. * rgerhards, 2009-04-27 */ #define CODEqueryEtryPt_TXIF_OMOD_QUERIES \ else if(!strcmp((char*) name, "beginTransaction")) {\ *pEtryPoint = beginTransaction;\ } else if(!strcmp((char*) name, "endTransaction")) {\ *pEtryPoint = endTransaction;\ } /* the following definition is a queryEtryPt block that must be added * if a non-output module supports "isCompatibleWithFeature". * rgerhards, 2009-07-20 */ #define CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES \ else if(!strcmp((char*) name, "isCompatibleWithFeature")) {\ *pEtryPoint = isCompatibleWithFeature;\ } /* the following definition is the standard block for queryEtryPt for INPUT * modules. This can be used if no specific handling (e.g. to cover version * differences) is needed. */ #define CODEqueryEtryPt_STD_IMOD_QUERIES \ CODEqueryEtryPt_STD_MOD_QUERIES \ else if(!strcmp((char*) name, "runInput")) {\ *pEtryPoint = runInput;\ } else if(!strcmp((char*) name, "willRun")) {\ *pEtryPoint = willRun;\ } else if(!strcmp((char*) name, "afterRun")) {\ *pEtryPoint = afterRun;\ } /* the following definition is the standard block for queryEtryPt for LIBRARY * modules. This can be used if no specific handling (e.g. to cover version * differences) is needed. */ #define CODEqueryEtryPt_STD_LIB_QUERIES \ CODEqueryEtryPt_STD_MOD_QUERIES /* modInit() * This has an extra parameter, which is the specific name of the modInit * function. That is needed for built-in modules, which must have unique * names in order to link statically. Please note that this is always only * the case with modInit() and NO other entry point. The reason is that only * modInit() is visible form a linker/loader point of view. All other entry * points are passed via rsyslog-internal query functions and are defined * static inside the modules source. This is an important concept, as it allows * us to support different interface versions within a single module. (Granted, * we do not currently have different interface versions, so we can not put * it to a test - but our firm believe is that we can do all abstraction needed...) * * Extra Comments: * initialize the module * * Later, much more must be done. So far, we only return a pointer * to the queryEtryPt() function * TODO: do interface version checking & handshaking * iIfVersRequetsed is the version of the interface specification that the * caller would like to see being used. ipIFVersProvided is what we * decide to provide. * rgerhards, 2007-11-21: see modExit() comment below for important information * on the need to initialize static data with code. modInit() may be called on a * cached, left-in-memory copy of a previous incarnation. */ #define BEGINmodInit(uniqName) \ rsRetVal modInit##uniqName(int iIFVersRequested __attribute__((unused)), int *ipIFVersProvided, rsRetVal (**pQueryEtryPt)(), rsRetVal (*pHostQueryEtryPt)(uchar*, rsRetVal (**)()), modInfo_t __attribute__((unused)) *pModInfo)\ {\ DEFiRet; \ rsRetVal (*pObjGetObjInterface)(obj_if_t *pIf); #define CODESTARTmodInit \ assert(pHostQueryEtryPt != NULL);\ iRet = pHostQueryEtryPt((uchar*)"objGetObjInterface", &pObjGetObjInterface); \ if((iRet != RS_RET_OK) || (pQueryEtryPt == NULL) || (ipIFVersProvided == NULL) || (pObjGetObjInterface == NULL)) { \ ENDfunc \ return (iRet == RS_RET_OK) ? RS_RET_PARAM_ERROR : iRet; \ } \ /* now get the obj interface so that we can access other objects */ \ CHKiRet(pObjGetObjInterface(&obj)); #define ENDmodInit \ finalize_it:\ *pQueryEtryPt = queryEtryPt;\ RETiRet;\ } /* now come some check functions, which enable a standard way of obtaining feature * information from the core. feat is the to-be-tested feature and featVar is a * variable that receives the result (0-not support, 1-supported). * This must be a macro, so that it is put into the output's code. Otherwise, we * would need to rely on a library entry point, which is what we intend to avoid ;) * rgerhards, 2009-04-27 */ #define INITChkCoreFeature(featVar, feat) \ { \ rsRetVal MACRO_Ret; \ rsRetVal (*pQueryCoreFeatureSupport)(int*, unsigned); \ int bSupportsIt; \ featVar = 0; \ MACRO_Ret = pHostQueryEtryPt((uchar*)"queryCoreFeatureSupport", &pQueryCoreFeatureSupport); \ if(MACRO_Ret == RS_RET_OK) { \ /* found entry point, so let's see if core supports it */ \ CHKiRet((*pQueryCoreFeatureSupport)(&bSupportsIt, feat)); \ if(bSupportsIt) \ featVar = 1; \ } else if(MACRO_Ret != RS_RET_ENTRY_POINT_NOT_FOUND) { \ ABORT_FINALIZE(MACRO_Ret); /* Something else went wrong, what is not acceptable */ \ } \ } /* definitions for host API queries */ #define CODEmodInit_QueryRegCFSLineHdlr \ CHKiRet(pHostQueryEtryPt((uchar*)"regCfSysLineHdlr", &omsdRegCFSLineHdlr)); #endif /* #ifndef MODULE_TEMPLATE_H_INCLUDED */ /* modExit() * This is the counterpart to modInit(). It destroys a module and makes it ready for * unloading. It is similiar to freeInstance() for the instance data. Please note that * this entry point needs to free any module-global data structures and registrations. * For example, the CfSysLineHandlers a module has registered need to be unregistered * here. This entry point is only called immediately before unloading of the module. So * it is likely to be destroyed. HOWEVER, the caller may decide to keep the module cached. * So a module must never assume that it is actually destroyed. A call to modInit() may * happen immediately after modExit(). So a module can NOT assume that static data elements * are being re-initialized by the loader - this must always be done by module code itself. * It is suggested to do this in modInit(). - rgerhards, 2007-11-21 */ #define BEGINmodExit \ static rsRetVal modExit(void)\ {\ DEFiRet; #define CODESTARTmodExit #define ENDmodExit \ RETiRet;\ } /* runInput() * This is the main function for input modules. It is used to gather data from the * input source and submit it to the message queue. Each runInput() instance has its own * thread. This is handled by the rsyslog engine. It needs to spawn off new threads only * if there is a module-internal need to do so. */ #define BEGINrunInput \ static rsRetVal runInput(thrdInfo_t __attribute__((unused)) *pThrd)\ {\ DEFiRet; #define CODESTARTrunInput \ dbgSetThrdName((uchar*)__FILE__); /* we need to provide something better later */ #define ENDrunInput \ RETiRet;\ } /* willRun() * This is a function that will be replaced in the longer term. It is used so * that a module can tell the caller if it will run or not. This is to be replaced * when we introduce input module instances. However, these require config syntax * changes and I may (or may not... ;)) hold that until another config file * format is available. -- rgerhards, 2007-12-17 * returns RS_RET_NO_RUN if it will not run (RS_RET_OK or error otherwise) */ #define BEGINwillRun \ static rsRetVal willRun(void)\ {\ DEFiRet; #define CODESTARTwillRun #define ENDwillRun \ RETiRet;\ } /* afterRun() * This function is called after an input module has been run and its thread has * been terminated. It shall do any necessary cleanup. * This is expected to evolve into a freeInstance type of call once the input module * interface evolves to support multiple instances. * rgerhards, 2007-12-17 */ #define BEGINafterRun \ static rsRetVal afterRun(void)\ {\ DEFiRet; #define CODESTARTafterRun #define ENDafterRun \ RETiRet;\ } /* doHUP() * This function is optional. Currently, it is available to output plugins * only, but may be made available to other types of plugins in the future. * A plugin does not need to define this entry point. If if does, it gets * called when a non-restart type of HUP is done. A plugin should register * this function so that it can close files, connection or other ressources * on HUP - if it can be assume the user wanted to do this as a part of HUP * processing. Note that the name "HUP" has historical reasons, it stems back * to the infamous SIGHUP which was sent to restart a syslogd. We still retain * that legacy, but may move this to a different signal. * rgerhards, 2008-10-22 */ #define CODEqueryEtryPt_doHUP \ else if(!strcmp((char*) name, "doHUP")) {\ *pEtryPoint = doHUP;\ } #define BEGINdoHUP \ static rsRetVal doHUP(instanceData __attribute__((unused)) *pData)\ {\ DEFiRet; #define CODESTARTdoHUP #define ENDdoHUP \ RETiRet;\ } /* vim:set ai: */