From 5eadc4163df392091884d088593dbf4e95e1aec9 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Fri, 1 Feb 2008 13:18:43 +0000 Subject: added input-plugin interface specification in form of a (copy) template input module --- plugins/imtemplate/Makefile.am | 6 + plugins/imtemplate/imtemplate.c | 440 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 446 insertions(+) create mode 100644 plugins/imtemplate/Makefile.am create mode 100644 plugins/imtemplate/imtemplate.c (limited to 'plugins/imtemplate') diff --git a/plugins/imtemplate/Makefile.am b/plugins/imtemplate/Makefile.am new file mode 100644 index 00000000..46a2267e --- /dev/null +++ b/plugins/imtemplate/Makefile.am @@ -0,0 +1,6 @@ +pkglib_LTLIBRARIES = imtemplate.la + +imtemplate_la_SOURCES = imtemplate.c +imtemplate_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(mudflap_cflags) +imtemplate_la_LDFLAGS = $(mudflap_libs) -module -avoid-version +imtemplate_la_LIBADD = diff --git a/plugins/imtemplate/imtemplate.c b/plugins/imtemplate/imtemplate.c new file mode 100644 index 00000000..6cae66c9 --- /dev/null +++ b/plugins/imtemplate/imtemplate.c @@ -0,0 +1,440 @@ +/* imtemplate.c + * + * This is NOT a real input module but a (copy)-template to create one. Please + * do NOT edit this file directly. Rather, copy it, together with the rest of + * the directory, to a new location ./plugins/im, then replace + * all references to imtemplate in Makefile.am to im. Be sure to + * fix the copyright notices to gain proper credit ;) Any derived version, + * however, needs to be placed under GPLv3 (see GPLv3 for details). If you + * do not like that policy, do not use this template or any of the header + * files. The rsyslog project greatly appreciates module contributions, so + * please consider contributing your work - even if you may think it only + * server a single very special purpose. It has turned out that at least some + * folks have similiar special purposes ;) + * + * IMPORTANT + * The comments in this file are actually the interface specification. I decided + * not to put it into a separate file as it is much simpler to keep it up to + * date when it is part of the actual template module. + * + * NAMING + * All input modules shall be named im. While this is not a hard + * requirement, it helps keeping track of things. + * + * Global variables and functions should have a prefix - use as somewhat + * longer one to prevent conflicts with rsyslog itself and other modules + * (OK, hopefully I'll have some more precise advise in the future...). + * + * INCLUDE MODULE IN THE MAIN MAKE SCRIPT + * If the module shall be provided as part of rsyslog (or simply as a build aid, + * you need to add it to the main autoconf files). To do so, you need to edit + * Makefile.am and configure.ac in the main directory. Search for imtemplate + * and copy/modify the relevant code for your plugin. + * + * DEBUGGING + * While you develop your code, you may want to add + * --enable-debug --enable-rtinst + * to your ./configure settings. These enable extra run-time checks, which cost + * a lot of performance but can help detect some of the most frequently made + * bugs. These settings will also provide you with a nice stack dump if something + * goes really wrong. + * + * MORE SAMPLES + * Remember that rsyslog ships with a number of input modules (./plugins/im*). It + * is always a good idea to have a look at them before starting your own. imudp + * may be a good, relatively trivial, sample. + * + * -------------------------------------------------------------------------------- + * + * This template was cretead on 2008-02-01 by Rainer Gerhards. + * + * Copyright 2008 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" /* this is for autotools and always must be the first include */ +#include +#include +#include +#include +#include /* do NOT remove: will soon be done by the module generation macros */ +#include "rsyslog.h" /* error codes etc... */ +#include "cfsysline.h" /* access to config file objects */ +#include "module-template.h" /* generic module interface code - very important, read it! */ +#include "srUtils.h" /* some utility functions */ + +MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ + +/* defines */ + +/* Module static data */ +DEF_IMOD_STATIC_DATA /* must be present, starts static data */ + +/* Here, define whatever static data is needed. Is it suggested that static variables only are + * used (not externally visible). If you need externally visible variables, make sure you use a + * prefix in order not to conflict with other modules or rsyslogd itself (also see comment + * at file header). + */ +/* static int imtemplateWhateverVar = 0; */ + +/* instanceData must be defined to keep the framework happy, but it currently + * is of no practical use. This may change in later revisions of the plugin + * interface. + */ +typedef struct _instanceData { +} instanceData; + +/* config settings */ + + +/* You may add any functions that you feel are useful for your needs. No specific restrictions + * apply, but we suggest that you use the "iRet" call order, which enables you to use debug + * support for your own functions and which also makes it easy to communicate exceptions back + * to the upstream caller (rsyslog framework, for example. + * + * The function below is a sample of how one of your functions may look like. Again, the sample + * below is *not* needed to be present in order to meet the interface requirements. + * + * Be sure to use static functions (suggested) or prefixes to prevent name conflicts -- see file + * header for more information. + */ +static rsRetVal /* rsRetVal is our generic error-reporting return type */ +imtemplateMyFunc(int iMyParam) +{ + DEFiRet; /* define iRet, the return code and other plumbing */ + /* define your local variables here */ + + /* code whatever you need to code here. The "iRet" system can be helpful: + * + * CHKiRet(function(param1, param2, ...)); + * calls a function and checks if it returns RS_RET_OK. If so, work + * proceeds. If some other code is returned, the function is aborted + * and control transferred to finalize_it (which you need to define) + * + * CHKiRet_Hdlr(function(param1, param2, ...)) + * much like CHKiRet, but allows you to specify your own code that is + * executed if the function does not return RS_RET_OK, e.g.: + * CHKiRet_Hdlr(function(a, b)) { + * ... some error handling here ... + * } + * control is not transferred to finalize_it, except if you use one + * of the relevant macros (described below) + * + * FINALIZE + * immediately transfers control to finalize_it, using the current + * value of iRet, e.g. + * if(bDone) + * FINALIZE; + * + * ABORT_FINALIZE(retcode) + * just like FINALIZE, except that iRet is set to the provided error + * code before control is transferred, e.g. + * if((ptr = malloc(20)) == NULL) + * ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + * + * In order for all this to work, you need to define finalize_it, e.g. + * + * finalize_it: + * RETiRet; + * + * RETiRet does some housekeeping and then does a "return iRet" to transfer + * control back to the caller. There shall only be one function exit and + * it shall be via RETiRet, preferrably at the end of the function code. + * + */ + +finalize_it: + /* clean up anything that needs to be cleaned up if processing did not + * go well, for example: + */ + if(iRet != RS_RET_OK) { + /* cleanup, e.g. + * free(somePtr); + */ + } + + RETiRet; +} + + +/* This function is the cancel cleanup handler. It is called when rsyslog decides the + * module must be stopped, what most probably happens during shutdown of rsyslogd. When + * this function is called, the runInput() function (below) is already terminated - somewhere + * in the middle of what it was doing. The cancel cleanup handler below should take + * care of any locked mutexes and such, things that really need to be cleaned up + * before processing continues. In general, many plugins do not need to provide + * any code at all here. + * + * IMPORTANT: the calling interface of this function can NOT be modified. It actually is + * called by pthreads. The provided argument is currently not being used. + */ +/* ------------------------------------------------------------------------------------------ * + * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */ +static void +inputModuleCleanup(void *arg) +{ + BEGINfunc +/* END no-touch zone * + * ------------------------------------------------------------------------------------------ */ + + + + /* your code here */ + + + +/* ------------------------------------------------------------------------------------------ * + * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */ + ENDfunc +} +/* END no-touch zone * + * ------------------------------------------------------------------------------------------ */ + + +/* This function is called by the framework to gather the input. The module stays + * most of its lifetime inside this function. It MUST NEVER exit this function. Doing + * so would end module processing and rsyslog would NOT reschedule the module. If + * you exit from this function, you violate the interface specification! + * + * So how is it terminated? When it is time to terminate, rsyslog actually cancels + * the threads. This may sound scary, but is not. There is a cancel cleanup handler + * defined (the function directly above). See comments there for specifics. + * + * runInput is always called on a single thread. If the module neees multiple threads, + * it is free to create them. HOWEVER, it must make sure that any threads created + * are killed and joined in the cancel cleanup handler. + */ +BEGINrunInput + /* define any local variables you need here */ +CODESTARTrunInput + /* ------------------------------------------------------------------------------------------ * + * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */ + pthread_cleanup_push(inputModuleCleanup, NULL); + while(1) { /* endless loop - do NOT break; out of it! */ + /* END no-touch zone * + * ------------------------------------------------------------------------------------------ */ + + /* your code here */ + + /* All rsyslog objects (see other modules, e.g. msg.c) are available + * to your here. Some useful things are: + * + * logerror("string"); + * logs an error message as syslogd + * + * logerror("string with %s", uchar* ptr) + * just like logerror, but with a variable pointer to a string + * + * The safe way to obtain a system error message is: + * char errStr[1024]; // 1024 is just a (relatively) safe bet... + * rs_strerror_r(errno, errStr, sizeof(errStr)); + * logerror("error occured: %s", errStr); // optional, of course... + * + * To log something to the debug log, simply use + * dbgprintf("fmtstring %d, %s\n", 4711, "like in printf"); + * Be sure to include a newline '\n' at the end of the message, else + * the debug log will become quite cluttered... + * + * There are several ways how a message can be enqueued. This part of the + * interface is currently underspecified. Have a look at the function definitions + * in syslogd.c (sorry, folks...). Some ways are: + * printchopped((char*)fromHost, (char*) pRcvBuf, l, udpLstnSocks[i+1], 1); + */ + + /* ------------------------------------------------------------------------------------------ * + * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */ + } + /*NOTREACHED*/ + + pthread_cleanup_pop(0); /* just for completeness, but never called... */ + RETiRet; /* use it to make sure the housekeeping is done! */ +ENDrunInput + /* END no-touch zone * + * ------------------------------------------------------------------------------------------ */ + + +/* The function is called by rsyslog before runInput() is called. It is a last chance + * to set up anything specific. Most importantly, it can be used to tell rsyslog if the + * input shall run or not. The idea is that if some config settings (or similiar things) + * are not OK, the input can tell rsyslog it will not execute. To do so, return + * RS_RET_NO_RUN or a specific error code. If RS_RET_OK is returned, rsyslog will + * proceed and call the runInput() entry point. + */ +BEGINwillRun + /* place any variables needed here */ +CODESTARTwillRun + + /* ... your code here ... */ + + /* Just to give you an idea, here are some samples (from the actual imudp module: + * + if(udpLstnSocks == NULL) + ABORT_FINALIZE(RS_RET_NO_RUN); + + if((pRcvBuf = malloc(MAXLINE * sizeof(char))) == NULL) { + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + * + */ +finalize_it: +ENDwillRun + + +/* This function is called by the framework after runInput() has been terminated. It + * shall free any resources and prepare the module for unload. + * + * So it is important that runInput() keeps track of what needs to be cleaned up. + * Objects to think about are files (must be closed), network connections, threads (must + * be stopped and joined) and memory (must be freed). Of course, there are a myriad + * of other things, so use your own judgement what you need to do. + * + * Another important chore of this function is to persist whatever state the module + * needs to persist. Unfortunately, there is currently no standard way of doing that. + * Future version of the module interface will probably support it, but that doesn't + * help you right at the moment. In general, it is suggested that anything that needs + * to be persisted is saved in a file, whose name and location is passed in by a + * module-specific config directive. + */ +BEGINafterRun + /* place any variables needed here */ +CODESTARTafterRun + + /* ... do cleanup here ... */ + + /* if you have a string config variable, remember to free its content: + * + if(pszStr != NULL) { + free(pszStr); + pszStr = NULL; + } + */ +ENDafterRun + + +/* The following entry points are defined in module-template.h. + * In general, they need to be present, but you do NOT need to provide + * any code here. + */ +BEGINfreeInstance +CODESTARTfreeInstance +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo +ENDdbgPrintInstInfo + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_IMOD_QUERIES +ENDqueryEtryPt + + +/* The following function shall reset all configuration variables to their + * default values. The code provided in modInit() below registers it to be + * called on "$ResetConfigVariables". You may also call it from other places, + * but in general this is not necessary. Once runInput() has been called, this + * function here is never again called. + */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + DEFiRet; + + /* if you have string variables in you config settings, you need to do this: + if(pszStr != NULL) { + free(pszStr); + pszStr = NULL; + } + * Note that it is vitally important that the pointer is set to NULL, because + * otherwise the framework handler will try to free it a second time when + * a new value is set! + */ + + + /* ... your code here ... */ + + + RETiRet; +} + + +/* modInit() is called once the module is loaded. It must perform all module-wide + * initialization tasks. There are also a number of housekeeping tasks that the + * framework requires. These are handled by the macros. Please note that the + * complexity of processing is depending on the actual module. However, only + * thing absolutely necessary should be done here. Actual app-level processing + * is to be performed in runInput(). A good sample of what to do here may be to + * set some variable defaults. The most important thing probably is registration + * of config command handlers. + */ +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = 1; /* interface spec version this module is written to (currently always 1) */ +CODEmodInit_QueryRegCFSLineHdlr + /* register config file handlers + * For details, see cfsysline.c/.h. The config file is automatically handled. In general, + * a pointer to a variable receiving the value and the config directive is to be supplied. + * A custom function pointer can only be provided, which then is called when the config + * directive appears. Limit this to cases where it is absolutely necessary. The + * STD_LOADABLE_MODULE_ID is a value that identifies the module. It is use to automatically + * unregister the module's config file handlers upon module unload. Do NOT use any other + * value for this parameter! Available Syntaxes (supported types) can be seen in cfsysline.h, + * the ecslCmdHdrlType enum has all that are currently defined. + * + * Config file directives should always be along the lines of + * + * $InputObjObjName + * + * An example would be $InputImtemplateRetriesMax. This is currently not enforced, + * but when we get to our new config file format and reader, this becomes quite + * important. + * + * Please note that config directives must be provided in lower case. The engine + * makes the mapping (what currently means case-insensitive comparison). The dollar + * sign is NOT part of the directive and thus not specified. + * + * Some samples: + * + * A hypothetical integer variable: + * CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputimtemplatemessagenumber", 0, eCmdHdlrInt, + NULL, &intVariable, STD_LOADABLE_MODULE_ID)); + * + * and a hypothetical string variable: + * CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputimtemplatemessagetext", 0, eCmdHdlrGetWord, + * NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID)); + */ + + /* whenever config variables exist, they should be resettable via $ResetConfigVariables. + * The following line adds our handler for that. Note that if you do not have any config + * variables at all (unlikely, I think...), you can remove this handler. + */ + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + + /* ... do whatever else you need to do, but keep it brief ... */ + +ENDmodInit +/* + * vim:set ai: + */ -- cgit