summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/imfile/Makefile.am6
-rw-r--r--plugins/imfile/imfile.c509
-rw-r--r--plugins/imgssapi/.cvsignore6
-rw-r--r--plugins/imgssapi/Makefile.am6
-rw-r--r--plugins/imgssapi/imgssapi.c703
-rw-r--r--plugins/imklog/.cvsignore6
-rw-r--r--plugins/imklog/Makefile.am6
-rw-r--r--plugins/imklog/imklog.c688
-rw-r--r--plugins/imklog/imklog.h46
-rw-r--r--plugins/imklog/ksym.c836
-rw-r--r--plugins/imklog/ksym_mod.c482
-rw-r--r--plugins/imklog/ksyms.h37
-rw-r--r--plugins/imklog/module.h35
-rw-r--r--plugins/immark/.cvsignore6
-rw-r--r--plugins/immark/Makefile.am6
-rw-r--r--plugins/immark/immark.c123
-rw-r--r--plugins/immark/immark.h35
-rw-r--r--plugins/imrelp/.cvsignore6
-rw-r--r--plugins/imrelp/Makefile.am6
-rw-r--r--plugins/imrelp/imrelp.c190
-rw-r--r--plugins/imtcp/.cvsignore6
-rw-r--r--plugins/imtcp/Makefile.am6
-rw-r--r--plugins/imtcp/imtcp.c216
-rw-r--r--plugins/imtemplate/Makefile.am6
-rw-r--r--plugins/imtemplate/imtemplate.c459
-rw-r--r--plugins/imudp/.cvsignore6
-rw-r--r--plugins/imudp/Makefile.am6
-rw-r--r--plugins/imudp/imudp.c296
-rw-r--r--plugins/imuxsock/.cvsignore6
-rw-r--r--plugins/imuxsock/Makefile.am6
-rw-r--r--plugins/imuxsock/imuxsock.c351
-rw-r--r--plugins/omgssapi/Makefile.am4
-rw-r--r--plugins/omgssapi/omgssapi.c215
-rw-r--r--plugins/omlibdbi/.cvsignore6
-rw-r--r--plugins/omlibdbi/Makefile.am6
-rw-r--r--plugins/omlibdbi/omlibdbi.c372
-rw-r--r--plugins/ommysql/Makefile.am4
-rw-r--r--plugins/ommysql/ommysql.c72
-rw-r--r--plugins/ommysql/ommysql.h15
-rw-r--r--plugins/ompgsql/Makefile.am4
-rw-r--r--plugins/ompgsql/ompgsql.c47
-rw-r--r--plugins/ompgsql/ompgsql.h15
-rw-r--r--plugins/omrelp/.cvsignore6
-rw-r--r--plugins/omrelp/Makefile.am6
-rw-r--r--plugins/omrelp/omrelp.c334
-rw-r--r--plugins/omsnmp/.cvsignore6
-rw-r--r--plugins/omsnmp/Makefile.am6
-rw-r--r--plugins/omsnmp/mibs/ADISCON-MIB.txt38
-rw-r--r--plugins/omsnmp/mibs/ADISCON-MONITORWARE-MIB.txt362
-rw-r--r--plugins/omsnmp/omsnmp.c533
-rw-r--r--plugins/omsnmp/omsnmp.h106
-rw-r--r--plugins/omtesting/.cvsignore6
-rw-r--r--plugins/omtesting/Makefile.am6
-rw-r--r--plugins/omtesting/omtesting.c183
54 files changed, 7227 insertions, 227 deletions
diff --git a/plugins/imfile/Makefile.am b/plugins/imfile/Makefile.am
new file mode 100644
index 00000000..23b64d1b
--- /dev/null
+++ b/plugins/imfile/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imfile.la
+
+imfile_la_SOURCES = imfile.c
+imfile_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imfile_la_LDFLAGS = -module -avoid-version
+imfile_la_LIBADD =
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
new file mode 100644
index 00000000..162cab9f
--- /dev/null
+++ b/plugins/imfile/imfile.c
@@ -0,0 +1,509 @@
+/* imfile.c
+ *
+ * This is the input module for reading text file data. A text file is a
+ * non-binary file who's lines are delemited by the \n character.
+ *
+ * Work originally begun 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h> /* do NOT remove: will soon be done by the module generation macros */
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include "rsyslog.h" /* error codes etc... */
+#include "syslogd.h"
+#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 */
+#include "msg.h"
+#include "stream.h"
+#include "errmsg.h"
+#include "datetime.h"
+
+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 */
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(datetime)
+
+typedef struct fileInfo_s {
+ uchar *pszFileName;
+ uchar *pszTag;
+ uchar *pszStateFile; /* file in which state between runs is to be stored */
+ int iFacility;
+ int iSeverity;
+ strm_t *pStrm; /* its stream (NULL if not assigned) */
+} fileInfo_t;
+
+
+/* config variables */
+static uchar *pszFileName = NULL;
+static uchar *pszFileTag = NULL;
+static uchar *pszStateFile = NULL;
+static int iPollInterval = 10; /* number of seconds to sleep when there was no file activity */
+static int iFacility = 128; /* local0 */
+static int iSeverity = 5; /* notice, as of rfc 3164 */
+
+static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */
+#define MAX_INPUT_FILES 100
+static fileInfo_t files[MAX_INPUT_FILES];
+
+
+/* enqueue the read file line as a message. The provided string is
+ * not freed - thuis must be done by the caller.
+ */
+static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
+{
+ DEFiRet;
+ msg_t *pMsg;
+
+ if(rsCStrLen(cstrLine) == 0) {
+ /* we do not process empty lines */
+ FINALIZE;
+ }
+
+ CHKiRet(msgConstruct(&pMsg));
+ MsgSetFlowControlType(pMsg, eFLOWCTL_FULL_DELAY);
+ MsgSetUxTradMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
+ MsgSetRawMsg(pMsg, (char*)rsCStrGetSzStr(cstrLine));
+ MsgSetMSG(pMsg, (char*)rsCStrGetSzStr(cstrLine));
+ MsgSetHOSTNAME(pMsg, LocalHostName);
+ MsgSetTAG(pMsg, (char*)pInfo->pszTag);
+ pMsg->iFacility = LOG_FAC(pInfo->iFacility);
+ pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
+ pMsg->bParseHOSTNAME = 0;
+ datetime.getCurrTime(&(pMsg->tTIMESTAMP)); /* use the current time! */
+ CHKiRet(submitMsg(pMsg));
+finalize_it:
+ RETiRet;
+}
+
+
+/* try to open a file. This involves checking if there is a status file and,
+ * if so, reading it in. Processing continues from the last know location.
+ */
+static rsRetVal
+openFile(fileInfo_t *pThis)
+{
+ DEFiRet;
+ strm_t *psSF = NULL;
+ uchar pszSFNam[MAXFNAME];
+ size_t lenSFNam;
+ struct stat stat_buf;
+
+ /* Construct file name */
+ lenSFNam = snprintf((char*)pszSFNam, sizeof(pszSFNam) / sizeof(uchar), "%s/%s",
+ (char*) glblGetWorkDir(), (char*)pThis->pszStateFile);
+
+ /* check if the file exists */
+ if(stat((char*) pszSFNam, &stat_buf) == -1) {
+ if(errno == ENOENT) {
+ /* currently no object! dbgoprint((obj_t*) pThis, "clean startup, no .si file found\n"); */
+ ABORT_FINALIZE(RS_RET_FILE_NOT_FOUND);
+ } else {
+ /* currently no object! dbgoprint((obj_t*) pThis, "error %d trying to access .si file\n", errno); */
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+ }
+
+ /* If we reach this point, we have a .si file */
+
+ CHKiRet(strmConstruct(&psSF));
+ CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_READ));
+ CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strmSetFName(psSF, pszSFNam, lenSFNam));
+ CHKiRet(strmConstructFinalize(psSF));
+
+ /* read back in the object */
+ CHKiRet(obj.Deserialize(&pThis->pStrm, (uchar*) "strm", psSF, NULL, pThis));
+
+ CHKiRet(strmSeekCurrOffs(pThis->pStrm));
+
+ /* OK, we could successfully read the file, so we now can request that it be deleted.
+ * If we need it again, it will be written on the next shutdown.
+ */
+ psSF->bDeleteOnClose = 1;
+
+finalize_it:
+ if(psSF != NULL)
+ strmDestruct(&psSF);
+
+ if(iRet != RS_RET_OK) {
+ CHKiRet(strmConstruct(&pThis->pStrm));
+ CHKiRet(strmSettOperationsMode(pThis->pStrm, STREAMMODE_READ));
+ CHKiRet(strmSetsType(pThis->pStrm, STREAMTYPE_FILE_MONITOR));
+ CHKiRet(strmSetFName(pThis->pStrm, pThis->pszFileName, strlen((char*) pThis->pszFileName)));
+ CHKiRet(strmConstructFinalize(pThis->pStrm));
+ }
+
+ RETiRet;
+}
+
+
+/* The following is a cancel cleanup handler for strmReadLine(). It is necessary in case
+ * strmReadLine() is cancelled while processing the stream. -- rgerhards, 2008-03-27
+ */
+static void pollFileCancelCleanup(void *pArg)
+{
+ BEGINfunc;
+ cstr_t **ppCStr = (cstr_t**) pArg;
+ if(*ppCStr != NULL)
+ rsCStrDestruct(ppCStr);
+ ENDfunc;
+}
+/* poll a file, need to check file rollover etc. open file if not open */
+static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
+{
+ DEFiRet;
+ cstr_t *pCStr = NULL;
+
+ ASSERT(pbHadFileData != NULL);
+
+ if(pThis->pStrm == NULL) {
+ CHKiRet(openFile(pThis)); /* open file */
+ }
+
+ pthread_cleanup_push(pollFileCancelCleanup, &pCStr);
+ /* loop below will be exited when strmReadLine() returns EOF */
+ while(1) {
+ CHKiRet(strmReadLine(pThis->pStrm, &pCStr));
+ *pbHadFileData = 1; /* this is just a flag, so set it and forget it */
+ CHKiRet(enqLine(pThis, pCStr)); /* process line */
+ rsCStrDestruct(&pCStr); /* discard string (must be done by us!) */
+ }
+ pthread_cleanup_pop(0);
+
+finalize_it:
+ if(pCStr != NULL) {
+ rsCStrDestruct(&pCStr);
+ }
+
+ 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 __attribute__((unused)) *arg)
+{
+ BEGINfunc
+/* END no-touch zone *
+ * ------------------------------------------------------------------------------------------ */
+
+
+
+ /* so far not needed */
+
+
+
+/* ------------------------------------------------------------------------------------------ *
+ * 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!
+ *
+ * We go through all files and remember if at least one had data. If so, we do
+ * another run (until no data was present in any file). Then we sleep for
+ * PollInterval seconds and restart the whole process. This ensures that as
+ * long as there is some data present, it will be processed at the fastest
+ * possible pace - probably important for busy systmes. If we monitor just a
+ * single file, the algorithm is slightly modified. In that case, the sleep
+ * hapens immediately. The idea here is that if we have just one file, we
+ * returned from the file processer because that file had no additional data.
+ * So even if we found some lines, it is highly unlikely to find a new one
+ * just now. Trying it would result in a performance-costly additional try
+ * which in the very, very vast majority of cases will never find any new
+ * lines.
+ * On spamming the main queue: keep in mind that it will automatically rate-limit
+ * ourselfes if we begin to overrun it. So we really do not need to care here.
+ */
+BEGINrunInput
+ int i;
+ int bHadFileData; /* were there at least one file with data during this run? */
+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 *
+ * ------------------------------------------------------------------------------------------ */
+
+ do {
+ bHadFileData = 0;
+ for(i = 0 ; i < iFilPtr ; ++i) {
+ pollFile(&files[i], &bHadFileData);
+ }
+ } while(iFilPtr > 1 && bHadFileData == 1); /* waring: do...while()! */
+
+ /* Note: the additional 10ns wait is vitally important. It guards rsyslog against totally
+ * hogging the CPU if the users selects a polling interval of 0 seconds. It doesn't hurt any
+ * other valid scenario. So do not remove. -- rgerhards, 2008-02-14
+ */
+ srSleep(iPollInterval, 10);
+
+ /* ------------------------------------------------------------------------------------------ *
+ * 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
+CODESTARTwillRun
+ if(iFilPtr == 0) {
+ errmsg.LogError(NO_ERRCODE, "No files configured to be monitored");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+finalize_it:
+ENDwillRun
+
+
+
+/* This function persists information for a specific file being monitored.
+ * To do so, it simply persists the stream object. We do NOT abort on error
+ * iRet as that makes matters worse (at least we can try persisting the others...).
+ * rgerhards, 2008-02-13
+ */
+static rsRetVal
+persistStrmState(fileInfo_t *pInfo)
+{
+ DEFiRet;
+ strm_t *psSF = NULL; /* state file (stream) */
+
+ ASSERT(pInfo != NULL);
+
+ /* TODO: create a function persistObj in obj.c? */
+ CHKiRet(strmConstruct(&psSF));
+ CHKiRet(strmSetDir(psSF, glblGetWorkDir(), strlen((char*)glblGetWorkDir())));
+ CHKiRet(strmSettOperationsMode(psSF, STREAMMODE_WRITE));
+ CHKiRet(strmSetiAddtlOpenFlags(psSF, O_TRUNC));
+ CHKiRet(strmSetsType(psSF, STREAMTYPE_FILE_SINGLE));
+ CHKiRet(strmSetFName(psSF, pInfo->pszStateFile, strlen((char*) pInfo->pszStateFile)));
+ CHKiRet(strmConstructFinalize(psSF));
+
+ CHKiRet(strmSerialize(pInfo->pStrm, psSF));
+
+ CHKiRet(strmDestruct(&psSF));
+
+finalize_it:
+ if(psSF != NULL)
+ strmDestruct(&psSF);
+
+ RETiRet;
+}
+
+
+/* This function is called by the framework after runInput() has been terminated. It
+ * shall free any resources and prepare the module for unload.
+ */
+BEGINafterRun
+ int i;
+CODESTARTafterRun
+ /* Close files and persist file state information. We do NOT abort on error iRet as that makes
+ * matters worse (at least we can try persisting the others...). Please note that, under stress
+ * conditions, it may happen that we are terminated before we actuall could open all streams. So
+ * before we change anything, we need to make sure the stream was open.
+ */
+ for(i = 0 ; i < iFilPtr ; ++i) {
+ if(files[i].pStrm != NULL) { /* stream open? */
+ persistStrmState(&files[i]);
+ strmDestruct(&(files[i].pStrm));
+ }
+ }
+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.
+ */
+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(pszFileName != NULL) {
+ free(pszFileName);
+ pszFileName = NULL;
+ }
+
+ if(pszFileTag != NULL) {
+ free(pszFileTag);
+ pszFileTag = NULL;
+ }
+
+ if(pszStateFile != NULL) {
+ free(pszFileTag);
+ pszFileTag = NULL;
+ }
+
+
+ /* set defaults... */
+ iPollInterval = 10;
+ iFacility = 128; /* local0 */
+ iSeverity = 5; /* notice, as of rfc 3164 */
+
+ RETiRet;
+}
+
+
+/* add a new monitor */
+static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ fileInfo_t *pThis;
+
+ free(pNewVal); /* we do not need it, but we must free it! */
+
+ if(iFilPtr < MAX_INPUT_FILES) {
+ pThis = &files[iFilPtr];
+ /* TODO: check for strdup() NULL return */
+ if(pszFileName == NULL) {
+ errmsg.LogError(NO_ERRCODE, "imfile error: no file name given, file monitor can not be created");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ } else {
+ pThis->pszFileName = (uchar*) strdup((char*) pszFileName);
+ }
+
+ if(pszFileTag == NULL) {
+ errmsg.LogError(NO_ERRCODE, "imfile error: no tag value given , file monitor can not be created");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ } else {
+ pThis->pszTag = (uchar*) strdup((char*) pszFileTag);
+ }
+
+ if(pszStateFile == NULL) {
+ errmsg.LogError(NO_ERRCODE, "imfile error: not state file name given, file monitor can not be created");
+ ABORT_FINALIZE(RS_RET_CONFIG_ERROR);
+ } else {
+ pThis->pszStateFile = (uchar*) strdup((char*) pszStateFile);
+ }
+
+ pThis->iSeverity = iSeverity;
+ pThis->iFacility = iFacility;
+ } else {
+ errmsg.LogError(NO_ERRCODE, "Too many file monitors configured - ignoring this one");
+ ABORT_FINALIZE(RS_RET_OUT_OF_DESRIPTORS);
+ }
+
+ CHKiRet(resetConfigVariables((uchar*) "dummy", (void*) pThis)); /* values are both dummies */
+
+finalize_it:
+ if(iRet == RS_RET_OK)
+ ++iFilPtr; /* we got a new file to monitor */
+
+ 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 = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord,
+ NULL, &pszFileName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfiletag", 0, eCmdHdlrGetWord,
+ NULL, &pszFileTag, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilestatefile", 0, eCmdHdlrGetWord,
+ NULL, &pszStateFile, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfileseverity", 0, eCmdHdlrSeverity,
+ NULL, &iSeverity, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilefacility", 0, eCmdHdlrFacility,
+ NULL, &iFacility, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilepollinterval", 0, eCmdHdlrInt,
+ NULL, &iPollInterval, STD_LOADABLE_MODULE_ID));
+ /* that command ads a new file! */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrunfilemonitor", 0, eCmdHdlrGetWord,
+ addMonitor, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/* vim:set ai:
+ */
diff --git a/plugins/imgssapi/.cvsignore b/plugins/imgssapi/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imgssapi/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imgssapi/Makefile.am b/plugins/imgssapi/Makefile.am
new file mode 100644
index 00000000..42a243f4
--- /dev/null
+++ b/plugins/imgssapi/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imgssapi.la
+
+imgssapi_la_SOURCES = imgssapi.c
+imgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imgssapi_la_LDFLAGS = -module -avoid-version
+imgssapi_la_LIBADD = $(gss_libs)
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
new file mode 100644
index 00000000..74d5d5c5
--- /dev/null
+++ b/plugins/imgssapi/imgssapi.c
@@ -0,0 +1,703 @@
+/* imgssapi.c
+ * This is the implementation of the GSSAPI input module.
+ *
+ * Note: the root gssapi code was contributed by varmojfekoj and is most often
+ * maintened by him. I am just doing the plumbing around it (I event don't have a
+ * test lab for gssapi yet... ). I am very grateful for this useful code
+ * contribution -- rgerhards, 2008-03-05
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <gssapi/gssapi.h>
+#include "rsyslog.h"
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+#include "srUtils.h"
+#include "gss-misc.h"
+#include "tcpsrv.h"
+#include "tcps_sess.h"
+#include "errmsg.h"
+
+
+MODULE_TYPE_INPUT
+
+/* defines */
+#define ALLOWEDMETHOD_GSS 2
+#define ALLOWEDMETHOD_TCP 1
+
+
+/* some forward definitions - they may go away when we no longer include imtcp.c */
+static rsRetVal addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal);
+static int TCPSessGSSInit(void);
+static void TCPSessGSSClose(tcps_sess_t* pSess);
+static int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len);
+static rsRetVal onSessAccept(tcpsrv_t *pThis, tcps_sess_t *ppSess);
+static rsRetVal OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *ppSess);
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(tcpsrv)
+DEFobjCurrIf(tcps_sess)
+DEFobjCurrIf(gssutil)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(net)
+
+static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
+static gss_cred_id_t gss_server_creds = GSS_C_NO_CREDENTIAL;
+
+/* our usr structure for the tcpsrv object */
+typedef struct gsssrv_s {
+ char allowedMethods;
+} gsssrv_t;
+
+/* our usr structure for the session object */
+typedef struct gss_sess_s {
+ OM_uint32 gss_flags;
+ gss_ctx_id_t gss_context;
+ char allowedMethods;
+} gss_sess_t;
+
+
+/* config variables */
+static int iTCPSessMax = 200; /* max number of sessions */
+static char *gss_listen_service_name = NULL;
+static int bPermitPlainTcp = 0; /* plain tcp syslog allowed on GSSAPI port? */
+
+
+/* methods */
+/* callbacks */
+static rsRetVal OnSessConstructFinalize(void *ppUsr)
+{
+ DEFiRet;
+ gss_sess_t **ppGSess = (gss_sess_t**) ppUsr;
+ gss_sess_t *pGSess;
+
+ assert(ppGSess != NULL);
+
+ if((pGSess = calloc(1, sizeof(gss_sess_t))) == NULL)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+
+ pGSess->gss_flags = 0;
+ pGSess->gss_context = GSS_C_NO_CONTEXT;
+ pGSess->allowedMethods = 0;
+
+ *ppGSess = pGSess;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Destruct the user session pointer for a GSSAPI session. Please note
+ * that it *is* valid to receive a NULL user pointer. In this case, the
+ * sessions is to be torn down before it was fully initialized. This
+ * happens in error cases, e.g. when the host ACL did not match.
+ * rgerhards, 2008-03-03
+ */
+static rsRetVal
+OnSessDestruct(void *ppUsr)
+{
+ DEFiRet;
+ gss_sess_t **ppGSess = (gss_sess_t**) ppUsr;
+
+ assert(ppGSess != NULL);
+ if(*ppGSess == NULL)
+ FINALIZE;
+
+ if((*ppGSess)->allowedMethods & ALLOWEDMETHOD_GSS) {
+ OM_uint32 maj_stat, min_stat;
+ maj_stat = gss_delete_sec_context(&min_stat, &(*ppGSess)->gss_context, GSS_C_NO_BUFFER);
+ if (maj_stat != GSS_S_COMPLETE)
+ gssutil.display_status("deleting context", maj_stat, min_stat);
+ }
+
+ free(*ppGSess);
+ *ppGSess = NULL;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* Check if the host is permitted to send us messages.
+ * Note: the pUsrSess may be zero if the server is running in tcp-only mode!
+ */
+static int
+isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void *pUsrSrv, void*pUsrSess)
+{
+ gsssrv_t *pGSrv;
+ gss_sess_t *pGSess;
+ char allowedMethods = 0;
+
+ BEGINfunc
+ assert(pUsrSrv != NULL);
+ pGSrv = (gsssrv_t*) pUsrSrv;
+ pGSess = (gss_sess_t*) pUsrSess;
+
+ if((pGSrv->allowedMethods & ALLOWEDMETHOD_TCP) &&
+ net.isAllowedSender(net.pAllowedSenders_TCP, addr, (char*)fromHostFQDN))
+ allowedMethods |= ALLOWEDMETHOD_TCP;
+ if((pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) &&
+ net.isAllowedSender(net.pAllowedSenders_GSS, addr, (char*)fromHostFQDN))
+ allowedMethods |= ALLOWEDMETHOD_GSS;
+ if(allowedMethods && pGSess != NULL)
+ pGSess->allowedMethods = allowedMethods;
+ ENDfunc
+ return allowedMethods;
+}
+
+static rsRetVal
+onSessAccept(tcpsrv_t *pThis, tcps_sess_t *pSess)
+{
+ DEFiRet;
+ gsssrv_t *pGSrv;
+
+ pGSrv = (gsssrv_t*) pThis->pUsr;
+
+ if(pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) {
+ iRet = OnSessAcceptGSS(pThis, pSess);
+ }
+
+ RETiRet;
+}
+
+
+static rsRetVal
+onRegularClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ gss_sess_t *pGSess;
+
+ assert(pSess != NULL);
+ assert(pSess->pUsr != NULL);
+ pGSess = (gss_sess_t*) pSess->pUsr;
+
+ if(pGSess->allowedMethods & ALLOWEDMETHOD_GSS)
+ TCPSessGSSClose(pSess);
+ else {
+ /* process any incomplete frames left over */
+ tcps_sess.PrepareClose(pSess);
+ /* Session closed */
+ tcps_sess.Close(pSess);
+ }
+ RETiRet;
+}
+
+
+static rsRetVal
+onErrClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ gss_sess_t *pGSess;
+
+ assert(pSess != NULL);
+ assert(pSess->pUsr != NULL);
+ pGSess = (gss_sess_t*) pSess->pUsr;
+
+ if(pGSess->allowedMethods & ALLOWEDMETHOD_GSS)
+ TCPSessGSSClose(pSess);
+ else
+ tcps_sess.Close(pSess);
+
+ RETiRet;
+}
+
+
+/* open the listen sockets */
+static int*
+doOpenLstnSocks(tcpsrv_t *pSrv)
+{
+ int *pRet = NULL;
+ gsssrv_t *pGSrv;
+
+ ISOBJ_TYPE_assert(pSrv, tcpsrv);
+ pGSrv = pSrv->pUsr;
+ assert(pGSrv != NULL);
+
+ /* first apply some config settings */
+ if(pGSrv->allowedMethods) {
+ if(pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) {
+ if(TCPSessGSSInit()) {
+ errmsg.LogError(NO_ERRCODE, "GSS-API initialization failed\n");
+ pGSrv->allowedMethods &= ~(ALLOWEDMETHOD_GSS);
+ }
+ }
+ if(pGSrv->allowedMethods) {
+ /* fallback to plain TCP */
+ if((pRet = tcpsrv.create_tcp_socket(pSrv)) != NULL) {
+ dbgprintf("Opened %d syslog TCP port(s).\n", *pRet);
+ }
+ }
+ }
+
+ return pRet;
+}
+
+
+static int
+doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf)
+{
+ int state;
+ int allowedMethods;
+ gss_sess_t *pGSess;
+
+ assert(pSess != NULL);
+ assert(pSess->pUsr != NULL);
+ pGSess = (gss_sess_t*) pSess->pUsr;
+
+ allowedMethods = pGSess->allowedMethods;
+ if(allowedMethods & ALLOWEDMETHOD_GSS)
+ state = TCPSessGSSRecv(pSess, buf, lenBuf);
+ else
+ state = recv(pSess->sock, buf, lenBuf, 0);
+ return state;
+}
+
+
+/* end callbacks */
+
+static rsRetVal
+addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ gsssrv_t *pGSrv;
+
+ if(pOurTcpsrv == NULL) {
+ /* first create/init the gsssrv "object" */
+ if((pGSrv = calloc(1, sizeof(gsssrv_t))) == NULL)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+
+ pGSrv->allowedMethods = ALLOWEDMETHOD_GSS;
+ if(bPermitPlainTcp)
+ pGSrv->allowedMethods |= ALLOWEDMETHOD_TCP;
+ /* gsssrv initialized */
+
+ CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
+ CHKiRet(tcpsrv.SetUsrP(pOurTcpsrv, pGSrv));
+ CHKiRet(tcpsrv.SetCBOnSessConstructFinalize(pOurTcpsrv, OnSessConstructFinalize));
+ CHKiRet(tcpsrv.SetCBOnSessDestruct(pOurTcpsrv, OnSessDestruct));
+ CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost));
+ CHKiRet(tcpsrv.SetCBRcvData(pOurTcpsrv, doRcvData));
+ CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks));
+ CHKiRet(tcpsrv.SetCBOnSessAccept(pOurTcpsrv, onSessAccept));
+ CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
+ CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
+ tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* returns 0 if all went OK, -1 if it failed */
+static int TCPSessGSSInit(void)
+{
+ gss_buffer_desc name_buf;
+ gss_name_t server_name;
+ OM_uint32 maj_stat, min_stat;
+
+ if (gss_server_creds != GSS_C_NO_CREDENTIAL)
+ return 0;
+
+ name_buf.value = (gss_listen_service_name == NULL) ? "host" : gss_listen_service_name;
+ name_buf.length = strlen(name_buf.value) + 1;
+ maj_stat = gss_import_name(&min_stat, &name_buf, GSS_C_NT_HOSTBASED_SERVICE, &server_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ gssutil.display_status("importing name", maj_stat, min_stat);
+ return -1;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+ &gss_server_creds, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ gssutil.display_status("acquiring credentials", maj_stat, min_stat);
+ return -1;
+ }
+
+ gss_release_name(&min_stat, &server_name);
+ dbgprintf("GSS-API initialized\n");
+ return 0;
+}
+
+
+/* returns 0 if all went OK, -1 if it failed
+ * tries to guess if the connection uses gssapi.
+ */
+static rsRetVal
+OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess)
+{
+ DEFiRet;
+ gss_buffer_desc send_tok, recv_tok;
+ gss_name_t client;
+ OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
+ gss_ctx_id_t *context;
+ OM_uint32 *sess_flags;
+ int fdSess;
+ char allowedMethods;
+ gsssrv_t *pGSrv;
+ gss_sess_t *pGSess;
+
+ assert(pSess != NULL);
+
+ pGSrv = (gsssrv_t*) pThis->pUsr;
+ pGSess = (gss_sess_t*) pSess->pUsr;
+ allowedMethods = pGSrv->allowedMethods;
+ if(allowedMethods & ALLOWEDMETHOD_GSS) {
+ /* Buffer to store raw message in case that
+ * gss authentication fails halfway through.
+ */
+ char buf[MAXLINE];
+ int ret = 0;
+
+ dbgprintf("GSS-API Trying to accept TCP session %p\n", pSess);
+
+ fdSess = pSess->sock; // TODO: method access!
+ if (allowedMethods & ALLOWEDMETHOD_TCP) {
+ int len;
+ fd_set fds;
+ struct timeval tv;
+
+ do {
+ FD_ZERO(&fds);
+ FD_SET(fdSess, &fds);
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+ ret = select(fdSess + 1, &fds, NULL, NULL, &tv);
+ } while (ret < 0 && errno == EINTR);
+ if (ret < 0) {
+ errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ } else if (ret == 0) {
+ dbgprintf("GSS-API Reverting to plain TCP\n");
+ pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
+ ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
+ }
+
+ do {
+ ret = recv(fdSess, buf, sizeof (buf), MSG_PEEK);
+ } while (ret < 0 && errno == EINTR);
+ if (ret <= 0) {
+ if (ret == 0)
+ dbgprintf("GSS-API Connection closed by peer\n");
+ else
+ errmsg.LogError(NO_ERRCODE, "TCP(GSS) session %p will be closed, error ignored\n", pSess);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+
+ if (ret < 4) {
+ dbgprintf("GSS-API Reverting to plain TCP\n");
+ pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
+ ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
+ } else if (ret == 4) {
+ /* The client might has been interupted after sending
+ * the data length (4B), give him another chance.
+ */
+ srSleep(1, 0);
+ do {
+ ret = recv(fdSess, buf, sizeof (buf), MSG_PEEK);
+ } while (ret < 0 && errno == EINTR);
+ if (ret <= 0) {
+ if (ret == 0)
+ dbgprintf("GSS-API Connection closed by peer\n");
+ else
+ errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+ }
+
+ /* TODO: how does this work together with IPv6? Does it? */
+ len = ntohl((buf[0] << 24)
+ | (buf[1] << 16)
+ | (buf[2] << 8)
+ | buf[3]);
+ if ((ret - 4) < len || len == 0) {
+ dbgprintf("GSS-API Reverting to plain TCP\n");
+ pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
+ ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
+ }
+ }
+
+ context = &pGSess->gss_context;
+ *context = GSS_C_NO_CONTEXT;
+ sess_flags = &pGSess->gss_flags;
+ do {
+ if (gssutil.recv_token(fdSess, &recv_tok) <= 0) {
+ errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+ maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, gss_server_creds,
+ &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &client,
+ NULL, &send_tok, sess_flags, NULL, NULL);
+ if (recv_tok.value) {
+ free(recv_tok.value);
+ recv_tok.value = NULL;
+ }
+ if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) {
+ gss_release_buffer(&min_stat, &send_tok);
+ if (*context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
+ if ((allowedMethods & ALLOWEDMETHOD_TCP) &&
+ (GSS_ROUTINE_ERROR(maj_stat) == GSS_S_DEFECTIVE_TOKEN)) {
+ dbgprintf("GSS-API Reverting to plain TCP\n");
+ dbgprintf("tcp session socket with new data: #%d\n", fdSess);
+ if(tcps_sess.DataRcvd(pSess, buf, ret) != RS_RET_OK) {
+ errmsg.LogError(NO_ERRCODE, "Tearing down TCP Session %p - see "
+ "previous messages for reason(s)\n", pSess);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+ pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
+ ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
+ }
+ gssutil.display_status("accepting context", maj_stat, acc_sec_min_stat);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+ if (send_tok.length != 0) {
+ if(gssutil.send_token(fdSess, &send_tok) < 0) {
+ gss_release_buffer(&min_stat, &send_tok);
+ errmsg.LogError(NO_ERRCODE, "TCP session %p will be closed, error ignored\n", pSess);
+ if (*context != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
+ ABORT_FINALIZE(RS_RET_ERR); // TODO: define good error codes
+ }
+ gss_release_buffer(&min_stat, &send_tok);
+ }
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+ maj_stat = gss_display_name(&min_stat, client, &recv_tok, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ gssutil.display_status("displaying name", maj_stat, min_stat);
+ else
+ dbgprintf("GSS-API Accepted connection from: %s\n", (char*) recv_tok.value);
+ gss_release_name(&min_stat, &client);
+ gss_release_buffer(&min_stat, &recv_tok);
+
+ dbgprintf("GSS-API Provided context flags:\n");
+ gssutil.display_ctx_flags(*sess_flags);
+ pGSess->allowedMethods = ALLOWEDMETHOD_GSS;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* returns: number of bytes read or -1 on error
+ * Replaces recv() for gssapi connections.
+ */
+int TCPSessGSSRecv(tcps_sess_t *pSess, void *buf, size_t buf_len)
+{
+ gss_buffer_desc xmit_buf, msg_buf;
+ gss_ctx_id_t *context;
+ OM_uint32 maj_stat, min_stat;
+ int fdSess;
+ int conf_state;
+ int state, len;
+ gss_sess_t *pGSess;
+
+ assert(pSess->pUsr != NULL);
+ pGSess = (gss_sess_t*) pSess->pUsr;
+
+ fdSess = pSess->sock;
+ if ((state = gssutil.recv_token(fdSess, &xmit_buf)) <= 0)
+ return state;
+
+ context = &pGSess->gss_context;
+ maj_stat = gss_unwrap(&min_stat, *context, &xmit_buf, &msg_buf,
+ &conf_state, (gss_qop_t *) NULL);
+ if(maj_stat != GSS_S_COMPLETE) {
+ gssutil.display_status("unsealing message", maj_stat, min_stat);
+ if(xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+ return (-1);
+ }
+ if (xmit_buf.value) {
+ free(xmit_buf.value);
+ xmit_buf.value = 0;
+ }
+
+ len = msg_buf.length < buf_len ? msg_buf.length : buf_len;
+ memcpy(buf, msg_buf.value, len);
+ gss_release_buffer(&min_stat, &msg_buf);
+
+ return len;
+}
+
+
+/* Takes care of cleaning up gssapi stuff and then calls
+ * TCPSessClose().
+ */
+void TCPSessGSSClose(tcps_sess_t* pSess)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_ctx_id_t *context;
+ gss_sess_t *pGSess;
+
+ assert(pSess->pUsr != NULL);
+ pGSess = (gss_sess_t*) pSess->pUsr;
+
+ context = &pGSess->gss_context;
+ maj_stat = gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
+ if (maj_stat != GSS_S_COMPLETE)
+ gssutil.display_status("deleting context", maj_stat, min_stat);
+ *context = GSS_C_NO_CONTEXT;
+ pGSess->gss_flags = 0;
+ pGSess->allowedMethods = 0;
+
+ tcps_sess.Close(pSess);
+}
+
+
+/* Counterpart of TCPSessGSSInit(). This is called to exit the GSS system
+ * at all. It is a server-based session exit.
+ */
+static rsRetVal
+TCPSessGSSDeinit(void)
+{
+ DEFiRet;
+ OM_uint32 maj_stat, min_stat;
+
+ if (gss_server_creds != GSS_C_NO_CREDENTIAL) {
+ maj_stat = gss_release_cred(&min_stat, &gss_server_creds);
+ if (maj_stat != GSS_S_COMPLETE)
+ gssutil.display_status("releasing credentials", maj_stat, min_stat);
+ }
+ RETiRet;
+}
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+CODESTARTrunInput
+ iRet = tcpsrv.Run(pOurTcpsrv);
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ if(pOurTcpsrv == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+
+ net.PrintAllowedSenders(2); /* TCP */
+ net.PrintAllowedSenders(3); /* GSS */
+finalize_it:
+ENDwillRun
+
+
+
+BEGINmodExit
+CODESTARTmodExit
+ if(pOurTcpsrv != NULL)
+ iRet = tcpsrv.Destruct(&pOurTcpsrv);
+ TCPSessGSSDeinit();
+
+ /* release objects we used */
+ objRelease(tcps_sess, LM_TCPSRV_FILENAME);
+ objRelease(tcpsrv, LM_TCPSRV_FILENAME);
+ objRelease(gssutil, LM_GSSUTIL_FILENAME);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ENDmodExit
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+ if (net.pAllowedSenders_TCP != NULL) {
+ net.clearAllowedSenders (net.pAllowedSenders_TCP);
+ net.pAllowedSenders_TCP = NULL;
+ }
+ if (net.pAllowedSenders_GSS != NULL) {
+ net.clearAllowedSenders (net.pAllowedSenders_GSS);
+ net.pAllowedSenders_GSS = NULL;
+ }
+ENDafterRun
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ if (gss_listen_service_name != NULL) {
+ free(gss_listen_service_name);
+ gss_listen_service_name = NULL;
+ }
+ bPermitPlainTcp = 0;
+ iTCPSessMax = 200;
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current definition */
+CODEmodInit_QueryRegCFSLineHdlr
+ pOurTcpsrv = NULL;
+ /* request objects we use */
+ CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
+ CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
+ CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverpermitplaintcp", 0, eCmdHdlrBinary,
+ NULL, &bPermitPlainTcp, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverrun", 0, eCmdHdlrGetWord,
+ addGSSListener, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssserverservicename", 0, eCmdHdlrGetWord,
+ NULL, &gss_listen_service_name, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputgssservermaxsessions", 0, eCmdHdlrInt,
+ NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/imklog/.cvsignore b/plugins/imklog/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imklog/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imklog/Makefile.am b/plugins/imklog/Makefile.am
new file mode 100644
index 00000000..11e00962
--- /dev/null
+++ b/plugins/imklog/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imklog.la
+
+imklog_la_SOURCES = imklog.c imklog.h module.h ksym.c ksyms.h ksym_mod.c
+imklog_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imklog_la_LDFLAGS = -module -avoid-version
+imklog_la_LIBADD =
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
new file mode 100644
index 00000000..bfea8c6f
--- /dev/null
+++ b/plugins/imklog/imklog.c
@@ -0,0 +1,688 @@
+/* The kernel log input module for Linux. This file heavily
+ * borrows from the klogd daemon provided by the sysklogd project.
+ * Many thanks for this piece of software.
+ *
+ * Please note that this file replaces the klogd daemon that was
+ * also present in pre-v3 versions of rsyslog.
+ *
+ * I have begun to convert this to an input module on 2007-12-17.
+ * IMPORTANT: more than a single instance is currently not supported. This
+ * needs to be revisited once the config file and input module interface
+ * supports multiple instances!
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <pthread.h>
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "imklog.h"
+
+MODULE_TYPE_INPUT
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+
+/* configuration settings TODO: move to instance data? */
+int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */
+static int symbols_twice = 0;
+static int use_syscall = 0;
+static int symbol_lookup = 1;
+/* TODO: configuration for the following directives must be implemented. It
+ * was not done yet because we either do not yet have a config handler for
+ * that type or I thought it was acceptable to push it to a later stage when
+ * I gained more handson experience with the input module interface (and the
+ * changes resulting from that). -- rgerhards, 2007-12-20
+ */
+static char *symfile = NULL;
+static int console_log_level = -1;
+
+
+/* Includes. */
+#include <unistd.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+
+#if HAVE_TIME_H
+# include <time.h>
+#endif
+
+#include <stdarg.h>
+#include <paths.h>
+#include "ksyms.h"
+
+#define __LIBRARY__
+#include <unistd.h>
+
+
+#if !defined(__GLIBC__)
+# define __NR_ksyslog __NR_syslog
+_syscall3(int,ksyslog,int, type, char *, buf, int, len);
+#else
+#include <sys/klog.h>
+#define ksyslog klogctl
+#endif
+
+
+
+#ifndef _PATH_KLOG
+#define _PATH_KLOG "/proc/kmsg"
+#endif
+
+#define LOG_BUFFER_SIZE 4096
+#define LOG_LINE_LENGTH 1000
+
+static int kmsg;
+static char log_buffer[LOG_BUFFER_SIZE];
+
+static enum LOGSRC {none, proc, kernel} logsrc;
+
+
+
+/* Function prototypes. */
+extern int ksyslog(int type, char *buf, int len);
+
+
+/* Write a message to the message queue.
+ * returns -1 if it fails, something else otherwise
+ */
+static rsRetVal writeSyslogV(int iPRI, const char *szFmt, va_list va)
+{
+ DEFiRet;
+ int iChars;
+ int iLen;
+ time_t tNow;
+ char msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */
+
+ assert(szFmt != NULL);
+
+ /* build the message */
+ time(&tNow);
+ /* we can use sprintf safely below, because we know the size of the constants.
+ * By doing so, we save some cpu cycles and code complexity (for unnecessary
+ * error checking).
+ */
+ iLen = sprintf(msgBuf, "<%d>%.15s kernel: ", iPRI, ctime(&tNow) + 4);
+
+ iChars = vsnprintf(msgBuf + iLen, sizeof(msgBuf) / sizeof(char) - iLen, szFmt, va);
+
+ /* here we must create our message object and supply it to the message queue
+ */
+ CHKiRet(parseAndSubmitMessage(LocalHostName, msgBuf, strlen(msgBuf), MSG_DONT_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_LIGHT_DELAY));
+
+finalize_it:
+ RETiRet;
+}
+
+/* And now the same with variable arguments */
+static int writeSyslog(int iPRI, const char *szFmt, ...)
+{
+ int iRet;
+ va_list va;
+
+ assert(szFmt != NULL);
+ va_start(va, szFmt);
+ iRet = writeSyslogV(iPRI, szFmt, va);
+ va_end(va);
+
+ return(iRet);
+}
+
+rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
+rsRetVal Syslog(int priority, char *fmt, ...)
+{
+ DEFiRet;
+ va_list ap;
+ char *argl;
+
+ /* Output using syslog. */
+ if (!strcmp(fmt, "%s")) {
+ va_start(ap, fmt);
+ argl = va_arg(ap, char *);
+ if (argl[0] == '<' && argl[1] && argl[2] == '>') {
+ switch ( argl[1] )
+ {
+ case '0':
+ priority = LOG_EMERG;
+ break;
+ case '1':
+ priority = LOG_ALERT;
+ break;
+ case '2':
+ priority = LOG_CRIT;
+ break;
+ case '3':
+ priority = LOG_ERR;
+ break;
+ case '4':
+ priority = LOG_WARNING;
+ break;
+ case '5':
+ priority = LOG_NOTICE;
+ break;
+ case '6':
+ priority = LOG_INFO;
+ break;
+ case '7':
+ default:
+ priority = LOG_DEBUG;
+ }
+ argl += 3;
+ }
+ iRet = writeSyslog(priority, fmt, argl);
+ va_end(ap);
+ } else {
+ va_start(ap, fmt);
+ iRet = writeSyslogV(priority, fmt, ap);
+ va_end(ap);
+ }
+
+ RETiRet;
+}
+
+
+static void CloseLogSrc(void)
+{
+ /* Turn on logging of messages to console, but only if we had the -c
+ * option -- rgerhards, 2007-08-01
+ */
+ if (console_log_level != -1)
+ ksyslog(7, NULL, 0);
+
+ /* Shutdown the log sources. */
+ switch ( logsrc )
+ {
+ case kernel:
+ ksyslog(0, 0, 0);
+ Syslog(LOG_INFO, "Kernel logging (ksyslog) stopped.");
+ break;
+ case proc:
+ close(kmsg);
+ Syslog(LOG_INFO, "Kernel logging (proc) stopped.");
+ break;
+ case none:
+ break;
+ }
+
+ return;
+}
+
+
+static enum LOGSRC GetKernelLogSrc(void)
+{
+ auto struct stat sb;
+
+ /* Set level of kernel console messaging.. */
+ if ( (console_log_level != -1) &&
+ (ksyslog(8, NULL, console_log_level) < 0) &&
+ (errno == EINVAL) )
+ {
+ /*
+ * An invalid arguement error probably indicates that
+ * a pre-0.14 kernel is being run. At this point we
+ * issue an error message and simply shut-off console
+ * logging completely.
+ */
+ Syslog(LOG_WARNING, "Cannot set console log level - disabling "
+ "console output.");
+ }
+
+ /*
+ * First do a stat to determine whether or not the proc based
+ * file system is available to get kernel messages from.
+ */
+ if ( use_syscall ||
+ ((stat(_PATH_KLOG, &sb) < 0) && (errno == ENOENT)) )
+ {
+ /* Initialize kernel logging. */
+ ksyslog(1, NULL, 0);
+ Syslog(LOG_INFO, "imklogd %s, log source = ksyslog "
+ "started.", VERSION);
+ return(kernel);
+ }
+
+ if ( (kmsg = open(_PATH_KLOG, O_RDONLY)) < 0 )
+ {
+ char sz[512];
+ snprintf(sz, sizeof(sz), "imklog: Cannot open proc file system, %d - %s.\n", errno, strerror(errno));
+ logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE);
+ ksyslog(7, NULL, 0); /* TODO: check this, implement more */
+ return(none);
+ }
+
+ Syslog(LOG_INFO, "imklog %s, log source = %s started.", \
+ VERSION, _PATH_KLOG);
+ return(proc);
+}
+
+
+/* Copy characters from ptr to line until a char in the delim
+ * string is encountered or until min( space, len ) chars have
+ * been copied.
+ *
+ * Returns the actual number of chars copied.
+ */
+static int copyin( char *line, int space,
+ const char *ptr, int len,
+ const char *delim )
+{
+ auto int i;
+ auto int count;
+
+ count = len < space ? len : space;
+
+ for(i=0; i<count && !strchr(delim, *ptr); i++ ) {
+ *line++ = *ptr++;
+ }
+
+ return(i);
+}
+
+/*
+ * Messages are separated by "\n". Messages longer than
+ * LOG_LINE_LENGTH are broken up.
+ *
+ * Kernel symbols show up in the input buffer as : "[<aaaaaa>]",
+ * where "aaaaaa" is the address. These are replaced with
+ * "[symbolname+offset/size]" in the output line - symbolname,
+ * offset, and size come from the kernel symbol table.
+ *
+ * If a kernel symbol happens to fall at the end of a message close
+ * in length to LOG_LINE_LENGTH, the symbol will not be expanded.
+ * (This should never happen, since the kernel should never generate
+ * messages that long.
+ *
+ * To preserve the original addresses, lines containing kernel symbols
+ * are output twice. Once with the symbols converted and again with the
+ * original text. Just in case somebody wants to run their own Oops
+ * analysis on the syslog, e.g. ksymoops.
+ */
+static void LogLine(char *ptr, int len)
+{
+ enum parse_state_enum {
+ PARSING_TEXT,
+ PARSING_SYMSTART, /* at < */
+ PARSING_SYMBOL,
+ PARSING_SYMEND /* at ] */
+ };
+
+ static char line_buff[LOG_LINE_LENGTH];
+
+ static char *line =line_buff;
+ static enum parse_state_enum parse_state = PARSING_TEXT;
+ static int space = sizeof(line_buff)-1;
+
+ static char *sym_start; /* points at the '<' of a symbol */
+
+ auto int delta = 0; /* number of chars copied */
+ auto int symbols_expanded = 0; /* 1 if symbols were expanded */
+ auto int skip_symbol_lookup = 0; /* skip symbol lookup on this pass */
+ auto char *save_ptr = ptr; /* save start of input line */
+ auto int save_len = len; /* save length at start of input line */
+
+ while( len > 0 )
+ {
+ if( space == 0 ) /* line buffer is full */
+ {
+ /*
+ ** Line too long. Start a new line.
+ */
+ *line = 0; /* force null terminator */
+
+ dbgprintf("Line buffer full:\n");
+ dbgprintf("\tLine: %s\n", line);
+
+ Syslog( LOG_INFO, "%s", line_buff );
+ line = line_buff;
+ space = sizeof(line_buff)-1;
+ parse_state = PARSING_TEXT;
+ symbols_expanded = 0;
+ skip_symbol_lookup = 0;
+ save_ptr = ptr;
+ save_len = len;
+ }
+
+ switch( parse_state )
+ {
+ case PARSING_TEXT:
+ delta = copyin( line, space, ptr, len, "\n[" );
+ line += delta;
+ ptr += delta;
+ space -= delta;
+ len -= delta;
+
+ if( space == 0 || len == 0 )
+ {
+ break; /* full line_buff or end of input buffer */
+ }
+
+ if( *ptr == '\0' ) /* zero byte */
+ {
+ ptr++; /* skip zero byte */
+ space -= 1;
+ len -= 1;
+
+ break;
+ }
+
+ if( *ptr == '\n' ) /* newline */
+ {
+ ptr++; /* skip newline */
+ space -= 1;
+ len -= 1;
+
+ *line = 0; /* force null terminator */
+ Syslog( LOG_INFO, "%s", line_buff );
+ line = line_buff;
+ space = sizeof(line_buff)-1;
+ if (symbols_twice) {
+ if (symbols_expanded) {
+ /* reprint this line without symbol lookup */
+ symbols_expanded = 0;
+ skip_symbol_lookup = 1;
+ ptr = save_ptr;
+ len = save_len;
+ }
+ else
+ {
+ skip_symbol_lookup = 0;
+ save_ptr = ptr;
+ save_len = len;
+ }
+ }
+ break;
+ }
+ if( *ptr == '[' ) /* possible kernel symbol */
+ {
+ *line++ = *ptr++;
+ space -= 1;
+ len -= 1;
+ if (!skip_symbol_lookup)
+ parse_state = PARSING_SYMSTART; /* at < */
+ break;
+ }
+ /* Now that line_buff is no longer fed to *printf as format
+ * string, '%'s are no longer "dangerous".
+ */
+ break;
+
+ case PARSING_SYMSTART:
+ if( *ptr != '<' )
+ {
+ parse_state = PARSING_TEXT; /* not a symbol */
+ break;
+ }
+
+ /*
+ ** Save this character for now. If this turns out to
+ ** be a valid symbol, this char will be replaced later.
+ ** If not, we'll just leave it there.
+ */
+
+ sym_start = line; /* this will point at the '<' */
+
+ *line++ = *ptr++;
+ space -= 1;
+ len -= 1;
+ parse_state = PARSING_SYMBOL; /* symbol... */
+ break;
+
+ case PARSING_SYMBOL:
+ delta = copyin( line, space, ptr, len, ">\n[" );
+ line += delta;
+ ptr += delta;
+ space -= delta;
+ len -= delta;
+ if( space == 0 || len == 0 )
+ {
+ break; /* full line_buff or end of input buffer */
+ }
+ if( *ptr != '>' )
+ {
+ parse_state = PARSING_TEXT;
+ break;
+ }
+
+ *line++ = *ptr++; /* copy the '>' */
+ space -= 1;
+ len -= 1;
+
+ parse_state = PARSING_SYMEND;
+
+ break;
+
+ case PARSING_SYMEND:
+ if( *ptr != ']' )
+ {
+ parse_state = PARSING_TEXT; /* not a symbol */
+ break;
+ }
+
+ /*
+ ** It's really a symbol! Replace address with the
+ ** symbol text.
+ */
+ {
+ auto int sym_space;
+
+ unsigned long value;
+ auto struct symbol sym;
+ auto char *symbol;
+
+ *(line-1) = 0; /* null terminate the address string */
+ value = strtoul(sym_start+1, (char **) 0, 16);
+ *(line-1) = '>'; /* put back delim */
+
+ if ( !symbol_lookup || (symbol = LookupSymbol(value, &sym)) == (char *)0 )
+ {
+ parse_state = PARSING_TEXT;
+ break;
+ }
+
+ /*
+ ** verify there is room in the line buffer
+ */
+ sym_space = space + ( line - sym_start );
+ if( (unsigned) sym_space < strlen(symbol) + 30 ) /*(30 should be overkill)*/
+ {
+ parse_state = PARSING_TEXT; /* not enough space */
+ break;
+ }
+
+ delta = sprintf( sym_start, "%s+%d/%d]",
+ symbol, sym.offset, sym.size );
+
+ space = sym_space + delta;
+ line = sym_start + delta;
+ symbols_expanded = 1;
+ }
+ ptr++;
+ len--;
+ parse_state = PARSING_TEXT;
+ break;
+
+ default: /* Can't get here! */
+ parse_state = PARSING_TEXT;
+
+ }
+ }
+
+ return;
+}
+
+
+static void LogKernelLine(void)
+{
+ auto int rdcnt;
+
+ /*
+ * Zero-fill the log buffer. This should cure a multitude of
+ * problems with klogd logging the tail end of the message buffer
+ * which will contain old messages. Then read the kernel log
+ * messages into this fresh buffer.
+ */
+ memset(log_buffer, '\0', sizeof(log_buffer));
+ if ( (rdcnt = ksyslog(2, log_buffer, sizeof(log_buffer)-1)) < 0 )
+ {
+ char sz[512];
+ if(errno == EINTR)
+ return;
+ snprintf(sz, sizeof(sz), "imklog: Error return from sys_sycall: %d - %s\n", errno, strerror(errno));
+ logmsgInternal(LOG_SYSLOG|LOG_ERR, sz, ADDDATE);
+ }
+ else
+ LogLine(log_buffer, rdcnt);
+ return;
+}
+
+
+static void LogProcLine(void)
+{
+ auto int rdcnt;
+
+ /*
+ * Zero-fill the log buffer. This should cure a multitude of
+ * problems with klogd logging the tail end of the message buffer
+ * which will contain old messages. Then read the kernel messages
+ * from the message pseudo-file into this fresh buffer.
+ */
+ memset(log_buffer, '\0', sizeof(log_buffer));
+ if ( (rdcnt = read(kmsg, log_buffer, sizeof(log_buffer)-1)) < 0 )
+ {
+ if ( errno == EINTR )
+ return;
+ Syslog(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno));
+ }
+ else
+ LogLine(log_buffer, rdcnt);
+
+ return;
+}
+
+
+BEGINrunInput
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(!pThrd->bShallStop) {
+ /* we do not need to handle the RS_RET_TERMINATE_NOW case any
+ * special because we just need to terminate. This may be different
+ * if a cleanup is needed. But for now, we can just use CHKiRet().
+ * rgerhards, 2007-12-17
+ */
+ switch ( logsrc )
+ {
+ case kernel:
+ LogKernelLine();
+ break;
+ case proc:
+ LogProcLine();
+ break;
+ case none:
+ /* TODO: We need to handle this case here somewhat more intelligent
+ * This is now at least partly done - code should never reach this point
+ * as willRun() already checked for the "none" status -- rgerhards, 2007-12-17
+ */
+ pause();
+ break;
+ }
+ }
+ RETiRet;
+ENDrunInput
+
+
+BEGINwillRun
+ /* Initialize this module. If that fails, we tell the engine we don't like to run */
+ /* Determine where kernel logging information is to come from. */
+ logsrc = GetKernelLogSrc();
+ if(logsrc == none) {
+ iRet = RS_RET_NO_KERNEL_LOGSRC;
+ } else {
+ if (symbol_lookup) {
+ symbol_lookup = (InitKsyms(symfile) == 1);
+ symbol_lookup |= InitMsyms();
+ if (symbol_lookup == 0) {
+ Syslog(LOG_WARNING, "cannot find any symbols, turning off symbol lookups\n");
+ }
+ }
+ }
+CODESTARTwillRun
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* cleanup here */
+ if(logsrc != none)
+ CloseLogSrc();
+
+ DeinitKsyms();
+ DeinitMsyms();
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ dbgPrintSymbols = 0;
+ symbols_twice = 0;
+ use_syscall = 0;
+ symfile = NULL;
+ symbol_lookup = 1;
+ return RS_RET_OK;
+}
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, NULL, &dbgPrintSymbols, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary, NULL, &symbol_lookup, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary, NULL, &symbols_twice, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary, NULL, &use_syscall, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-basic-offset: 8
+ * tab-width: 8
+ * End:
+ * vi:set ai:
+ */
diff --git a/plugins/imklog/imklog.h b/plugins/imklog/imklog.h
new file mode 100644
index 00000000..71525a79
--- /dev/null
+++ b/plugins/imklog/imklog.h
@@ -0,0 +1,46 @@
+/* imklog.h
+ * These are the definitions for the klog message generation module.
+ *
+ * File begun on 2007-12-17 by RGerhards
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef IMKLOG_H_INCLUDED
+#define IMKLOG_H_INCLUDED 1
+
+#include "rsyslog.h"
+#include "syslogd.h"
+
+/* global variables */
+extern int dbgPrintSymbols;
+
+/* prototypes */
+extern int InitKsyms(char *);
+extern void DeinitKsyms(void);
+extern int InitMsyms(void);
+extern void DeinitMsyms(void);
+extern char * ExpandKadds(char *, char *);
+extern void SetParanoiaLevel(int);
+extern void vsyslog(int pri, const char *fmt, va_list ap);
+rsRetVal Syslog(int priority, char *fmt, ...) __attribute__((format(printf,2, 3)));
+
+#endif /* #ifndef IMKLOG_H_INCLUDED */
+/* vi:set ai:
+ */
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
new file mode 100644
index 00000000..b7d5903e
--- /dev/null
+++ b/plugins/imklog/ksym.c
@@ -0,0 +1,836 @@
+/* ksym.c - functions for kernel address->symbol translation
+ * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ * Copyright (c) 1996 Enjellic Systems Development
+ * Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org>
+ * Copyright (C) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/*
+ * This file contains functions which handle the translation of kernel
+ * numeric addresses into symbols for the klogd utility.
+ *
+ * Sat Oct 28 09:00:14 CDT 1995: Dr. Wettstein
+ * Initial Version.
+ *
+ * Fri Nov 24 12:50:52 CST 1995: Dr. Wettstein
+ * Added VERBOSE_DEBUGGING define to make debugging output more
+ * manageable.
+ *
+ * Added support for verification of the loaded kernel symbols. If
+ * no version information can be be found in the mapfile a warning
+ * message is issued but translation will still take place. This
+ * will be the default case if kernel versions < 1.3.43 are used.
+ *
+ * If the symbols in the mapfile are of the same version as the kernel
+ * that is running an informative message is issued. If the symbols
+ * in the mapfile do not match the current kernel version a warning
+ * message is issued and translation is disabled.
+ *
+ * Wed Dec 6 16:14:11 CST 1995: Dr. Wettstein
+ * Added /boot/System.map to the list of symbol maps to search for.
+ * Also made this map the first item in the search list. I am open
+ * to CONSTRUCTIVE suggestions for any additions or corrections to
+ * the list of symbol maps to search for. Be forewarned that the
+ * list in use is the consensus agreement between myself, Linus and
+ * some package distributers. It is a given that no list will suit
+ * everyone's taste. If you have rabid concerns about the list
+ * please feel free to edit the system_maps array and compile your
+ * own binaries.
+ *
+ * Added support for searching of the list of symbol maps. This
+ * allows support for access to multiple symbol maps. The theory
+ * behind this is that a production kernel may have a system map in
+ * /boot/System.map. If a test kernel is booted this system map
+ * would be skipped in favor of one found in /usr/src/linux.
+ *
+ * Thu Jan 18 11:18:31 CST 1996: Dr. Wettstein
+ * Added patch from beta-testers to allow for reading of both
+ * ELF and a.out map files.
+ *
+ * Wed Aug 21 09:15:49 CDT 1996: Dr. Wettstein
+ * Reloading of kernel module symbols is now turned on by the
+ * SetParanoiaLevel function. The default behavior is to NOT reload
+ * the kernel module symbols when a protection fault is detected.
+ *
+ * Added support for freeing of the current kernel module symbols.
+ * This was necessary to support reloading of the kernel module symbols.
+ *
+ * When a matching static symbol table is loaded the kernel version
+ * number is printed.
+ *
+ * Mon Jun 9 17:12:42 CST 1997: Martin Schulze
+ * Added #1 and #2 to some error messages in order to being able
+ * to divide them (ulmo@Q.Net)
+ *
+ * Fri Jun 13 10:50:23 CST 1997: Martin Schulze
+ * Changed definition of LookupSymbol to non-static because it is
+ * used in klogd.c, too.
+ *
+ * Fri Jan 9 23:00:08 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Fixed bug that caused klogd to die if there is no System.map available.
+ *
+ * Sun 29 Mar 18:14:07 BST 1998: Mark Simon Phillips <M.S.Phillips@nortel.co.uk>
+ * Switched to fgets() as gets() is not buffer overrun secure.
+ *
+ * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Modified loop for detecting the correct system map. Now it won't
+ * stop if a file has been found but doesn't contain the correct map.
+ * Special thanks go go Mark Simon Phillips for the hint.
+ *
+ * Mon Oct 12 00:42:30 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Modified CheckVersion()
+ * . Use shift to decode the kernel version
+ * . Compare integers of kernel version
+ * . extract major.minor.patch from utsname.release via sscanf()
+ * The reason lays in possible use of kernel flavours which
+ * modify utsname.release but no the Version_ symbol.
+ *
+ * Sun Feb 21 22:27:49 EST 1999: Keith Owens <kaos@ocs.com.au>
+ * Fixed bug that caused klogd to die if there is no sym_array available.
+ *
+ * Tue Sep 12 23:48:12 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
+ * Close symbol file in InitKsyms() when an error occurred.
+ */
+
+
+/* Includes. */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/utsname.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include "imklog.h"
+#include "ksyms.h"
+#include "module.h"
+
+
+int num_syms = 0;
+static int i_am_paranoid = 0;
+static char vstring[12];
+static struct sym_table *sym_array = (struct sym_table *) 0;
+
+static char *system_maps[] =
+{
+ "/boot/System.map",
+ "/System.map",
+ NULL
+};
+
+
+/* Function prototypes. */
+static char * FindSymbolFile(void);
+static int AddSymbol(unsigned long, char*);
+static void FreeSymbols(void);
+static int CheckVersion(char *);
+static int CheckMapVersion(char *);
+
+
+/*************************************************************************
+ * Function: InitKsyms
+ *
+ * Purpose: This function is responsible for initializing and loading
+ * the data tables used by the kernel address translations.
+ *
+ * Arguements: (char *) mapfile
+ *
+ * mapfile:-> A pointer to a complete path
+ * specification of the file containing
+ * the kernel map to use.
+ *
+ * Return: int
+ *
+ * A boolean style context is returned. The return value will
+ * be true if initialization was successful. False if not.
+ **************************************************************************/
+extern int InitKsyms(char *mapfile)
+{
+ auto char type,
+ sym[512];
+
+ auto int version = 0;
+
+ auto unsigned long int address;
+
+ auto FILE *sym_file;
+
+
+ /* Check and make sure that we are starting with a clean slate. */
+ if ( num_syms > 0 )
+ FreeSymbols();
+
+
+ /*
+ * Search for and open the file containing the kernel symbols.
+ */
+ if ( mapfile != (char *) 0 ) {
+ if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 )
+ {
+ Syslog(LOG_WARNING, "Cannot open map file: %s.", mapfile);
+ return(0);
+ }
+ } else {
+ if ( (mapfile = FindSymbolFile()) == (char *) 0 ) {
+ Syslog(LOG_WARNING, "Cannot find map file.");
+ dbgprintf("Cannot find map file.\n");
+ return(0);
+ }
+
+ if ( (sym_file = fopen(mapfile, "r")) == (FILE *) 0 ) {
+ Syslog(LOG_WARNING, "Cannot open map file.");
+ dbgprintf("Cannot open map file.\n");
+ return(0);
+ }
+ }
+
+
+ /*
+ * Read the kernel symbol table file and add entries for each
+ * line. I suspect that the use of fscanf is not really in vogue
+ * but it was quick and dirty and IMHO suitable for fixed format
+ * data such as this. If anybody doesn't agree with this please
+ * e-mail me a diff containing a parser with suitable political
+ * correctness -- GW.
+ */
+ while ( !feof(sym_file) ) {
+ if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
+ Syslog(LOG_ERR, "Error in symbol table input (#1).");
+ fclose(sym_file);
+ return(0);
+ }
+ if(dbgPrintSymbols)
+ dbgprintf("Address: %lx, Type: %c, Symbol: %s\n", address, type, sym);
+
+ if ( AddSymbol(address, sym) == 0 ) {
+ Syslog(LOG_ERR, "Error adding symbol - %s.", sym);
+ fclose(sym_file);
+ return(0);
+ }
+
+ if ( version == 0 )
+ version = CheckVersion(sym);
+ }
+
+
+ Syslog(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile);
+ switch(version) {
+ case -1:
+ Syslog(LOG_WARNING, "Symbols do not match kernel version.");
+ num_syms = 0;
+ break;
+
+ case 0:
+ Syslog(LOG_WARNING, "Cannot verify that symbols match kernel version.");
+ break;
+
+ case 1:
+ Syslog(LOG_INFO, "Symbols match kernel version %s.", vstring);
+ break;
+ }
+
+ fclose(sym_file);
+ return(1);
+}
+
+
+extern void DeinitKsyms(void)
+{
+ FreeSymbols();
+}
+
+
+/**************************************************************************
+ * Function: FindSymbolFile
+ *
+ * Purpose: This function is responsible for encapsulating the search
+ * for a valid symbol file. Encapsulating the search for
+ * the map file in this function allows an intelligent search
+ * process to be implemented.
+ *
+ * The list of symbol files will be searched until either a
+ * symbol file is found whose version matches the currently
+ * executing kernel or the end of the list is encountered. If
+ * the end of the list is encountered the first available
+ * symbol file is returned to the caller.
+ *
+ * This strategy allows klogd to locate valid symbol files
+ * for both a production and an experimental kernel. For
+ * example a map for a production kernel could be installed
+ * in /boot. If an experimental kernel is loaded the map
+ * in /boot will be skipped and the map in /usr/src/linux would
+ * be used if its version number matches the executing kernel.
+ *
+ * Arguements: None specified.
+ *
+ * Return: char *
+ *
+ * If a valid system map cannot be located a null pointer
+ * is returned to the caller.
+ *
+ * If the search is succesful a pointer is returned to the
+ * caller which points to the name of the file containing
+ * the symbol table to be used.
+ **************************************************************************/
+static char *FindSymbolFile(void)
+{
+ auto char *file = (char *) 0,
+ **mf = system_maps;
+
+ auto struct utsname utsname;
+ static char symfile[100];
+
+ auto FILE *sym_file = (FILE *) 0;
+
+ if ( uname(&utsname) < 0 ) {
+ Syslog(LOG_ERR, "Cannot get kernel version information.");
+ return(0);
+ }
+
+ dbgprintf("Searching for symbol map.\n");
+
+ for(mf = system_maps; *mf != (char *) 0 && file == (char *) 0; ++mf) {
+
+ snprintf(symfile, sizeof(symfile), "%s-%s", *mf, utsname.release);
+ dbgprintf("Trying %s.\n", symfile);
+ if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
+ if (CheckMapVersion(symfile) == 1)
+ file = symfile;
+ fclose(sym_file);
+ }
+ if (sym_file == (FILE *) 0 || file == (char *) 0) {
+ sprintf (symfile, "%s", *mf);
+ dbgprintf("Trying %s.\n", symfile);
+ if ( (sym_file = fopen(symfile, "r")) != (FILE *) 0 ) {
+ if (CheckMapVersion(symfile) == 1)
+ file = symfile;
+ fclose(sym_file);
+ }
+ }
+
+ }
+
+ /* At this stage of the game we are at the end of the symbol tables. */
+ dbgprintf("End of search list encountered.\n");
+ return(file);
+}
+
+
+/**************************************************************************
+ * Function: CheckVersion
+ *
+ * Purpose: This function is responsible for determining whether or
+ * the system map being loaded matches the version of the
+ * currently running kernel.
+ *
+ * The kernel version is checked by examing a variable which
+ * is of the form: _Version_66347 (a.out) or Version_66437 (ELF).
+ *
+ * The suffix of this variable is the current kernel version
+ * of the kernel encoded in base 256. For example the
+ * above variable would be decoded as:
+ *
+ * (66347 = 1*65536 + 3*256 + 43 = 1.3.43)
+ *
+ * (Insert appropriate deities here) help us if Linus ever
+ * needs more than 255 patch levels to get a kernel out the
+ * door... :-)
+ *
+ * Arguements: (char *) version
+ *
+ * version:-> A pointer to the string which
+ * is to be decoded as a kernel
+ * version variable.
+ *
+ * Return: int
+ *
+ * -1:-> The currently running kernel version does
+ * not match this version string.
+ *
+ * 0:-> The string is not a kernel version variable.
+ *
+ * 1:-> The executing kernel is of the same version
+ * as the version string.
+ **************************************************************************/
+static int CheckVersion(char *version)
+{
+ auto int vnum,
+ major,
+ minor,
+ patch;
+ int kvnum;
+ auto struct utsname utsname;
+
+ static char *prefix = { "Version_" };
+
+
+ /* Early return if there is no hope. */
+ if ( strncmp(version, prefix, strlen(prefix)) == 0 /* ELF */ ||
+ (*version == '_' &&
+ strncmp(++version, prefix, strlen(prefix)) == 0 ) /* a.out */ )
+ ;
+ else
+ return(0);
+
+
+ /* Since the symbol looks like a kernel version we can start
+ * things out by decoding the version string into its component
+ * parts.
+ */
+ vnum = atoi(version + strlen(prefix));
+ patch = vnum & 0x000000FF;
+ minor = (vnum >> 8) & 0x000000FF;
+ major = (vnum >> 16) & 0x000000FF;
+ dbgprintf("Version string = %s, Major = %d, Minor = %d, Patch = %d.\n", version +
+ strlen(prefix), major, minor, patch);
+ sprintf(vstring, "%d.%d.%d", major, minor, patch);
+
+ /* We should now have the version string in the vstring variable in
+ * the same format that it is stored in by the kernel. We now
+ * ask the kernel for its version information and compare the two
+ * values to determine if our system map matches the kernel
+ * version level.
+ */
+ if ( uname(&utsname) < 0 ) {
+ Syslog(LOG_ERR, "Cannot get kernel version information.");
+ return(0);
+ }
+ dbgprintf("Comparing kernel %s with symbol table %s.\n", utsname.release, vstring);
+
+ if ( sscanf (utsname.release, "%d.%d.%d", &major, &minor, &patch) < 3 ) {
+ Syslog(LOG_ERR, "Kernel send bogus release string `%s'.", utsname.release);
+ return(0);
+ }
+
+ /* Compute the version code from data sent by the kernel */
+ kvnum = (major << 16) | (minor << 8) | patch;
+
+ /* Failure. */
+ if ( vnum != kvnum )
+ return(-1);
+
+ /* Success. */
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: CheckMapVersion
+ *
+ * Purpose: This function is responsible for determining whether or
+ * the system map being loaded matches the version of the
+ * currently running kernel. It uses CheckVersion as
+ * backend.
+ *
+ * Arguements: (char *) fname
+ *
+ * fname:-> A pointer to the string which
+ * references the system map file to
+ * be used.
+ *
+ * Return: int
+ *
+ * -1:-> The currently running kernel version does
+ * not match the version in the given file.
+ *
+ * 0:-> No system map file or no version information.
+ *
+ * 1:-> The executing kernel is of the same version
+ * as the version of the map file.
+ **************************************************************************/
+static int CheckMapVersion(char *fname)
+{
+ int version;
+ FILE *sym_file;
+ auto unsigned long int address;
+ auto char type,
+ sym[512];
+
+ if ( (sym_file = fopen(fname, "r")) != (FILE *) 0 ) {
+ /*
+ * At this point a map file was successfully opened. We
+ * now need to search this file and look for version
+ * information.
+ */
+ Syslog(LOG_INFO, "Inspecting %s", fname);
+
+ version = 0;
+ while ( !feof(sym_file) && (version == 0) ) {
+ if ( fscanf(sym_file, "%lx %c %s\n", &address, &type, sym) != 3 ) {
+ Syslog(LOG_ERR, "Error in symbol table input (#2).");
+ fclose(sym_file);
+ return(0);
+ }
+ if(dbgPrintSymbols)
+ dbgprintf("Address: %lx, Type: %c, Symbol: %s\n", address, type, sym);
+ version = CheckVersion(sym);
+ }
+ fclose(sym_file);
+
+ switch ( version ) {
+ case -1:
+ Syslog(LOG_ERR, "Symbol table has incorrect version number.\n");
+ break;
+ case 0:
+ dbgprintf("No version information found.\n");
+ break;
+ case 1:
+ dbgprintf("Found table with matching version number.\n");
+ break;
+ }
+
+ return(version);
+ }
+
+ return(0);
+}
+
+
+/**************************************************************************
+ * Function: AddSymbol
+ *
+ * Purpose: This function is responsible for adding a symbol name
+ * and its address to the symbol table.
+ *
+ * Arguements: (unsigned long) address, (char *) symbol
+ *
+ * Return: int
+ *
+ * A boolean value is assumed. True if the addition is
+ * successful. False if not.
+ **************************************************************************/
+static int AddSymbol(unsigned long address, char *symbol)
+{
+ /* Allocate the the symbol table entry. */
+ sym_array = (struct sym_table *) realloc(sym_array, (num_syms+1) *
+ sizeof(struct sym_table));
+ if ( sym_array == (struct sym_table *) 0 )
+ return(0);
+
+ /* Then the space for the symbol. */
+ sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char) + 1);
+ if ( sym_array[num_syms].name == (char *) 0 )
+ return(0);
+
+ sym_array[num_syms].value = address;
+ strcpy(sym_array[num_syms].name, symbol);
+ ++num_syms;
+ return(1);
+}
+
+
+/**************************************************************************
+ * Function: LookupSymbol
+ *
+ * Purpose: Find the symbol which is related to the given kernel
+ * address.
+ *
+ * Arguements: (long int) value, (struct symbol *) sym
+ *
+ * value:-> The address to be located.
+ *
+ * sym:-> A pointer to a structure which will be
+ * loaded with the symbol's parameters.
+ *
+ * Return: (char *)
+ *
+ * If a match cannot be found a diagnostic string is printed.
+ * If a match is found the pointer to the symbolic name most
+ * closely matching the address is returned.
+ **************************************************************************/
+char * LookupSymbol(unsigned long value, struct symbol *sym)
+{
+ auto int lp;
+
+ auto char *last;
+ auto char *name;
+
+ struct symbol ksym, msym;
+
+ if (!sym_array)
+ return((char *) 0);
+
+ last = sym_array[0].name;
+ ksym.offset = 0;
+ ksym.size = 0;
+ if ( value < sym_array[0].value )
+ return((char *) 0);
+
+ for(lp = 0; lp <= num_syms; ++lp) {
+ if ( sym_array[lp].value > value ) {
+ ksym.offset = value - sym_array[lp-1].value;
+ ksym.size = sym_array[lp].value - \
+ sym_array[lp-1].value;
+ break;
+ }
+ last = sym_array[lp].name;
+ }
+
+ name = LookupModuleSymbol(value, &msym);
+
+ if ( ksym.offset == 0 && msym.offset == 0 ) {
+ return((char *) 0);
+ }
+
+ if ( ksym.offset == 0 || msym.offset < 0 ||
+ (ksym.offset > 0 && ksym.offset < msym.offset) ) {
+ sym->offset = ksym.offset;
+ sym->size = ksym.size;
+ return(last);
+ } else {
+ sym->offset = msym.offset;
+ sym->size = msym.size;
+ return(name);
+ }
+
+
+ return((char *) 0);
+}
+
+/**************************************************************************
+ * Function: FreeSymbols
+ *
+ * Purpose: This function is responsible for freeing all memory which
+ * has been allocated to hold the static symbol table. It
+ * also initializes the symbol count and in general prepares
+ * for a re-read of a static symbol table.
+ *
+ * Arguements: void
+ *
+ * Return: void
+ **************************************************************************/
+static void FreeSymbols(void)
+{
+ auto int lp;
+
+ /* Free each piece of memory allocated for symbol names. */
+ for(lp= 0; lp < num_syms; ++lp)
+ free(sym_array[lp].name);
+
+ /* Whack the entire array and initialize everything. */
+ free(sym_array);
+ sym_array = (struct sym_table *) 0;
+ num_syms = 0;
+
+ return;
+}
+
+
+/**************************************************************************
+ * Function: LogExpanded
+ *
+ * Purpose: This function is responsible for logging a kernel message
+ * line after all potential numeric kernel addresses have
+ * been resolved symolically.
+ *
+ * Arguements: (char *) line, (char *) el
+ *
+ * line:-> A pointer to the buffer containing the kernel
+ * message to be expanded and logged.
+ *
+ * el:-> A pointer to the buffer into which the expanded
+ * kernel line will be written.
+ *
+ * Return: void
+ **************************************************************************/
+extern char *ExpandKadds(char *line, char *el)
+{
+ auto char dlm,
+ *kp,
+ *sl = line,
+ *elp = el,
+ *symbol;
+ char num[15];
+ auto unsigned long int value;
+ auto struct symbol sym;
+
+ sym.offset = 0;
+ sym.size = 0;
+
+ /*
+ * This is as handy a place to put this as anyplace.
+ *
+ * Since the insertion of kernel modules can occur in a somewhat
+ * dynamic fashion we need some mechanism to insure that the
+ * kernel symbol tables get read just prior to when they are
+ * needed.
+ *
+ * To accomplish this we look for the Oops string and use its
+ * presence as a signal to load the module symbols.
+ *
+ * This is not the best solution of course, especially if the
+ * kernel is rapidly going out to lunch. What really needs to
+ * be done is to somehow generate a callback from the
+ * kernel whenever a module is loaded or unloaded. I am
+ * open for patches.
+ */
+ if ( i_am_paranoid &&
+ (strstr(line, "Oops:") != (char *) 0) && !InitMsyms() )
+ Syslog(LOG_WARNING, "Cannot load kernel module symbols.\n");
+
+
+ /*
+ * Early return if there do not appear to be any kernel
+ * messages in this line.
+ */
+ if ( (num_syms == 0) ||
+ (kp = strstr(line, "[<")) == (char *) 0 ) {
+#ifdef __sparc__
+ if (num_syms) {
+ /* On SPARC, register dumps do not have the [< >] characters in it.
+ */
+ static struct sparc_tests {
+ char *str;
+ int len;
+ } tests[] = { { "PC: ", 4 },
+ { " o7: ", 5 },
+ { " ret_pc: ", 9 },
+ { " i7: ", 5 },
+ { "Caller[", 7 }
+ };
+ int i, j, ndigits;
+ char *kp2;
+ for (i = 0; i < 5; i++) {
+ kp = strstr(line, tests[i].str);
+ if (!kp) continue;
+ kp2 = kp + tests[i].len;
+ if (!isxdigit(*kp2)) continue;
+ for (ndigits = 1; isxdigit(kp2[ndigits]); ndigits++);
+ if (ndigits != 8 && ndigits != 16) continue;
+ /* On sparc64, all kernel addresses are in first 4GB */
+ if (ndigits == 16) {
+ if (strncmp (kp2, "00000000", 8)) continue;
+ kp2 += 8;
+ }
+ if (!i) {
+ char *kp3;
+ if (ndigits == 16 && kp > line && kp[-1L] != 'T') continue;
+ kp3 = kp2 + 8;
+ if (ndigits == 16) {
+ if (strncmp (kp3, " TNPC: 00000000", 15) || !isxdigit(kp3[15]))
+ continue;
+ kp3 += 15;
+ } else {
+ if (strncmp (kp3, " NPC: ", 6) || !isxdigit(kp3[6]))
+ continue;
+ kp3 += 6;
+ }
+ for (j = 0; isxdigit(kp3[j]); j++);
+ if (j != 8) continue;
+ strncpy(elp, line, kp2 + 8 - line);
+ elp += kp2 + 8 - line;
+ value = strtol(kp2, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strncpy(elp, kp2 + 8, kp3 - kp2);
+ elp += kp3 - kp2;
+ value = strtol(kp3, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strcpy(elp, kp3 + 8);
+ } else {
+ strncpy(elp, line, kp2 + 8 - line);
+ elp += kp2 + 8 - line;
+ value = strtol(kp2, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) ) {
+ if (sym.size)
+ elp += sprintf(elp, " (%s+%d/%d)", symbol, sym.offset, sym.size);
+ else
+ elp += sprintf(elp, " (%s)", symbol);
+ }
+ strcpy(elp, kp2 + 8);
+ }
+ return el;
+ }
+ }
+#endif
+ strcpy(el, line);
+ return(el);
+ }
+
+ /* Loop through and expand all kernel messages. */
+ do {
+ while ( sl < kp+1 )
+ *elp++ = *sl++;
+
+ /* Now poised at a kernel delimiter. */
+ if ( (kp = strstr(sl, ">]")) == (char *) 0 ) {
+ strcpy(el, sl);
+ return(el);
+ }
+ dlm = *kp;
+ strncpy(num,sl+1,kp-sl-1);
+ num[kp-sl-1] = '\0';
+ value = strtoul(num, (char **) 0, 16);
+ if ( (symbol = LookupSymbol(value, &sym)) == (char *) 0 )
+ symbol = sl;
+
+ strcat(elp, symbol);
+ elp += strlen(symbol);
+ dbgprintf("Symbol: %s = %lx = %s, %x/%d\n", sl+1, value,
+ (sym.size==0) ? symbol+1 : symbol, sym.offset, sym.size);
+
+ value = 2;
+ if ( sym.size != 0 ) {
+ --value;
+ ++kp;
+ elp += sprintf(elp, "+0x%x/0x%02x", sym.offset, sym.size);
+ }
+ strncat(elp, kp, value);
+ elp += value;
+ sl = kp + value;
+ if ( (kp = strstr(sl, "[<")) == (char *) 0 )
+ strcat(elp, sl);
+ }
+ while ( kp != (char *) 0);
+
+ dbgprintf("Expanded line: %s\n", el);
+ return(el);
+}
+
+
+/**************************************************************************
+ * Function: SetParanoiaLevel
+ *
+ * Purpose: This function is an interface function for setting the
+ * mode of loadable module symbol lookups. Probably overkill
+ * but it does slay another global variable.
+ *
+ * Arguements: (int) level
+ *
+ * level:-> The amount of paranoia which is to be
+ * present when resolving kernel exceptions.
+ * Return: void
+ **************************************************************************/
+extern void SetParanoiaLevel(int level)
+{
+ i_am_paranoid = level;
+ return;
+}
+
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
new file mode 100644
index 00000000..3e064e20
--- /dev/null
+++ b/plugins/imklog/ksym_mod.c
@@ -0,0 +1,482 @@
+/*
+ * ksym_mod.c - functions for building symbol lookup tables for klogd
+ * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ * Copyright (c) 1996 Enjellic Systems Development
+ * Copyright (c) 1998-2007 Martin Schulze <joey@infodrom.org>
+ * Copyright (C) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+*/
+
+/*
+ * This file implements functions which are useful for building
+ * a symbol lookup table based on the in kernel symbol table
+ * maintained by the Linux kernel.
+ *
+ * Proper logging of kernel panics generated by loadable modules
+ * tends to be difficult. Since the modules are loaded dynamically
+ * their addresses are not known at kernel load time. A general
+ * protection fault (Oops) cannot be properly deciphered with
+ * classic methods using the static symbol map produced at link time.
+ *
+ * One solution to this problem is to have klogd attempt to translate
+ * addresses from module when the fault occurs. By referencing the
+ * the kernel symbol table proper resolution of these symbols is made
+ * possible.
+ *
+ * At least that is the plan.
+ *
+ * Wed Aug 21 09:20:09 CDT 1996: Dr. Wettstein
+ * The situation where no module support has been compiled into a
+ * kernel is now detected. An informative message is output indicating
+ * that the kernel has no loadable module support whenever kernel
+ * module symbols are loaded.
+ *
+ * An informative message is printed indicating the number of kernel
+ * modules and the number of symbols loaded from these modules.
+ *
+ * Sun Jun 15 16:23:29 MET DST 1997: Michael Alan Dorman
+ * Some more glibc patches made by <mdorman@debian.org>.
+ *
+ * Sat Jan 10 15:00:18 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Fixed problem with klogd not being able to be built on a kernel
+ * newer than 2.1.18. It was caused by modified structures
+ * inside the kernel that were included. I have worked in a
+ * patch from Alessandro Suardi <asuardi@uninetcom.it>.
+ *
+ * Sun Jan 25 20:57:34 CET 1998: Martin Schulze <joey@infodrom.north.de>
+ * Another patch for Linux/alpha by Christopher C Chimelis
+ * <chris@classnet.med.miami.edu>.
+ *
+ * Thu Mar 19 23:39:29 CET 1998: Manuel Rodrigues <pmanuel@cindy.fe.up.pt>
+ * Changed lseek() to llseek() in order to support > 2GB address
+ * space which provided by kernels > 2.1.70.
+ *
+ * Mon Apr 13 18:18:45 CEST 1998: Martin Schulze <joey@infodrom.north.de>
+ * Removed <sys/module.h> as it's no longer part of recent glibc
+ * versions. Added prototyp for llseek() which has been
+ * forgotton in <unistd.h> from glibc. Added more log
+ * information if problems occurred while reading a system map
+ * file, by submission from Mark Simon Phillips <M.S.Phillips@nortel.co.uk>.
+ *
+ * Sun Jan 3 18:38:03 CET 1999: Martin Schulze <joey@infodrom.north.de>
+ * Corrected return value of AddModule if /dev/kmem can't be
+ * loaded. This will prevent klogd from segfaulting if /dev/kmem
+ * is not available. Patch from Topi Miettinen <tom@medialab.sonera.net>.
+ *
+ * Tue Sep 12 23:11:13 CEST 2000: Martin Schulze <joey@infodrom.ffis.de>
+ * Changed llseek() to lseek64() in order to skip a libc warning.
+ */
+
+
+/* Includes. */
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#if !defined(__GLIBC__)
+#include <linux/time.h>
+#include <linux/module.h>
+#else /* __GLIBC__ */
+#include "module.h"
+#endif /* __GLIBC__ */
+#include <stdarg.h>
+#include <paths.h>
+#include <linux/version.h>
+
+#include "rsyslog.h"
+#include "imklog.h"
+#include "ksyms.h"
+
+#define KSYMS "/proc/kallsyms"
+
+static int num_modules = 0;
+struct Module *sym_array_modules = (struct Module *) 0;
+
+static int have_modules = 0;
+
+
+/* Function prototypes. */
+static void FreeModules(void);
+static int AddSymbol(const char *);
+struct Module *AddModule(const char *);
+static int symsort(const void *, const void *);
+
+/* Imported from ksym.c */
+extern int num_syms;
+
+
+/**************************************************************************
+ * Function: InitMsyms
+ *
+ * Purpose: This function is responsible for building a symbol
+ * table which can be used to resolve addresses for
+ * loadable modules.
+ *
+ * Arguements: Void
+ *
+ * Return: A boolean return value is assumed.
+ *
+ * A false value indicates that something went wrong.
+ *
+ * True if loading is successful.
+ **************************************************************************/
+extern int InitMsyms(void)
+{
+
+ auto int rtn,
+ tmp;
+ FILE *ksyms;
+ char buf[128];
+ char *p;
+
+ /* Initialize the kernel module symbol table. */
+ FreeModules();
+
+ ksyms = fopen(KSYMS, "r");
+
+ if ( ksyms == NULL ) {
+ if ( errno == ENOENT )
+ Syslog(LOG_INFO, "No module symbols loaded - "
+ "kernel modules not enabled.\n");
+ else
+ Syslog(LOG_ERR, "Error loading kernel symbols " \
+ "- %s\n", strerror(errno));
+ return(0);
+ }
+
+ dbgprintf("Loading kernel module symbols - Source: %s\n", KSYMS);
+
+ while ( fgets(buf, sizeof(buf), ksyms) != NULL ) {
+ if (num_syms > 0 && index(buf, '[') == NULL)
+ continue;
+
+ p = index(buf, ' ');
+
+ if ( p == NULL )
+ continue;
+
+ if ( buf[strlen(buf)-1] == '\n' )
+ buf[strlen(buf)-1] = '\0';
+ /* overlong lines will be ignored above */
+
+ AddSymbol(buf);
+ }
+
+ if(ksyms != NULL)
+ fclose(ksyms);
+
+ have_modules = 1;
+
+ /* Sort the symbol tables in each module. */
+ for (rtn = tmp = 0; tmp < num_modules; ++tmp) {
+ rtn += sym_array_modules[tmp].num_syms;
+ if ( sym_array_modules[tmp].num_syms < 2 )
+ continue;
+ qsort(sym_array_modules[tmp].sym_array, \
+ sym_array_modules[tmp].num_syms, \
+ sizeof(struct sym_table), symsort);
+ }
+
+ if ( rtn == 0 )
+ Syslog(LOG_INFO, "No module symbols loaded.");
+ else
+ Syslog(LOG_INFO, "Loaded %d %s from %d module%s", rtn, \
+ (rtn == 1) ? "symbol" : "symbols", \
+ num_modules, (num_modules == 1) ? "." : "s.");
+
+ return(1);
+}
+
+
+static int symsort(const void *p1, const void *p2)
+{
+ auto const struct sym_table *sym1 = p1,
+ *sym2 = p2;
+
+ if ( sym1->value < sym2->value )
+ return(-1);
+ if ( sym1->value == sym2->value )
+ return(0);
+ return(1);
+}
+
+
+extern void DeinitMsyms(void)
+{
+ FreeModules();
+}
+
+
+/**************************************************************************
+ * Function: FreeModules
+ *
+ * Purpose: This function is used to free all memory which has been
+ * allocated for the modules and their symbols.
+ *
+ * Arguements: None specified.
+ *
+ * Return: void
+ **************************************************************************/
+static void FreeModules()
+{
+ auto int nmods,
+ nsyms;
+ auto struct Module *mp;
+
+ /* Check to see if the module symbol tables need to be cleared. */
+ have_modules = 0;
+ if ( num_modules == 0 )
+ return;
+
+ if ( sym_array_modules == NULL )
+ return;
+
+ for (nmods = 0; nmods < num_modules; ++nmods) {
+ mp = &sym_array_modules[nmods];
+ if ( mp->num_syms == 0 )
+ continue;
+
+ for (nsyms= 0; nsyms < mp->num_syms; ++nsyms)
+ free(mp->sym_array[nsyms].name);
+ free(mp->sym_array);
+ if ( mp->name != NULL )
+ free(mp->name);
+ }
+
+ free(sym_array_modules);
+ sym_array_modules = (struct Module *) 0;
+ num_modules = 0;
+ return;
+}
+
+
+/**************************************************************************
+ * Function: AddModule
+ *
+ * Purpose: This function is responsible for adding a module to
+ * the list of currently loaded modules.
+ *
+ * Arguments: (const char *) module
+ *
+ * module:-> The name of the module.
+ *
+ * Return: struct Module *
+ **************************************************************************/
+
+struct Module *AddModule(module)
+ const char *module;
+{
+ struct Module *mp;
+
+ if ( num_modules == 0 ) {
+ sym_array_modules = (struct Module *)malloc(sizeof(struct Module));
+
+ if ( sym_array_modules == NULL )
+ {
+ Syslog(LOG_WARNING, "Cannot allocate Module array.\n");
+ return NULL;
+ }
+ mp = sym_array_modules;
+ } else {
+ /* Allocate space for the module. */
+ mp = (struct Module *) \
+ realloc(sym_array_modules, \
+ (num_modules+1) * sizeof(struct Module));
+
+ if ( mp == NULL )
+ {
+ Syslog(LOG_WARNING, "Cannot allocate Module array.\n");
+ return NULL;
+ }
+
+ sym_array_modules = mp;
+ mp = &sym_array_modules[num_modules];
+ }
+
+ num_modules++;
+ mp->sym_array = NULL;
+ mp->num_syms = 0;
+
+ if ( module != NULL )
+ mp->name = strdup(module);
+ else
+ mp->name = NULL;
+
+ return mp;
+}
+
+
+/**************************************************************************
+ * Function: AddSymbol
+ *
+ * Purpose: This function is responsible for adding a symbol name
+ * and its address to the symbol table.
+ *
+ * Arguements: const char *
+ *
+ * Return: int
+ *
+ * A boolean value is assumed. True if the addition is
+ * successful. False if not.
+ **************************************************************************/
+static int AddSymbol(line)
+ const char *line;
+{
+ char *module;
+ unsigned long address;
+ char *p;
+ static char *lastmodule = NULL;
+ struct Module *mp;
+
+ module = index(line, '[');
+
+ if ( module != NULL ) {
+ p = index(module, ']');
+ if ( p != NULL )
+ *p = '\0';
+ p = module++;
+ while ( isspace(*(--p)) )
+ /*SKIP*/;
+ *(++p) = '\0';
+ }
+
+ p = index(line, ' ');
+
+ if ( p == NULL )
+ return(0);
+
+ *p = '\0';
+
+ address = strtoul(line, (char **) 0, 16);
+
+ p += 3;
+
+ if ( num_modules == 0 ||
+ ( lastmodule == NULL && module != NULL ) ||
+ ( module == NULL && lastmodule != NULL) ||
+ ( module != NULL && strcmp(module, lastmodule))) {
+ mp = AddModule(module);
+
+ if ( mp == NULL )
+ return(0);
+ } else
+ mp = &sym_array_modules[num_modules-1];
+
+ lastmodule = mp->name;
+
+ /* Allocate space for the symbol table entry. */
+ mp->sym_array = (struct sym_table *) realloc(mp->sym_array, \
+ (mp->num_syms+1) * sizeof(struct sym_table));
+
+ if ( mp->sym_array == (struct sym_table *) 0 )
+ return(0);
+
+ mp->sym_array[mp->num_syms].name = strdup(p);
+ if ( mp->sym_array[mp->num_syms].name == (char *) 0 )
+ return(0);
+
+ /* Stuff interesting information into the module. */
+ mp->sym_array[mp->num_syms].value = address;
+ ++mp->num_syms;
+
+ return(1);
+}
+
+
+
+/**************************************************************************
+ * Function: LookupModuleSymbol
+ *
+ * Purpose: Find the symbol which is related to the given address from
+ * a kernel module.
+ *
+ * Arguements: (long int) value, (struct symbol *) sym
+ *
+ * value:-> The address to be located.
+ *
+ * sym:-> A pointer to a structure which will be
+ * loaded with the symbol's parameters.
+ *
+ * Return: (char *)
+ *
+ * If a match cannot be found a diagnostic string is printed.
+ * If a match is found the pointer to the symbolic name most
+ * closely matching the address is returned.
+ **************************************************************************/
+extern char * LookupModuleSymbol(value, sym)
+ unsigned long value;
+ struct symbol *sym;
+{
+ auto int nmod,
+ nsym;
+ auto struct sym_table *last;
+ auto struct Module *mp;
+ static char ret[100];
+
+ sym->size = 0;
+ sym->offset = 0;
+ if ( num_modules == 0 )
+ return((char *) 0);
+
+ for (nmod = 0; nmod < num_modules; ++nmod) {
+ mp = &sym_array_modules[nmod];
+
+ /*
+ * Run through the list of symbols in this module and
+ * see if the address can be resolved.
+ */
+ for(nsym = 1, last = &mp->sym_array[0];
+ nsym < mp->num_syms;
+ ++nsym) {
+ if ( mp->sym_array[nsym].value > value )
+ {
+ if ( sym->size == 0 ||
+ (value - last->value) < sym->offset ||
+ ( (sym->offset == (value - last->value)) &&
+ (mp->sym_array[nsym].value-last->value) < sym->size ) )
+ {
+ sym->offset = value - last->value;
+ sym->size = mp->sym_array[nsym].value - \
+ last->value;
+ ret[sizeof(ret)-1] = '\0';
+ if ( mp->name == NULL )
+ snprintf(ret, sizeof(ret)-1,
+ "%s", last->name);
+ else
+ snprintf(ret, sizeof(ret)-1,
+ "%s:%s", mp->name, last->name);
+ }
+ break;
+ }
+ last = &mp->sym_array[nsym];
+ }
+ }
+
+ if ( sym->size > 0 )
+ return(ret);
+
+ /* It has been a hopeless exercise. */
+ return((char *) 0);
+}
diff --git a/plugins/imklog/ksyms.h b/plugins/imklog/ksyms.h
new file mode 100644
index 00000000..b5362ff3
--- /dev/null
+++ b/plugins/imklog/ksyms.h
@@ -0,0 +1,37 @@
+/* ksym.h - Definitions for symbol table utilities.
+ * Copyright (c) 1995, 1996 Dr. G.W. Wettstein <greg@wind.rmcc.com>
+ * Copyright (c) 1996 Enjellic Systems Development
+ * Copyright (c) 2004-7 Martin Schulze <joey@infodrom.org>
+ * Copyright (c) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+/* Variables, structures and type definitions static to this module. */
+
+struct symbol
+{
+ char *name;
+ int size;
+ int offset;
+};
+
+
+/* Function prototypes. */
+extern char * LookupSymbol(unsigned long, struct symbol *);
+extern char * LookupModuleSymbol(unsigned long int, struct symbol *);
diff --git a/plugins/imklog/module.h b/plugins/imklog/module.h
new file mode 100644
index 00000000..38a26fea
--- /dev/null
+++ b/plugins/imklog/module.h
@@ -0,0 +1,35 @@
+/* module.h - Miscellaneous module definitions
+ * Copyright (c) 1996 Richard Henderson <rth@tamu.edu>
+ * Copyright (c) 2004-7 Martin Schulze <joey@infodrom.org>
+ * Copyright (c) 2007-2008 Rainer Gerhards <rgerhards@adiscon.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+struct sym_table
+{
+ unsigned long value;
+ char *name;
+};
+
+struct Module
+{
+ struct sym_table *sym_array;
+ int num_syms;
+
+ char *name;
+};
diff --git a/plugins/immark/.cvsignore b/plugins/immark/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/immark/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/immark/Makefile.am b/plugins/immark/Makefile.am
new file mode 100644
index 00000000..3dc0e408
--- /dev/null
+++ b/plugins/immark/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = immark.la
+
+immark_la_SOURCES = immark.c immark.h
+immark_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+immark_la_LDFLAGS = -module -avoid-version
+immark_la_LIBADD =
diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c
new file mode 100644
index 00000000..30118de0
--- /dev/null
+++ b/plugins/immark/immark.c
@@ -0,0 +1,123 @@
+/* immark.c
+ * This is the implementation of the build-in mark message input module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <pthread.h>
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "module-template.h"
+
+MODULE_TYPE_INPUT
+
+/* defines */
+#define DEFAULT_MARK_PERIOD (20 * 60)
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+static int iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
+
+/* This function is called to gather input. It must terminate only
+ * a) on failure (iRet set accordingly)
+ * b) on termination of the input module (as part of the unload process)
+ * Code begun 2007-12-12 rgerhards
+ *
+ * This code must simply spawn emit a mark message at each mark interval.
+ * We are running on our own thread, so this is extremely easy: we just
+ * sleep MarkInterval seconds and each time we awake, we inject the message.
+ * Please note that we do not do the other fancy things that sysklogd
+ * (and pre 1.20.2 releases of rsyslog) did in mark procesing. They simply
+ * do not belong here.
+ */
+BEGINrunInput
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(1) {
+ /* we do not need to handle the RS_RET_TERMINATE_NOW case any
+ * special because we just need to terminate. This may be different
+ * if a cleanup is needed. But for now, we can just use CHKiRet().
+ * rgerhards, 2007-12-17
+ */
+ CHKiRet(thrdSleep(pThrd, iMarkMessagePeriod, 0)); /* seconds, micro seconds */
+ logmsgInternal(LOG_INFO, "-- MARK --", ADDDATE|MARK);
+ }
+finalize_it:
+ return iRet;
+ENDrunInput
+
+
+BEGINwillRun
+CODESTARTwillRun
+ if(iMarkMessagePeriod == 0)
+ iRet = RS_RET_NO_RUN;
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
+
+ return RS_RET_OK;
+}
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL, &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * vi:set ai:
+ */
diff --git a/plugins/immark/immark.h b/plugins/immark/immark.h
new file mode 100644
index 00000000..db98978b
--- /dev/null
+++ b/plugins/immark/immark.h
@@ -0,0 +1,35 @@
+/* immark.h
+ * These are the definitions for the built-in mark message generation module. This
+ * file may disappear when this has become a loadable module.
+ *
+ * File begun on 2007-12-12 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef IMMARK_H_INCLUDED
+#define IMMARK_H_INCLUDED 1
+
+/* prototypes */
+rsRetVal immark_runInput(void);
+
+#endif /* #ifndef IMMARK_H_INCLUDED */
+/*
+ * vi:set ai:
+ */
diff --git a/plugins/imrelp/.cvsignore b/plugins/imrelp/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imrelp/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imrelp/Makefile.am b/plugins/imrelp/Makefile.am
new file mode 100644
index 00000000..53c9322c
--- /dev/null
+++ b/plugins/imrelp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imrelp.la
+
+imrelp_la_SOURCES = imrelp.c
+imrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS)
+imrelp_la_LDFLAGS = -module -avoid-version
+imrelp_la_LIBADD = $(RELP_LIBS)
diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c
new file mode 100644
index 00000000..b7308016
--- /dev/null
+++ b/plugins/imrelp/imrelp.c
@@ -0,0 +1,190 @@
+/* imrelp.c
+ *
+ * This is the implementation of the RELP input module.
+ *
+ * File begun on 2008-03-13 by RGerhards
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <librelp.h>
+#include "rsyslog.h"
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+
+MODULE_TYPE_INPUT
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(net)
+
+/* Module static data */
+static relpEngine_t *pRelpEngine; /* our relp engine */
+
+
+/* config settings */
+static int iTCPSessMax = 200; /* max number of sessions */
+
+
+/* ------------------------------ callbacks ------------------------------ */
+#if 0
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
+ void __attribute__((unused)) *pUsrSess)
+{
+ return net.isAllowedSender(net.pAllowedSenders_TCP, addr, fromHostFQDN);
+}
+
+#endif // #if 0
+
+/* 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
+ */
+static relpRetVal
+onSyslogRcv(uchar *pHostname, uchar __attribute__((unused)) *pIP, uchar *pMsg, size_t lenMsg)
+{
+ DEFiRet;
+ parseAndSubmitMessage((char*)pHostname, (char*)pMsg, lenMsg, MSG_PARSE_HOSTNAME,
+ NOFLAG, eFLOWCTL_LIGHT_DELAY);
+
+ RETiRet;
+}
+
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+
+static rsRetVal addListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ if(pRelpEngine == NULL) {
+ CHKiRet(relpEngineConstruct(&pRelpEngine));
+ CHKiRet(relpEngineSetDbgprint(pRelpEngine, dbgprintf));
+ CHKiRet(relpEngineSetEnableCmd(pRelpEngine, (uchar*) "syslog", eRelpCmdState_Required));
+ CHKiRet(relpEngineSetSyslogRcv(pRelpEngine, onSyslogRcv));
+ }
+
+ CHKiRet(relpEngineAddListner(pRelpEngine, pNewVal));
+
+ free(pNewVal); /* we do no longer need it */
+
+finalize_it:
+ RETiRet;
+}
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+CODESTARTrunInput
+ /* TODO: we must be careful to start the listener here. Currently, tcpsrv.c seems to
+ * do that in ConstructFinalize
+ */
+ iRet = relpEngineRun(pRelpEngine);
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* first apply some config settings */
+ //net.PrintAllowedSenders(2); /* TCP */
+ if(pRelpEngine == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+finalize_it:
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+#if 0
+ if(net.pAllowedSenders_TCP != NULL) {
+ net.clearAllowedSenders(net.pAllowedSenders_TCP);
+ net.pAllowedSenders_TCP = NULL;
+ }
+#endif
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ if(pRelpEngine != NULL)
+ iRet = relpEngineDestruct(&pRelpEngine);
+
+ /* release objects we used */
+ objRelease(net, LM_NET_FILENAME);
+ENDmodExit
+
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ iTCPSessMax = 200;
+ return RS_RET_OK;
+}
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_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(net, LM_NET_FILENAME));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord,
+ addListener, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpmaxsessions", 0, eCmdHdlrInt,
+ NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+/* vim:set ai:
+ */
diff --git a/plugins/imtcp/.cvsignore b/plugins/imtcp/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imtcp/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imtcp/Makefile.am b/plugins/imtcp/Makefile.am
new file mode 100644
index 00000000..fe43cd98
--- /dev/null
+++ b/plugins/imtcp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imtcp.la
+
+imtcp_la_SOURCES = imtcp.c
+imtcp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imtcp_la_LDFLAGS = -module -avoid-version
+imtcp_la_LIBADD =
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
new file mode 100644
index 00000000..7baa95f2
--- /dev/null
+++ b/plugins/imtcp/imtcp.c
@@ -0,0 +1,216 @@
+/* imtcp.c
+ * This is the implementation of the TCP input module.
+ *
+ * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "net.h"
+#include "tcpsrv.h"
+
+MODULE_TYPE_INPUT
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(tcpsrv)
+DEFobjCurrIf(tcps_sess)
+DEFobjCurrIf(net)
+
+/* Module static data */
+static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */
+
+/* config settings */
+static int iTCPSessMax = 200; /* max number of sessions */
+
+
+/* callbacks */
+/* this shall go into a specific ACL module! */
+static int
+isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
+ void __attribute__((unused)) *pUsrSess)
+{
+ return net.isAllowedSender(net.pAllowedSenders_TCP, addr, fromHostFQDN);
+}
+
+
+static int*
+doOpenLstnSocks(tcpsrv_t *pSrv)
+{
+ ISOBJ_TYPE_assert(pSrv, tcpsrv);
+ return tcpsrv.create_tcp_socket(pSrv);
+}
+
+
+static int
+doRcvData(tcps_sess_t *pSess, char *buf, size_t lenBuf)
+{
+ int state;
+ assert(pSess != NULL);
+
+ state = recv(pSess->sock, buf, lenBuf, 0);
+ return state;
+}
+
+static rsRetVal
+onRegularClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ /* process any incomplete frames left over */
+ tcps_sess.PrepareClose(pSess);
+ /* Session closed */
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
+
+
+static rsRetVal
+onErrClose(tcps_sess_t *pSess)
+{
+ DEFiRet;
+ assert(pSess != NULL);
+
+ tcps_sess.Close(pSess);
+ RETiRet;
+}
+
+/* ------------------------------ end callbacks ------------------------------ */
+
+
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ if(pOurTcpsrv == NULL) {
+ CHKiRet(tcpsrv.Construct(&pOurTcpsrv));
+ CHKiRet(tcpsrv.SetCBIsPermittedHost(pOurTcpsrv, isPermittedHost));
+ CHKiRet(tcpsrv.SetCBRcvData(pOurTcpsrv, doRcvData));
+ CHKiRet(tcpsrv.SetCBOpenLstnSocks(pOurTcpsrv, doOpenLstnSocks));
+ CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
+ CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
+ tcpsrv.configureTCPListen(pOurTcpsrv, (char *) pNewVal);
+ CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+CODESTARTrunInput
+ /* TODO: we must be careful to start the listener here. Currently, tcpsrv.c seems to
+ * do that in ConstructFinalize
+ */
+ iRet = tcpsrv.Run(pOurTcpsrv);
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* first apply some config settings */
+ net.PrintAllowedSenders(2); /* TCP */
+ if(pOurTcpsrv == NULL)
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+finalize_it:
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+ if(net.pAllowedSenders_TCP != NULL) {
+ net.clearAllowedSenders(net.pAllowedSenders_TCP);
+ net.pAllowedSenders_TCP = NULL;
+ }
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ if(pOurTcpsrv != NULL)
+ iRet = tcpsrv.Destruct(&pOurTcpsrv);
+
+ /* release objects we used */
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(tcps_sess, LM_TCPSRV_FILENAME);
+ objRelease(tcpsrv, LM_TCPSRV_FILENAME);
+ENDmodExit
+
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ iTCPSessMax = 200;
+ return RS_RET_OK;
+}
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ pOurTcpsrv = NULL;
+ /* request objects we use */
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(tcps_sess, LM_TCPSRV_FILENAME));
+ CHKiRet(objUse(tcpsrv, LM_TCPSRV_FILENAME));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpserverrun", 0, eCmdHdlrGetWord,
+ addTCPListener, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputtcpmaxsessions", 0, eCmdHdlrInt,
+ NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+/* vim:set ai:
+ */
diff --git a/plugins/imtemplate/Makefile.am b/plugins/imtemplate/Makefile.am
new file mode 100644
index 00000000..a9221817
--- /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)
+imtemplate_la_LDFLAGS = -module -avoid-version
+imtemplate_la_LIBADD =
diff --git a/plugins/imtemplate/imtemplate.c b/plugins/imtemplate/imtemplate.c
new file mode 100644
index 00000000..6d29c4f1
--- /dev/null
+++ b/plugins/imtemplate/imtemplate.c
@@ -0,0 +1,459 @@
+/* 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<yourname>, then replace
+ * all references to imtemplate in Makefile.am to im<yourname>. 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<something>. 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h> /* 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; */
+
+/* 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:
+ *
+ * errmsg.LogError(NO_ERRCODE, format-string, ... params ...);
+ * logs an error message as syslogd, just as printf, e.g.
+ * errmsg.LogError(NO_ERRCODE, "Error %d occured during %s", 1, "test");
+ *
+ * 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...).
+ *
+ * If you received a full syslog message that must be decoded by a message
+ * parser, parseAndSubmitMessage() is the way to go. It's not just a funny name
+ * but also a quite some legacy. Consequently, its interface is, ummm, not
+ * well designed.
+ * parseAndSubmitMessage((char*)fromHost, (char*) pRcvBuf, lenRcvd, bParseHost);
+ * fromHost
+ * is the host that we received the message from (a string)
+ * pRcvBuf
+ * is the received (to-be-decoded) message
+ * lenRcvd
+ * is the length of the received message. Please note that pRcvBuf is
+ * NOT a standard C-string. Most importantly it is NOT expected to be
+ * \0-terminated. Thus the lenght is vitally imporant (if it is wrong,
+ * rsyslogd will probably segfault).
+ * bParseHost
+ * is a boolean (0-no, 1-yes). It tells the parser whether or not
+ * a hostname should be parsed from the message. This is important
+ * for sources that are known not to provide a hostname.
+ * Use define MSG_PARSE_HOSTNAME and MSG_DONT_PARSE_HOSTNAME
+ *
+ * Another, more elaborate, way is to create the message object ourselves and
+ * pass it to the rule engine. That way is more appropriate if the message
+ * does not need to be parsed, for example when reading text (log) files. In that way,
+ * we can set the message properties as of our liking. This is how it works:
+ *
+ msg_t *pMsg;
+ CHKiRet(msgConstruct(&pMsg));
+ MsgSetUxTradMsg(pMsg, msg);
+ MsgSetRawMsg(pMsg, msg);
+ MsgSetHOSTNAME(pMsg, LocalHostName);
+ MsgSetTAG(pMsg, "rsyslogd:");
+ pMsg->iFacility = LOG_FAC(pri);
+ pMsg->iSeverity = LOG_PRI(pri);
+ pMsg->bParseHOSTNAME = 0;
+ getCurrTime(&(pMsg->tTIMESTAMP)); / * use the current time! * /
+ flags |= INTERNAL_MSG;
+ logmsg(pMsg, flags); / * some time, CHKiRet() will work here, too [today NOT!] * /
+ *
+ * Note that UxTradMsg is a wild construct. For the time being, set it to
+ * the raw message text. I am hard thinking at dropping that beast at all...
+ *
+ * This example probably does not set all message properties (but the ones
+ * that are of practical importance). If you need all, check msg.h. Use
+ * method access functions whereever possible, unfortunately not all structure
+ * members are currently exposed in that clean way - so you sometimes need
+ * to access them directly (it goes without saying that we will fix that
+ * over time ;)).
+ */
+
+ /* ------------------------------------------------------------------------------------------ *
+ * 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. If you do not return anything
+ * specific, RS_RET_OK is automatically returned (as in all functions).
+ */
+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.
+ */
+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
+ *
+ * $Input<moduleobject>ObjObjName
+ *
+ * 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:
+ */
diff --git a/plugins/imudp/.cvsignore b/plugins/imudp/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imudp/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imudp/Makefile.am b/plugins/imudp/Makefile.am
new file mode 100644
index 00000000..53fdad16
--- /dev/null
+++ b/plugins/imudp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imudp.la
+
+imudp_la_SOURCES = imudp.c
+imudp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imudp_la_LDFLAGS = -module -avoid-version
+imudp_la_LIBADD =
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
new file mode 100644
index 00000000..cda794c3
--- /dev/null
+++ b/plugins/imudp/imudp.c
@@ -0,0 +1,296 @@
+/* imudp.c
+ * This is the implementation of the UDP input module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include "rsyslog.h"
+#include "syslogd.h"
+#include "net.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "srUtils.h"
+#include "errmsg.h"
+
+MODULE_TYPE_INPUT
+
+/* defines */
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(net)
+
+static int *udpLstnSocks = NULL; /* Internet datagram sockets, first element is nbr of elements
+ * read-only after init(), but beware of restart! */
+static uchar *pszBindAddr = NULL; /* IP to bind socket to */
+static uchar *pRcvBuf = NULL; /* receive buffer (for a single packet). We use a global and alloc
+ * it so that we can check available memory in willRun() and request
+ * termination if we can not get it. -- rgerhards, 2007-12-27
+ */
+
+/* config settings */
+
+
+/* This function is called when a new listener shall be added. It takes
+ * the configured parameters, tries to bind the socket and, if that
+ * succeeds, adds it to the list of existing listen sockets.
+ * rgerhards, 2007-12-27
+ */
+static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ uchar *bindAddr;
+ int *newSocks;
+ int *tmpSocks;
+ int iSrc, iDst;
+
+ /* check which address to bind to. We could do this more compact, but have not
+ * done so in order to make the code more readable. -- rgerhards, 2007-12-27
+ */
+ if(pszBindAddr == NULL)
+ bindAddr = NULL;
+ else if(pszBindAddr[0] == '*' && pszBindAddr[1] == '\0')
+ bindAddr = NULL;
+ else
+ bindAddr = pszBindAddr;
+
+ dbgprintf("Trying to open syslog UDP ports at %s:%s.\n",
+ (bindAddr == NULL) ? (uchar*)"*" : bindAddr, pNewVal);
+
+ newSocks = net.create_udp_socket(bindAddr, (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal, 1);
+ if(newSocks != NULL) {
+ /* we now need to add the new sockets to the existing set */
+ if(udpLstnSocks == NULL) {
+ /* esay, we can just replace it */
+ udpLstnSocks = newSocks;
+ } else {
+ /* we need to add them */
+ if((tmpSocks = malloc(sizeof(int) * 1 + newSocks[0] + udpLstnSocks[0])) == NULL) {
+ dbgprintf("out of memory trying to allocate udp listen socket array\n");
+ /* in this case, we discard the new sockets but continue with what we
+ * already have
+ */
+ free(newSocks);
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ } else {
+ /* ready to copy */
+ iDst = 1;
+ for(iSrc = 1 ; iSrc <= udpLstnSocks[0] ; ++iSrc)
+ tmpSocks[iDst++] = udpLstnSocks[iSrc];
+ for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc)
+ tmpSocks[iDst++] = newSocks[iSrc];
+ tmpSocks[0] = udpLstnSocks[0] + newSocks[0];
+ free(newSocks);
+ free(udpLstnSocks);
+ udpLstnSocks = tmpSocks;
+ }
+ }
+ }
+
+finalize_it:
+ free(pNewVal); /* in any case, this is no longer needed */
+
+ RETiRet;
+}
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ int maxfds;
+ int nfds;
+ int i;
+ fd_set readfds;
+ struct sockaddr_storage frominet;
+ socklen_t socklen;
+ uchar fromHost[NI_MAXHOST];
+ uchar fromHostFQDN[NI_MAXHOST];
+ ssize_t l;
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(1) {
+ /* Add the Unix Domain Sockets to the list of read
+ * descriptors.
+ * rgerhards 2005-08-01: we must now check if there are
+ * any local sockets to listen to at all. If the -o option
+ * is given without -a, we do not need to listen at all..
+ */
+ maxfds = 0;
+ FD_ZERO (&readfds);
+
+ /* Add the UDP listen sockets to the list of read descriptors.
+ */
+ if(udpLstnSocks != NULL) {
+ for (i = 0; i < *udpLstnSocks; i++) {
+ if (udpLstnSocks[i+1] != -1) {
+ if(Debug)
+ net.debugListenInfo(udpLstnSocks[i+1], "UDP");
+ FD_SET(udpLstnSocks[i+1], &readfds);
+ if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1];
+ }
+ }
+ }
+ if(Debug) {
+ dbgprintf("--------imUDP calling select, active file descriptors (max %d): ", maxfds);
+ for (nfds = 0; nfds <= maxfds; ++nfds)
+ if ( FD_ISSET(nfds, &readfds) )
+ dbgprintf("%d ", nfds);
+ dbgprintf("\n");
+ }
+
+ /* wait for io to become ready */
+ nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
+
+ if(udpLstnSocks != NULL) {
+ for (i = 0; nfds && i < *udpLstnSocks; i++) {
+ if (FD_ISSET(udpLstnSocks[i+1], &readfds)) {
+ socklen = sizeof(frominet);
+ l = recvfrom(udpLstnSocks[i+1], (char*) pRcvBuf, MAXLINE - 1, 0,
+ (struct sockaddr *)&frominet, &socklen);
+ if (l > 0) {
+ if(net.cvthname(&frominet, fromHost, fromHostFQDN) == RS_RET_OK) {
+ dbgprintf("Message from inetd socket: #%d, host: %s\n",
+ udpLstnSocks[i+1], fromHost);
+ /* Here we check if a host is permitted to send us
+ * syslog messages. If it isn't, we do not further
+ * process the message but log a warning (if we are
+ * configured to do this).
+ * rgerhards, 2005-09-26
+ */
+ if(net.isAllowedSender(net.pAllowedSenders_UDP,
+ (struct sockaddr *)&frominet, (char*)fromHostFQDN)) {
+ parseAndSubmitMessage((char*)fromHost, (char*) pRcvBuf, l,
+ MSG_PARSE_HOSTNAME, NOFLAG, eFLOWCTL_NO_DELAY);
+ } else {
+ dbgprintf("%s is not an allowed sender\n", (char*)fromHostFQDN);
+ if(option_DisallowWarning) {
+ errmsg.LogError(NO_ERRCODE, "UDP message from disallowed sender %s discarded",
+ (char*)fromHost);
+ }
+ }
+ }
+ } else if (l < 0 && errno != EINTR && errno != EAGAIN) {
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ dbgprintf("INET socket error: %d = %s.\n", errno, errStr);
+ errmsg.LogError(NO_ERRCODE, "recvfrom inet");
+ /* should be harmless */
+ sleep(1);
+ }
+ --nfds; /* indicate we have processed one */
+ }
+ }
+ }
+ }
+
+ return iRet;
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ net.PrintAllowedSenders(1); /* UDP */
+
+ /* if we could not set up any listners, there is no point in running... */
+ 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
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+ if (net.pAllowedSenders_UDP != NULL) {
+ net.clearAllowedSenders (net.pAllowedSenders_UDP);
+ net.pAllowedSenders_UDP = NULL;
+ }
+ if(udpLstnSocks != NULL)
+ net.closeUDPListenSockets(udpLstnSocks);
+ if(pRcvBuf != NULL)
+ free(pRcvBuf);
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ if(pszBindAddr != NULL) {
+ free(pszBindAddr);
+ pszBindAddr = NULL;
+ }
+ if(udpLstnSocks != NULL) {
+ net.closeUDPListenSockets(udpLstnSocks);
+ udpLstnSocks = NULL;
+ }
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord,
+ addListner, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
+ NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * vi:set ai:
+ */
diff --git a/plugins/imuxsock/.cvsignore b/plugins/imuxsock/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/imuxsock/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/imuxsock/Makefile.am b/plugins/imuxsock/Makefile.am
new file mode 100644
index 00000000..e165bb7d
--- /dev/null
+++ b/plugins/imuxsock/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imuxsock.la
+
+imuxsock_la_SOURCES = imuxsock.c
+imuxsock_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+imuxsock_la_LDFLAGS = -module -avoid-version
+imuxsock_la_LIBADD =
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
new file mode 100644
index 00000000..3cdcbf0a
--- /dev/null
+++ b/plugins/imuxsock/imuxsock.c
@@ -0,0 +1,351 @@
+/* imuxsock.c
+ * This is the implementation of the Unix sockets input module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-12-20 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include "syslogd.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "srUtils.h"
+#include "errmsg.h"
+
+MODULE_TYPE_INPUT
+
+/* defines */
+#define MAXFUNIX 20
+#ifndef _PATH_LOG
+#ifdef BSD
+#define _PATH_LOG "/var/run/log"
+#else
+#define _PATH_LOG "/dev/log"
+#endif
+#endif
+
+
+/* handle some defines missing on more than one platform */
+#ifndef SUN_LEN
+#define SUN_LEN(su) \
+ (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path))
+#endif
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+static int startIndexUxLocalSockets; /* process funix from that index on (used to
+ * suppress local logging. rgerhards 2005-08-01
+ * read-only after startup
+ */
+static int funixParseHost[MAXFUNIX] = { 0, }; /* should parser parse host name? read-only after startup */
+static int funixFlags[MAXFUNIX] = { ADDDATE, }; /* should parser parse host name? read-only after startup */
+static uchar *funixn[MAXFUNIX] = { (uchar*) _PATH_LOG }; /* read-only after startup */
+static int funix[MAXFUNIX] = { -1, }; /* read-only after startup */
+static int nfunix = 1; /* number of Unix sockets open / read-only after startup */
+
+/* config settings */
+static int bOmitLocalLogging = 0;
+static uchar *pLogSockName = NULL;
+static int bIgnoreTimestamp = 1; /* ignore timestamps present in the incoming message? */
+
+
+/* set the timestamp ignore / not ignore option for the system
+ * log socket. This must be done separtely, as it is not added via a command
+ * but present by default. -- rgerhards, 2008-03-06
+ */
+static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal, int iNewVal)
+{
+ DEFiRet;
+ funixFlags[0] = iNewVal ? ADDDATE : NOFLAG;
+ RETiRet;
+}
+
+
+/* add an additional listen socket. Socket names are added
+ * until the array is filled up. It is never reset, only at
+ * module unload.
+ * TODO: we should change the array to a list so that we
+ * can support any number of listen socket names.
+ * rgerhards, 2007-12-20
+ */
+static rsRetVal addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ char errStr[1024];
+
+ if(nfunix < MAXFUNIX) {
+ if(*pNewVal == ':') {
+ funixParseHost[nfunix] = 1;
+ }
+ else {
+ funixParseHost[nfunix] = 0;
+ }
+ funixFlags[nfunix] = bIgnoreTimestamp ? ADDDATE : NOFLAG;
+ funixn[nfunix++] = pNewVal;
+ }
+ else {
+ snprintf(errStr, sizeof(errStr), "rsyslogd: Out of unix socket name descriptors, ignoring %s\n",
+ pNewVal);
+ logmsgInternal(LOG_SYSLOG|LOG_ERR, errStr, ADDDATE);
+ }
+
+ return RS_RET_OK;
+}
+
+/* free the funixn[] socket names - needed as cleanup on several places
+ * note that nfunix is NOT reset! funixn[0] is never freed, as it comes from
+ * the constant memory pool - and if not, it is freeed via some other pointer.
+ */
+static rsRetVal discardFunixn(void)
+{
+ int i;
+
+ for (i = 1; i < nfunix; i++) {
+ if(funixn[i] != NULL) {
+ free(funixn[i]);
+ funixn[i] = NULL;
+ }
+ }
+
+ return RS_RET_OK;
+}
+
+
+static int create_unix_socket(const char *path)
+{
+ struct sockaddr_un sunx;
+ int fd;
+ char line[MAXLINE +1];
+
+ if (path[0] == '\0')
+ return -1;
+
+ unlink(path);
+
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ (void) strncpy(sunx.sun_path, path, sizeof(sunx.sun_path));
+ fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (fd < 0 || bind(fd, (struct sockaddr *) &sunx,
+ SUN_LEN(&sunx)) < 0 ||
+ chmod(path, 0666) < 0) {
+ snprintf(line, sizeof(line), "cannot create %s", path);
+ errmsg.LogError(NO_ERRCODE, "%s", line);
+ dbgprintf("cannot create %s (%d).\n", path, errno);
+ close(fd);
+ return -1;
+ }
+ return fd;
+}
+
+
+/* This function receives data from a socket indicated to be ready
+ * to receive and submits the message received for processing.
+ * rgerhards, 2007-12-20
+ */
+static rsRetVal readSocket(int fd, int bParseHost, int flags)
+{
+ DEFiRet;
+ int iRcvd;
+ char line[MAXLINE +1];
+
+ iRcvd = recv(fd, line, MAXLINE - 1, 0);
+ dbgprintf("Message from UNIX socket: #%d\n", fd);
+ if (iRcvd > 0) {
+ parseAndSubmitMessage(LocalHostName, line, iRcvd, bParseHost, flags, eFLOWCTL_LIGHT_DELAY);
+ } else if (iRcvd < 0 && errno != EINTR) {
+ char errStr[1024];
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ dbgprintf("UNIX socket error: %d = %s.\n", errno, errStr);
+ errmsg.LogError(NO_ERRCODE, "recvfrom UNIX");
+ }
+
+ RETiRet;
+}
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ int maxfds;
+ int nfds;
+ int i;
+ int fd;
+ fd_set readfds;
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(1) {
+ /* Add the Unix Domain Sockets to the list of read
+ * descriptors.
+ * rgerhards 2005-08-01: we must now check if there are
+ * any local sockets to listen to at all. If the -o option
+ * is given without -a, we do not need to listen at all..
+ */
+ maxfds = 0;
+ FD_ZERO (&readfds);
+ /* Copy master connections */
+ for (i = startIndexUxLocalSockets; i < nfunix; i++) {
+ if (funix[i] != -1) {
+ FD_SET(funix[i], &readfds);
+ if (funix[i]>maxfds) maxfds=funix[i];
+ }
+ }
+
+ if(Debug) {
+ dbgprintf("--------imuxsock calling select, active file descriptors (max %d): ", maxfds);
+ for (nfds= 0; nfds <= maxfds; ++nfds)
+ if ( FD_ISSET(nfds, &readfds) )
+ dbgprintf("%d ", nfds);
+ dbgprintf("\n");
+ }
+
+ /* wait for io to become ready */
+ nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
+
+ for (i = 0; i < nfunix && nfds > 0; i++) {
+ if ((fd = funix[i]) != -1 && FD_ISSET(fd, &readfds)) {
+ readSocket(fd, funixParseHost[i], funixFlags[i]);
+ --nfds; /* indicate we have processed one */
+ }
+ }
+ }
+
+ RETiRet;
+ENDrunInput
+
+
+BEGINwillRun
+CODESTARTwillRun
+ register int i;
+
+ /* first apply some config settings */
+ startIndexUxLocalSockets = bOmitLocalLogging ? 1 : 0;
+ if(pLogSockName != NULL)
+ funixn[0] = pLogSockName;
+
+ /* initialize and return if will run or not */
+ for (i = startIndexUxLocalSockets ; i < nfunix ; i++) {
+ if ((funix[i] = create_unix_socket((char*) funixn[i])) != -1)
+ dbgprintf("Opened UNIX socket '%s' (fd %d).\n", funixn[i], funix[i]);
+ }
+
+ RETiRet;
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ int i;
+ /* do cleanup here */
+ /* Close the UNIX sockets. */
+ for (i = 0; i < nfunix; i++)
+ if (funix[i] != -1)
+ close(funix[i]);
+
+ /* Clean-up files. */
+ for (i = 0; i < nfunix; i++)
+ if (funixn[i] && funix[i] != -1)
+ unlink((char*) funixn[i]);
+ /* free no longer needed string */
+ if(pLogSockName != NULL)
+ free(pLogSockName);
+
+ discardFunixn();
+ nfunix = 1;
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ bOmitLocalLogging = 0;
+ if(pLogSockName != NULL) {
+ free(pLogSockName);
+ pLogSockName = NULL;
+ }
+
+ discardFunixn();
+ nfunix = 1;
+ bIgnoreTimestamp = 1;
+
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+ int i;
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ /* initialize funixn[] array */
+ for(i = 1 ; i < MAXFUNIX ; ++i) {
+ funixn[i] = NULL;
+ funix[i] = -1;
+ }
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omitlocallogging", 0, eCmdHdlrBinary,
+ NULL, &bOmitLocalLogging, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketignoremsgtimestamp", 0, eCmdHdlrBinary,
+ NULL, &bIgnoreTimestamp, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord,
+ NULL, &pLogSockName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"addunixlistensocket", 0, eCmdHdlrGetWord,
+ addLstnSocketName, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ /* the following one is a (dirty) trick: the system log socket is not added via
+ * an "addUnixListenSocket" config format. As such, the timestamp can not be modified
+ * via $InputUnixListenSocketIgnoreMsgTimestamp". So we need to add a special directive
+ * for that. We should revisit all of that once we have the new config format...
+ * rgerhards, 2008-03-06
+ */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketignoremsgtimestamp", 0, eCmdHdlrBinary,
+ setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * vi:set ai:
+ */
diff --git a/plugins/omgssapi/Makefile.am b/plugins/omgssapi/Makefile.am
index 9fa6a241..5280a1ce 100644
--- a/plugins/omgssapi/Makefile.am
+++ b/plugins/omgssapi/Makefile.am
@@ -1,6 +1,6 @@
pkglib_LTLIBRARIES = omgssapi.la
-omgssapi_la_SOURCES = omgssapi.c ../../module-template.h
-omgssapi_la_CPPFLAGS = $(pgsql_cflags) -I$(srcdir)/../..
+omgssapi_la_SOURCES = omgssapi.c
+omgssapi_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
omgssapi_la_LDFLAGS = -module -avoid-version
omgssapi_la_LIBADD = $(gss_libs)
diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 97b8bd55..be051776 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -54,25 +54,29 @@
#include "cfsysline.h"
#include "module-template.h"
#include "gss-misc.h"
+#include "tcpclt.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+
+#define INET_SUSPEND_TIME 60
+/* equal to 1 minute - TODO: see if we can get rid of this now that we have
+ * the retry intervals in the engine -- rgerhards, 2008-03-12
+ */
-#define INET_SUSPEND_TIME 60 /* equal to 1 minute */
- /* rgerhards, 2005-07-26: This was 3 minutes. As the
- * same timer is used for tcp based syslog, we have
- * reduced it. However, it might actually be worth
- * thinking about a buffered tcp sender, which would be
- * a much better alternative. When that happens, this
- * time here can be re-adjusted to 3 minutes (or,
- * even better, made configurable).
- */
#define INET_RETRY_MAX 30 /* maximum of retries for gethostbyname() */
/* was 10, changed to 30 because we reduced INET_SUSPEND_TIME by one third. So
* this "fixes" some of implications of it (see comment on INET_SUSPEND_TIME).
* rgerhards, 2005-07-26
+ * TODO: this needs to be reviewed in spite of the new engine, too -- rgerhards, 2008-03-12
*/
/* internal structures
*/
DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(gssutil)
+DEFobjCurrIf(tcpclt)
typedef struct _instanceData {
char f_hname[MAXHOSTNAMELEN+1];
@@ -86,22 +90,14 @@ typedef struct _instanceData {
struct addrinfo *f_addr;
int compressionLevel; /* 0 - no compression, else level for zlib */
char *port;
- char *savedMsg;
- int savedMsgLen; /* length of savedMsg in octets */
- TCPFRAMINGMODE tcp_framing;
- enum TCPSendStatus {
- TCP_SEND_NOTCONNECTED = 0,
- TCP_SEND_CONNECTING = 1,
- TCP_SEND_READY = 2
- } status;
time_t ttSuspend; /* time selector was suspended */
+ tcpclt_t *pTCPClt; /* our tcpclt object */
gss_ctx_id_t gss_context;
OM_uint32 gss_flags;
-# ifdef USE_PTHREADS
- pthread_mutex_t mtxTCPSend;
-# endif
} instanceData;
+/* config data */
+static uchar *pszTplName = NULL; /* name of the default template to use */
static char *gss_base_service_name = NULL;
static enum gss_mode_t {
GSSMODE_MIC,
@@ -114,7 +110,7 @@ static enum gss_mode_t {
* We may change the implementation to try to lookup the port
* if it is unspecified. So far, we use the IANA default auf 514.
*/
-char *getFwdSyslogPt(instanceData *pData)
+static char *getFwdSyslogPt(instanceData *pData)
{
assert(pData != NULL);
if(pData->port == NULL)
@@ -123,48 +119,6 @@ char *getFwdSyslogPt(instanceData *pData)
return(pData->port);
}
-/* get send status
- * rgerhards, 2005-10-24
- */
-static void TCPSendSetStatus(instanceData *pData, enum TCPSendStatus iNewState)
-{
- assert(pData != NULL);
- assert( (iNewState == TCP_SEND_NOTCONNECTED)
- || (iNewState == TCP_SEND_CONNECTING)
- || (iNewState == TCP_SEND_READY));
-
- /* there can potentially be a race condition, so guard by mutex */
-# ifdef USE_PTHREADS
- pthread_mutex_lock(&pData->mtxTCPSend);
-# endif
- pData->status = iNewState;
-# ifdef USE_PTHREADS
- pthread_mutex_unlock(&pData->mtxTCPSend);
-# endif
-}
-
-
-/* get send status
- * rgerhards, 2005-10-24
- */
-static enum TCPSendStatus TCPSendGetStatus(instanceData *pData)
-{
- enum TCPSendStatus eState;
- assert(pData != NULL);
-
- /* there can potentially be a race condition, so guard by mutex */
-# ifdef USE_PTHREADS
- pthread_mutex_lock(&pData->mtxTCPSend);
-# endif
- eState = pData->status;
-# ifdef USE_PTHREADS
- pthread_mutex_unlock(&pData->mtxTCPSend);
-# endif
-
- return eState;
-}
-
-
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -194,7 +148,7 @@ CODESTARTfreeInstance
if (pData->gss_context != GSS_C_NO_CONTEXT) {
maj_stat = gss_delete_sec_context(&min_stat, &pData->gss_context, GSS_C_NO_BUFFER);
if (maj_stat != GSS_S_COMPLETE)
- display_status("deleting context", maj_stat, min_stat);
+ gssutil.display_status("deleting context", maj_stat, min_stat);
}
/* this is meant to be done when module is unloaded,
but since this module is static...
@@ -204,11 +158,8 @@ CODESTARTfreeInstance
gss_base_service_name = NULL;
}
-# ifdef USE_PTHREADS
- /* delete any mutex objects, if present */
- pthread_mutex_destroy(&pData->mtxTCPSend);
-# endif
/* final cleanup */
+ tcpclt.Destruct(&pData->pTCPClt);
if(pData->sock >= 0)
close(pData->sock);
ENDfreeInstance
@@ -271,7 +222,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
out_tok.length = 0;
if (maj_stat != GSS_S_COMPLETE) {
- display_status("parsing name", maj_stat, min_stat);
+ gssutil.display_status("parsing name", maj_stat, min_stat);
goto fail;
}
@@ -284,7 +235,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
*sess_flags |= GSS_C_CONF_FLAG;
}
dbgprintf("GSS-API requested context flags:\n");
- display_ctx_flags(*sess_flags);
+ gssutil.display_ctx_flags(*sess_flags);
do {
maj_stat = gss_init_sec_context(&init_sec_min_stat, GSS_C_NO_CREDENTIAL, context,
@@ -295,17 +246,17 @@ static rsRetVal TCPSendGSSInit(void *pvData)
if (maj_stat != GSS_S_COMPLETE
&& maj_stat != GSS_S_CONTINUE_NEEDED) {
- display_status("initializing context", maj_stat, init_sec_min_stat);
+ gssutil.display_status("initializing context", maj_stat, init_sec_min_stat);
goto fail;
}
if (s == -1)
- if ((s = pData->sock = TCPSendCreateSocket(pData->f_addr)) == -1)
+ if ((s = pData->sock = tcpclt.CreateSocket(pData->f_addr)) == -1)
goto fail;
if (out_tok.length != 0) {
dbgprintf("GSS-API Sending init_sec_context token (length: %ld)\n", (long) out_tok.length);
- if (send_token(s, &out_tok) < 0) {
+ if (gssutil.send_token(s, &out_tok) < 0) {
goto fail;
}
}
@@ -313,7 +264,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
if (maj_stat == GSS_S_CONTINUE_NEEDED) {
dbgprintf("GSS-API Continue needed...\n");
- if (recv_token(s, &in_tok) <= 0) {
+ if (gssutil.recv_token(s, &in_tok) <= 0) {
goto fail;
}
tok_ptr = &in_tok;
@@ -322,16 +273,16 @@ static rsRetVal TCPSendGSSInit(void *pvData)
dbgprintf("GSS-API Provided context flags:\n");
*sess_flags = ret_flags;
- display_ctx_flags(*sess_flags);
+ gssutil.display_ctx_flags(*sess_flags);
dbgprintf("GSS-API Context initialized\n");
gss_release_name(&min_stat, &target_name);
finalize_it:
- return iRet;
+ RETiRet;
fail:
- logerror("GSS-API Context initialization failed\n");
+ errmsg.LogError(NO_ERRCODE, "GSS-API Context initialization failed\n");
gss_release_name(&min_stat, &target_name);
gss_release_buffer(&min_stat, &out_tok);
if (*context != GSS_C_NO_CONTEXT) {
@@ -341,7 +292,7 @@ finalize_it:
if (s != -1)
close(s);
pData->sock = -1;
- return RS_RET_GSS_SENDINIT_ERROR;
+ ABORT_FINALIZE(RS_RET_GSS_SENDINIT_ERROR);
}
@@ -364,11 +315,11 @@ static rsRetVal TCPSendGSSSend(void *pvData, char *msg, size_t len)
maj_stat = gss_wrap(&min_stat, *context, (gss_mode == GSSMODE_ENC) ? 1 : 0, GSS_C_QOP_DEFAULT,
&in_buf, NULL, &out_buf);
if (maj_stat != GSS_S_COMPLETE) {
- display_status("wrapping message", maj_stat, min_stat);
+ gssutil.display_status("wrapping message", maj_stat, min_stat);
goto fail;
}
- if (send_token(s, &out_buf) < 0) {
+ if (gssutil.send_token(s, &out_buf) < 0) {
goto fail;
}
gss_release_buffer(&min_stat, &out_buf);
@@ -430,7 +381,7 @@ static rsRetVal doTryResume(instanceData *pData)
break;
}
- return iRet;
+ RETiRet;
}
@@ -499,7 +450,7 @@ CODESTARTdoAction
}
# endif
- CHKiRet_Hdlr(TCPSend(pData, psz, l, pData->tcp_framing, TCPSendGSSInit, TCPSendGSSSend, TCPSendGSSPrepRetry)) {
+ CHKiRet_Hdlr(tcpclt.Send(pData->pTCPClt, pData, psz, l)) {
/* error! */
dbgprintf("error forwarding via tcp, suspending\n");
pData->eDestState = eDestFORW_SUSP;
@@ -516,6 +467,7 @@ BEGINparseSelectorAct
int error;
int bErr;
struct addrinfo hints, *res;
+ TCPFRAMINGMODE tcp_framing = TCP_FRAMING_OCTET_STUFFING;
CODESTARTparseSelectorAct
CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* first check if this config line is actually for us
@@ -537,10 +489,6 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
if((iRet = createInstance(&pData)) != RS_RET_OK)
goto finalize_it;
-# ifdef USE_PTHREADS
- pthread_mutex_init(&pData->mtxTCPSend, 0);
-# endif
-
/* we are now after the protocol indicator. Now check if we should
* use compression. We begin to use a new option format for this:
* @(option,option)host:port
@@ -569,20 +517,20 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
++p; /* eat */
pData->compressionLevel = iLevel;
} else {
- logerrorInt("Invalid compression level '%c' specified in "
+ errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in "
"forwardig action - NOT turning on compression.",
*p);
}
# else
- logerror("Compression requested, but rsyslogd is not compiled "
+ errmsg.LogError(NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
"with compression support - request ignored.");
# endif /* #ifdef USE_NETZIP */
} else if(*p == 'o') { /* octet-couting based TCP framing? */
++p; /* eat */
/* no further options settable */
- pData->tcp_framing = TCP_FRAMING_OCTET_COUNTING;
+ tcp_framing = TCP_FRAMING_OCTET_COUNTING;
} else { /* invalid option! Just skip it... */
- logerrorInt("Invalid option %c in forwarding action - ignoring.", *p);
+ errmsg.LogError(NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p);
++p; /* eat invalid option */
}
/* the option processing is done. We now do a generic skip
@@ -598,7 +546,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* we probably have end of string - leave it for the rest
* of the code to handle it (but warn the user)
*/
- logerror("Option block not terminated in gssapi forward action.");
+ errmsg.LogError(NO_ERRCODE, "Option block not terminated in gssapi forward action.");
}
/* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
* now skip to port and then template name. rgerhards 2005-07-06
@@ -616,7 +564,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* SKIP AND COUNT */;
pData->port = malloc(i + 1);
if(pData->port == NULL) {
- logerror("Could not get memory to store syslog forwarding port, "
+ errmsg.LogError(NO_ERRCODE, "Could not get memory to store syslog forwarding port, "
"using default port, results may not be what you intend\n");
/* we leave f_forw.port set to NULL, this is then handled by
* getFwdSyslogPt().
@@ -634,7 +582,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
if(bErr == 0) { /* only 1 error msg! */
bErr = 1;
errno = 0;
- logerror("invalid selector line (port), probably not doing "
+ errmsg.LogError(NO_ERRCODE, "invalid selector line (port), probably not doing "
"what was intended");
}
}
@@ -650,9 +598,8 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
strcpy(pData->f_hname, (char*) q);
/* process template */
- if((iRet = cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) " StdFwdFmt"))
- != RS_RET_OK)
- goto finalize_it;
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
/* first set the pData->eDestState */
memset(&hints, 0, sizeof(hints));
@@ -669,6 +616,14 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
pData->f_addr = res;
}
+ /* now create our tcpclt */
+ CHKiRet(tcpclt.Construct(&pData->pTCPClt));
+ /* and set callbacks */
+ CHKiRet(tcpclt.SetSendInit(pData->pTCPClt, TCPSendGSSInit));
+ CHKiRet(tcpclt.SetSendFrame(pData->pTCPClt, TCPSendGSSSend));
+ CHKiRet(tcpclt.SetSendPrepRetry(pData->pTCPClt, TCPSendGSSPrepRetry));
+ CHKiRet(tcpclt.SetFraming(pData->pTCPClt, tcp_framing));
+
/* TODO: do we need to call freeInstance if we failed - this is a general question for
* all output modules. I'll address it lates as the interface evolves. rgerhards, 2007-07-25
*/
@@ -676,45 +631,16 @@ CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
-BEGINneedUDPSocket
-CODESTARTneedUDPSocket
- iRet = RS_RET_FALSE;
-ENDneedUDPSocket
-
-
-BEGINonSelectReadyWrite
-CODESTARTonSelectReadyWrite
- dbgprintf("tcp send socket %d ready for writing.\n", pData->sock);
- TCPSendSetStatus(pData, TCP_SEND_READY);
- /* Send stored message (if any) */
- if(pData->savedMsg != NULL) {
- if(TCPSend(pData, pData->savedMsg, pData->savedMsgLen, pData->tcp_framing,
- TCPSendGSSInit, TCPSendGSSSend, TCPSendGSSPrepRetry) != RS_RET_OK) {
- /* error! */
- pData->eDestState = eDestFORW_SUSP;
- errno = 0;
- logerror("error forwarding via tcp, suspending...");
- }
- free(pData->savedMsg);
- pData->savedMsg = NULL;
- }
-ENDonSelectReadyWrite
-
-
-BEGINgetWriteFDForSelect
-CODESTARTgetWriteFDForSelect
- if( (pData->eDestState == eDestFORW)
- && TCPSendGetStatus(pData) == TCP_SEND_CONNECTING) {
- *fd = pData->sock;
- iRet = RS_RET_OK;
- }
-ENDgetWriteFDForSelect
-
-
-
-
BEGINmodExit
CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(gssutil, LM_GSSUTIL_FILENAME);
+ objRelease(tcpclt, LM_TCPCLT_FILENAME);
+
+ if(pszTplName != NULL) {
+ free(pszTplName);
+ pszTplName = NULL;
+ }
ENDmodExit
@@ -727,21 +653,21 @@ ENDqueryEtryPt
/* set a new GSSMODE based on config directive */
static rsRetVal setGSSMode(void __attribute__((unused)) *pVal, uchar *mode)
{
+ DEFiRet;
+
if (!strcmp((char *) mode, "integrity")) {
gss_mode = GSSMODE_MIC;
- free(mode);
dbgprintf("GSS-API gssmode set to GSSMODE_MIC\n");
} else if (!strcmp((char *) mode, "encryption")) {
gss_mode = GSSMODE_ENC;
- free(mode);
dbgprintf("GSS-API gssmode set to GSSMODE_ENC\n");
} else {
- logerrorSz("unknown gssmode parameter: %s", (char *) mode);
- free(mode);
- return RS_RET_ERR;
+ errmsg.LogError(NO_ERRCODE, "unknown gssmode parameter: %s", (char *) mode);
+ iRet = RS_RET_INVALID_PARAMS;
}
+ free(mode);
- return RS_RET_OK;
+ RETiRet;
}
@@ -752,16 +678,25 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
free(gss_base_service_name);
gss_base_service_name = NULL;
}
+ if(pszTplName != NULL) {
+ free(pszTplName);
+ pszTplName = NULL;
+ }
return RS_RET_OK;
}
BEGINmodInit()
CODESTARTmodInit
- *ipIFVersProvided = 1; /* so far, we only support the initial definition */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(gssutil, LM_GSSUTIL_FILENAME));
+ CHKiRet(objUse(tcpclt, LM_TCPCLT_FILENAME));
+
CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssforwardservicename", 0, eCmdHdlrGetWord, NULL, &gss_base_service_name, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"gssmode", 0, eCmdHdlrGetWord, setGSSMode, &gss_mode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actiongssforwarddefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
ENDmodInit
diff --git a/plugins/omlibdbi/.cvsignore b/plugins/omlibdbi/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/omlibdbi/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/omlibdbi/Makefile.am b/plugins/omlibdbi/Makefile.am
new file mode 100644
index 00000000..872fc67c
--- /dev/null
+++ b/plugins/omlibdbi/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = omlibdbi.la
+
+omlibdbi_la_SOURCES = omlibdbi.c
+omlibdbi_la_CPPFLAGS = -I$(top_srcdir) $(libdbi_cflags) $(pthreads_cflags)
+omlibdbi_la_LDFLAGS = -module -avoid-version
+omlibdbi_la_LIBADD = $(libdbi_libs)
diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c
new file mode 100644
index 00000000..a942a453
--- /dev/null
+++ b/plugins/omlibdbi/omlibdbi.c
@@ -0,0 +1,372 @@
+/* omlibdbi.c
+ * This is the implementation of the dbi output module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * This depends on libdbi being present with the proper settings. Older
+ * versions do not necessarily have them. Please visit this bug tracker
+ * for details: http://bugzilla.adiscon.com/show_bug.cgi?id=31
+ *
+ * File begun on 2008-02-14 by RGerhards (extracted from syslogd.c)
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <dbi/dbi.h>
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "cfsysline.h"
+#include "srUtils.h"
+#include "template.h"
+#include "module-template.h"
+#include "debug.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+static int bDbiInitialized = 0; /* dbi_initialize() can only be called one - this keeps track of it */
+
+typedef struct _instanceData {
+ dbi_conn conn; /* handle to database */
+ uchar *drvrName; /* driver to use */
+ uchar *host; /* host to connect to */
+ uchar *usrName; /* user name for connect */
+ uchar *pwd; /* password for connect */
+ uchar *dbName; /* database to use */
+ unsigned uLastDBErrno; /* last errno returned by libdbi or 0 if all is well */
+} instanceData;
+
+
+/* config settings */
+static uchar *dbiDrvrDir = NULL;/* global: where do the dbi drivers reside? */
+static uchar *drvrName = NULL; /* driver to use */
+static uchar *host = NULL; /* host to connect to */
+static uchar *usrName = NULL; /* user name for connect */
+static uchar *pwd = NULL; /* password for connect */
+static uchar *dbName = NULL; /* database to use */
+#ifdef HAVE_DBI_R
+static dbi_inst dbiInst;
+#endif
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* we do not like repeated message reduction inside the database */
+ENDisCompatibleWithFeature
+
+
+/* The following function is responsible for closing a
+ * database connection.
+ */
+static void closeConn(instanceData *pData)
+{
+ ASSERT(pData != NULL);
+
+ if(pData->conn != NULL) { /* just to be on the safe side... */
+ dbi_conn_close(pData->conn);
+ pData->conn = NULL;
+ }
+}
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ closeConn(pData);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ /* nothing special here */
+ENDdbgPrintInstInfo
+
+
+/* log a database error with descriptive message.
+ * We check if we have a valid database handle. If not, we simply
+ * report an error, but can not be specific. RGerhards, 2007-01-30
+ */
+static void
+reportDBError(instanceData *pData, int bSilent)
+{
+ unsigned uDBErrno;
+ char errMsg[1024];
+ const char *pszDbiErr;
+
+ BEGINfunc
+ ASSERT(pData != NULL);
+
+ /* output log message */
+ errno = 0;
+ if(pData->conn == NULL) {
+ errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain connection handle");
+ } else { /* we can ask dbi for the error description... */
+ uDBErrno = dbi_conn_error(pData->conn, &pszDbiErr);
+ snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", uDBErrno, pszDbiErr);
+ if(bSilent || uDBErrno == pData->uLastDBErrno)
+ dbgprintf("libdbi, DBError(silent): %s\n", errMsg);
+ else {
+ pData->uLastDBErrno = uDBErrno;
+ errmsg.LogError(NO_ERRCODE, "%s", errMsg);
+ }
+ }
+
+ ENDfunc
+}
+
+
+/* The following function is responsible for initializing a connection
+ */
+static rsRetVal initConn(instanceData *pData, int bSilent)
+{
+ DEFiRet;
+ int iDrvrsLoaded;
+
+ ASSERT(pData != NULL);
+ ASSERT(pData->conn == NULL);
+
+ if(bDbiInitialized == 0) {
+ /* we need to init libdbi first */
+# ifdef HAVE_DBI_R
+ iDrvrsLoaded = dbi_initialize_r((char*) dbiDrvrDir, &dbiInst);
+# else
+ iDrvrsLoaded = dbi_initialize((char*) dbiDrvrDir);
+# endif
+ if(iDrvrsLoaded == 0) {
+ errmsg.LogError(NO_ERRCODE, "libdbi error: libdbi or libdbi drivers not present on this system - suspending.");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ } else if(iDrvrsLoaded < 0) {
+ errmsg.LogError(NO_ERRCODE, "libdbi error: libdbi could not be initialized - suspending.");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ bDbiInitialized = 1; /* we are done for the rest of our existence... */
+ }
+
+# ifdef HAVE_DBI_R
+ pData->conn = dbi_conn_new_r((char*)pData->drvrName, dbiInst);
+# else
+ pData->conn = dbi_conn_new((char*)pData->drvrName);
+# endif
+ if(pData->conn == NULL) {
+ errmsg.LogError(NO_ERRCODE, "can not initialize libdbi connection");
+ iRet = RS_RET_SUSPENDED;
+ } else { /* we could get the handle, now on with work... */
+ /* Connect to database */
+ dbi_conn_set_option(pData->conn, "host", (char*) pData->host);
+ dbi_conn_set_option(pData->conn, "username", (char*) pData->usrName);
+ dbi_conn_set_option(pData->conn, "dbname", (char*) pData->dbName);
+ if(pData->pwd != NULL)
+ dbi_conn_set_option(pData->conn, "password", (char*) pData->pwd);
+ if(dbi_conn_connect(pData->conn) < 0) {
+ reportDBError(pData, bSilent);
+ closeConn(pData); /* ignore any error we may get */
+ iRet = RS_RET_SUSPENDED;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* The following function writes the current log entry
+ * to an established database connection.
+ */
+rsRetVal writeDB(uchar *psz, instanceData *pData)
+{
+ DEFiRet;
+ dbi_result dbiRes = NULL;
+
+ ASSERT(psz != NULL);
+ ASSERT(pData != NULL);
+
+ /* see if we are ready to proceed */
+ if(pData->conn == NULL) {
+ CHKiRet(initConn(pData, 0));
+ }
+
+ /* try insert */
+ if((dbiRes = dbi_conn_query(pData->conn, (const char*)psz)) == NULL) {
+ /* error occured, try to re-init connection and retry */
+ closeConn(pData); /* close the current handle */
+ CHKiRet(initConn(pData, 0)); /* try to re-open */
+ if((dbiRes = dbi_conn_query(pData->conn, (const char*)psz)) == NULL) { /* re-try insert */
+ /* we failed, giving up for now */
+ reportDBError(pData, 0);
+ closeConn(pData); /* free ressources */
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ }
+
+finalize_it:
+ if(iRet == RS_RET_OK) {
+ pData->uLastDBErrno = 0; /* reset error for error supression */
+ }
+
+ if(dbiRes != NULL)
+ dbi_result_free(dbiRes);
+
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ if(pData->conn == NULL) {
+ iRet = initConn(pData, 1);
+ }
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ dbgprintf("\n");
+ iRet = writeDB(ppString[0], pData);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(!strncmp((char*) p, ":omlibdbi:", sizeof(":omlibdbi:") - 1)) {
+ p += sizeof(":omlibdbi:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ CHKiRet(createInstance(&pData));
+
+ /* no create the instance based on what we currently have */
+ if(drvrName == NULL) {
+ errmsg.LogError(NO_ERRCODE, "omlibdbi: no db driver name given - action can not be created");
+ ABORT_FINALIZE(RS_RET_NO_DRIVERNAME);
+ }
+
+ if((pData->drvrName = (uchar*) strdup((char*)drvrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ /* NULL values are supported because drivers have different needs.
+ * They will err out on connect. -- rgerhards, 2008-02-15
+ */
+ if(host != NULL)
+ if((pData->host = (uchar*) strdup((char*)host)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(usrName != NULL)
+ if((pData->usrName = (uchar*) strdup((char*)usrName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(dbName != NULL)
+ if((pData->dbName = (uchar*) strdup((char*)dbName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ if(pwd != NULL)
+ if((pData->pwd = (uchar*) strdup((char*)"")) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt"));
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* if we initialized libdbi, we now need to cleanup */
+ if(bDbiInitialized) {
+# ifdef HAVE_DBI_R
+ dbi_shutdown_r(dbiInst);
+# else
+ dbi_shutdown();
+# endif
+ }
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+/* Reset config variables for this module to default values.
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ DEFiRet;
+
+ if(dbiDrvrDir != NULL) {
+ free(dbiDrvrDir);
+ dbiDrvrDir = NULL;
+ }
+
+ if(drvrName != NULL) {
+ free(drvrName);
+ drvrName = NULL;
+ }
+
+ if(host != NULL) {
+ free(host);
+ host = NULL;
+ }
+
+ if(usrName != NULL) {
+ free(usrName);
+ usrName = NULL;
+ }
+
+ if(pwd != NULL) {
+ free(pwd);
+ pwd = NULL;
+ }
+
+ if(dbName != NULL) {
+ free(dbName);
+ dbName = NULL;
+ }
+
+ RETiRet;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidriverdirectory", 0, eCmdHdlrGetWord, NULL, &dbiDrvrDir, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidriver", 0, eCmdHdlrGetWord, NULL, &drvrName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbihost", 0, eCmdHdlrGetWord, NULL, &host, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbiusername", 0, eCmdHdlrGetWord, NULL, &usrName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbipassword", 0, eCmdHdlrGetWord, NULL, &pwd, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidbname", 0, eCmdHdlrGetWord, NULL, &dbName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/ommysql/Makefile.am b/plugins/ommysql/Makefile.am
index 6397de5b..3b4e6d75 100644
--- a/plugins/ommysql/Makefile.am
+++ b/plugins/ommysql/Makefile.am
@@ -1,7 +1,7 @@
pkglib_LTLIBRARIES = ommysql.la
-ommysql_la_SOURCES = ommysql.c ommysql.h ../../module-template.h
-ommysql_la_CPPFLAGS = $(mysql_cflags) -I$(srcdir)/../.. $(pthreads_cflags)
+ommysql_la_SOURCES = ommysql.c ommysql.h
+ommysql_la_CPPFLAGS = -I$(top_srcdir) $(mysql_cflags) $(pthreads_cflags)
ommysql_la_LDFLAGS = -module -avoid-version
ommysql_la_LIBADD = $(mysql_libs)
diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c
index 02ab68a7..0522e31d 100644
--- a/plugins/ommysql/ommysql.c
+++ b/plugins/ommysql/ommysql.c
@@ -8,19 +8,20 @@
*
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
*
- * This program 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 2
- * of the License, or (at your option) any later version.
+ * This file is part of rsyslog.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
@@ -42,10 +43,14 @@
#include "template.h"
#include "ommysql.h"
#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
/* internal structures
*/
DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
typedef struct _instanceData {
MYSQL *f_hmysql; /* handle to MySQL */
@@ -75,7 +80,7 @@ ENDisCompatibleWithFeature
*/
static void closeMySQL(instanceData *pData)
{
- assert(pData != NULL);
+ ASSERT(pData != NULL);
if(pData->f_hmysql != NULL) { /* just to be on the safe side... */
mysql_server_end();
@@ -90,27 +95,12 @@ CODESTARTfreeInstance
ENDfreeInstance
-BEGINneedUDPSocket
-CODESTARTneedUDPSocket
-ENDneedUDPSocket
-
-
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo
/* nothing special here */
ENDdbgPrintInstInfo
-BEGINonSelectReadyWrite
-CODESTARTonSelectReadyWrite
-ENDonSelectReadyWrite
-
-
-BEGINgetWriteFDForSelect
-CODESTARTgetWriteFDForSelect
-ENDgetWriteFDForSelect
-
-
/* log a database error with descriptive message.
* We check if we have a valid MySQL handle. If not, we simply
* report an error, but can not be specific. RGerhards, 2007-01-30
@@ -120,12 +110,12 @@ static void reportDBError(instanceData *pData, int bSilent)
char errMsg[512];
unsigned uMySQLErrno;
- assert(pData != NULL);
+ ASSERT(pData != NULL);
/* output log message */
errno = 0;
if(pData->f_hmysql == NULL) {
- logerror("unknown DB error occured - could not obtain MySQL handle");
+ errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain MySQL handle");
} else { /* we can ask mysql for the error description... */
uMySQLErrno = mysql_errno(pData->f_hmysql);
snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", uMySQLErrno,
@@ -134,7 +124,7 @@ static void reportDBError(instanceData *pData, int bSilent)
dbgprintf("mysql, DBError(silent): %s\n", errMsg);
else {
pData->uLastMySQLErrno = uMySQLErrno;
- logerror(errMsg);
+ errmsg.LogError(NO_ERRCODE, "%s", errMsg);
}
}
@@ -150,12 +140,12 @@ static rsRetVal initMySQL(instanceData *pData, int bSilent)
{
DEFiRet;
- assert(pData != NULL);
- assert(pData->f_hmysql == NULL);
+ ASSERT(pData != NULL);
+ ASSERT(pData->f_hmysql == NULL);
pData->f_hmysql = mysql_init(NULL);
if(pData->f_hmysql == NULL) {
- logerror("can not initialize MySQL handle");
+ errmsg.LogError(NO_ERRCODE, "can not initialize MySQL handle");
iRet = RS_RET_SUSPENDED;
} else { /* we could get the handle, now on with work... */
/* Connect to database */
@@ -167,7 +157,7 @@ static rsRetVal initMySQL(instanceData *pData, int bSilent)
}
}
- return iRet;
+ RETiRet;
}
@@ -179,8 +169,13 @@ rsRetVal writeMySQL(uchar *psz, instanceData *pData)
{
DEFiRet;
- assert(psz != NULL);
- assert(pData != NULL);
+ ASSERT(psz != NULL);
+ ASSERT(pData != NULL);
+
+ /* see if we are ready to proceed */
+ if(pData->f_hmysql == NULL) {
+ CHKiRet(initMySQL(pData, 0));
+ }
/* try insert */
if(mysql_query(pData->f_hmysql, (char*)psz)) {
@@ -200,7 +195,7 @@ finalize_it:
pData->uLastMySQLErrno = 0; /* reset error for error supression */
}
- return iRet;
+ RETiRet;
}
@@ -239,9 +234,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
}
/* ok, if we reach this point, we have something for us */
- if((iRet = createInstance(&pData)) != RS_RET_OK)
- goto finalize_it;
-
+ CHKiRet(createInstance(&pData));
/* rger 2004-10-28: added support for MySQL
* >server,dbname,userid,password
@@ -277,10 +270,10 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
* Retries make no sense.
*/
if (iMySQLPropErr) {
- logerror("Trouble with MySQL connection properties. -MySQL logging disabled");
+ errmsg.LogError(NO_ERRCODE, "Trouble with MySQL connection properties. -MySQL logging disabled");
ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
} else {
- CHKiRet(initMySQL(pData, 0));
+ pData->f_hmysql = NULL; /* initialize, but connect only on first message (important for queued mode!) */
}
CODE_STD_FINALIZERparseSelectorAct
@@ -300,8 +293,9 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
- *ipIFVersProvided = 1; /* so far, we only support the initial definition */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDmodInit
/*
* vi:set ai:
diff --git a/plugins/ommysql/ommysql.h b/plugins/ommysql/ommysql.h
index d8d2d538..d8075785 100644
--- a/plugins/ommysql/ommysql.h
+++ b/plugins/ommysql/ommysql.h
@@ -5,19 +5,20 @@
*
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
*
- * This program 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 2
- * of the License, or (at your option) any later version.
+ * This file is part of rsyslog.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
diff --git a/plugins/ompgsql/Makefile.am b/plugins/ompgsql/Makefile.am
index 5206b36d..b2e3effa 100644
--- a/plugins/ompgsql/Makefile.am
+++ b/plugins/ompgsql/Makefile.am
@@ -1,7 +1,7 @@
pkglib_LTLIBRARIES = ompgsql.la
-ompgsql_la_SOURCES = ompgsql.c ompgsql.h ../../module-template.h
-ompgsql_la_CPPFLAGS = $(pgsql_cflags) -I$(srcdir)/../..
+ompgsql_la_SOURCES = ompgsql.c ompgsql.h
+ompgsql_la_CPPFLAGS = -I$(top_srcdir) $(pgsql_cflags)
ompgsql_la_LDFLAGS = -module -avoid-version
ompgsql_la_LIBADD = $(pgsql_libs)
diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index 24a68a5f..1d7b2eb7 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -8,19 +8,20 @@
*
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
*
- * This program 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 2
- * of the License, or (at your option) any later version.
+ * This file is part of rsyslog.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
@@ -41,10 +42,14 @@
#include "template.h"
#include "ompgsql.h"
#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
/* internal structures
*/
DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
typedef struct _instanceData {
PGconn *f_hpgsql; /* handle to PgSQL */
@@ -87,27 +92,12 @@ CODESTARTfreeInstance
ENDfreeInstance
-BEGINneedUDPSocket
-CODESTARTneedUDPSocket
-ENDneedUDPSocket
-
-
BEGINdbgPrintInstInfo
CODESTARTdbgPrintInstInfo
/* nothing special here */
ENDdbgPrintInstInfo
-BEGINonSelectReadyWrite
-CODESTARTonSelectReadyWrite
-ENDonSelectReadyWrite
-
-
-BEGINgetWriteFDForSelect
-CODESTARTgetWriteFDForSelect
-ENDgetWriteFDForSelect
-
-
/* log a database error with descriptive message.
* We check if we have a valid handle. If not, we simply
* report an error, but can not be specific. RGerhards, 2007-01-30
@@ -123,7 +113,7 @@ static void reportDBError(instanceData *pData, int bSilent)
/* output log message */
errno = 0;
if(pData->f_hpgsql == NULL) {
- logerror("unknown DB error occured - could not obtain PgSQL handle");
+ errmsg.LogError(NO_ERRCODE, "unknown DB error occured - could not obtain PgSQL handle");
} else { /* we can ask pgsql for the error description... */
ePgSQLStatus = PQstatus(pData->f_hpgsql);
snprintf(errMsg, sizeof(errMsg)/sizeof(char), "db error (%d): %s\n", ePgSQLStatus,
@@ -132,7 +122,7 @@ static void reportDBError(instanceData *pData, int bSilent)
dbgprintf("pgsql, DBError(silent): %s\n", errMsg);
else {
pData->eLastPgSQLStatus = ePgSQLStatus;
- logerror(errMsg);
+ errmsg.LogError(NO_ERRCODE, "%s", errMsg);
}
}
@@ -160,7 +150,7 @@ static rsRetVal initPgSQL(instanceData *pData, int bSilent)
iRet = RS_RET_SUSPENDED;
}
- return iRet;
+ RETiRet;
}
@@ -196,7 +186,7 @@ finalize_it:
pData->eLastPgSQLStatus = CONNECTION_OK; /* reset error for error supression */
}
- return iRet;
+ RETiRet;
}
@@ -274,7 +264,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
* Retries make no sense.
*/
if (iPgSQLPropErr) {
- logerror("Trouble with PgSQL connection properties. -PgSQL logging disabled");
+ errmsg.LogError(NO_ERRCODE, "Trouble with PgSQL connection properties. -PgSQL logging disabled");
ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
} else {
CHKiRet(initPgSQL(pData, 0));
@@ -296,8 +286,9 @@ ENDqueryEtryPt
BEGINmodInit()
CODESTARTmodInit
- *ipIFVersProvided = 1; /* so far, we only support the initial definition */
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
ENDmodInit
/*
* vi:set ai:
diff --git a/plugins/ompgsql/ompgsql.h b/plugins/ompgsql/ompgsql.h
index 8285ac33..495291f4 100644
--- a/plugins/ompgsql/ompgsql.h
+++ b/plugins/ompgsql/ompgsql.h
@@ -5,19 +5,20 @@
*
* Copyright 2007 Rainer Gerhards and Adiscon GmbH.
*
- * This program 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 2
- * of the License, or (at your option) any later version.
+ * This file is part of rsyslog.
*
- * This program is distributed in the hope that it will be useful,
+ * 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 this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this distribution.
*/
diff --git a/plugins/omrelp/.cvsignore b/plugins/omrelp/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/omrelp/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/omrelp/Makefile.am b/plugins/omrelp/Makefile.am
new file mode 100644
index 00000000..dfc2111f
--- /dev/null
+++ b/plugins/omrelp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = omrelp.la
+
+omrelp_la_SOURCES = omrelp.c
+omrelp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags) $(RELP_CFLAGS)
+omrelp_la_LDFLAGS = -module -avoid-version
+omrelp_la_LIBADD = $(RELP_LIBS)
diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
new file mode 100644
index 00000000..39d25812
--- /dev/null
+++ b/plugins/omrelp/omrelp.c
@@ -0,0 +1,334 @@
+/* omrelp.c
+ *
+ * This is the implementation of the RELP output module.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2008-03-13 by RGerhards
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <librelp.h>
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+static relpEngine_t *pRelpEngine; /* our relp engine */
+
+typedef struct _instanceData {
+ char f_hname[MAXHOSTNAMELEN+1];
+ int compressionLevel; /* 0 - no compression, else level for zlib */
+ char *port;
+ int bInitialConnect; /* is this the initial connection request of our module? (0-no, 1-yes) */
+ int bIsConnected; /* currently connected to server? 0 - no, 1 - yes */
+ relpClt_t *pRelpClt; /* relp client for this instance */
+} instanceData;
+
+/* get the syslog forward port from selector_t. The passed in
+ * struct must be one that is setup for forwarding.
+ * rgerhards, 2007-06-28
+ * We may change the implementation to try to lookup the port
+ * if it is unspecified. So far, we use the IANA default auf 514.
+ */
+static char *getRelpPt(instanceData *pData)
+{
+ assert(pData != NULL);
+ if(pData->port == NULL)
+ return("514");
+ else
+ return(pData->port);
+}
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ pData->bInitialConnect = 1;
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if(pData->port != NULL)
+ free(pData->port);
+
+ /* final cleanup */
+ if(pData->pRelpClt != NULL)
+ relpEngineCltDestruct(pRelpEngine, &pData->pRelpClt);
+
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ printf("RELP/%s", pData->f_hname);
+ENDdbgPrintInstInfo
+
+
+/* try to connect to server
+ * rgerhards, 2008-03-21
+ */
+static rsRetVal doConnect(instanceData *pData)
+{
+ DEFiRet;
+
+ if(pData->bInitialConnect) {
+ iRet = relpCltConnect(pData->pRelpClt, family, (uchar*) pData->port, (uchar*) pData->f_hname);
+ if(iRet == RELP_RET_OK)
+ pData->bInitialConnect = 0;
+ } else {
+ iRet = relpCltReconnect(pData->pRelpClt);
+ }
+
+ if(iRet == RELP_RET_OK) {
+ pData->bIsConnected = 1;
+ } else {
+ pData->bIsConnected = 0;
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = doConnect(pData);
+ENDtryResume
+
+
+BEGINdoAction
+ uchar *pMsg; /* temporary buffering */
+ size_t lenMsg;
+ relpRetVal ret;
+CODESTARTdoAction
+ dbgprintf(" %s:%s/RELP\n", pData->f_hname, getRelpPt(pData));
+
+ if(!pData->bIsConnected) {
+ CHKiRet(doConnect(pData));
+ }
+
+ pMsg = ppString[0];
+ lenMsg = strlen((char*) pMsg); /* TODO: don't we get this? */
+
+ /* TODO: think about handling oversize messages! */
+ if(lenMsg > MAXLINE)
+ lenMsg = MAXLINE;
+
+ /* forward */
+ ret = relpCltSendSyslog(pData->pRelpClt, (uchar*) pMsg, lenMsg);
+RUNLOG_VAR("%d", ret);
+ if(ret != RELP_RET_OK) {
+ /* error! */
+ dbgprintf("error forwarding via relp, suspending\n");
+ iRet = RS_RET_SUSPENDED;
+ }
+
+finalize_it:
+ENDdoAction
+
+
+BEGINparseSelectorAct
+ uchar *q;
+ int i;
+ int bErr;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(!strncmp((char*) p, ":omrelp:", sizeof(":omrelp:") - 1)) {
+ p += sizeof(":omrelp:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ if((iRet = createInstance(&pData)) != RS_RET_OK)
+ FINALIZE;
+
+ /* we are now after the protocol indicator. Now check if we should
+ * use compression. We begin to use a new option format for this:
+ * @(option,option)host:port
+ * The first option defined is "z[0..9]" where the digit indicates
+ * the compression level. If it is not given, 9 (best compression) is
+ * assumed. An example action statement might be:
+ * :omrelp:(z5,o)127.0.0.1:1400
+ * Which means send via TCP with medium (5) compresion (z) to the local
+ * host on port 1400. The '0' option means that octet-couting (as in
+ * IETF I-D syslog-transport-tls) is to be used for framing (this option
+ * applies to TCP-based syslog only and is ignored when specified with UDP).
+ * That is not yet implemented.
+ * rgerhards, 2006-12-07
+ * TODO: think of all this in spite of RELP -- rgerhards, 2008-03-13
+ */
+ if(*p == '(') {
+ /* at this position, it *must* be an option indicator */
+ do {
+ ++p; /* eat '(' or ',' (depending on when called) */
+ /* check options */
+ if(*p == 'z') { /* compression */
+# ifdef USE_NETZIP
+ ++p; /* eat */
+ if(isdigit((int) *p)) {
+ int iLevel;
+ iLevel = *p - '0';
+ ++p; /* eat */
+ pData->compressionLevel = iLevel;
+ } else {
+ errmsg.LogError(NO_ERRCODE, "Invalid compression level '%c' specified in "
+ "forwardig action - NOT turning on compression.",
+ *p);
+ }
+# else
+ errmsg.LogError(NO_ERRCODE, "Compression requested, but rsyslogd is not compiled "
+ "with compression support - request ignored.");
+# endif /* #ifdef USE_NETZIP */
+ } else { /* invalid option! Just skip it... */
+ errmsg.LogError(NO_ERRCODE, "Invalid option %c in forwarding action - ignoring.", *p);
+ ++p; /* eat invalid option */
+ }
+ /* the option processing is done. We now do a generic skip
+ * to either the next option or the end of the option
+ * block.
+ */
+ while(*p && *p != ')' && *p != ',')
+ ++p; /* just skip it */
+ } while(*p && *p == ','); /* Attention: do.. while() */
+ if(*p == ')')
+ ++p; /* eat terminator, on to next */
+ else
+ /* we probably have end of string - leave it for the rest
+ * of the code to handle it (but warn the user)
+ */
+ errmsg.LogError(NO_ERRCODE, "Option block not terminated in forwarding action.");
+ }
+ /* extract the host first (we do a trick - we replace the ';' or ':' with a '\0')
+ * now skip to port and then template name. rgerhards 2005-07-06
+ */
+ for(q = p ; *p && *p != ';' && *p != ':' ; ++p)
+ /* JUST SKIP */;
+
+ pData->port = NULL;
+ if(*p == ':') { /* process port */
+ uchar * tmp;
+
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ tmp = ++p;
+ for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
+ /* SKIP AND COUNT */;
+ pData->port = malloc(i + 1);
+ if(pData->port == NULL) {
+ errmsg.LogError(NO_ERRCODE, "Could not get memory to store relp port, "
+ "using default port, results may not be what you intend\n");
+ /* we leave f_forw.port set to NULL, this is then handled by getRelpPt() */
+ } else {
+ memcpy(pData->port, tmp, i);
+ *(pData->port + i) = '\0';
+ }
+ }
+
+ /* now skip to template */
+ bErr = 0;
+ while(*p && *p != ';') {
+ if(*p && *p != ';' && !isspace((int) *p)) {
+ if(bErr == 0) { /* only 1 error msg! */
+ bErr = 1;
+ errno = 0;
+ errmsg.LogError(NO_ERRCODE, "invalid selector line (port), probably not doing "
+ "what was intended");
+ }
+ }
+ ++p;
+ }
+
+ /* TODO: make this if go away! */
+ if(*p == ';') {
+ *p = '\0'; /* trick to obtain hostname (later)! */
+ strcpy(pData->f_hname, (char*) q);
+ *p = ';';
+ } else
+ strcpy(pData->f_hname, (char*) q);
+
+ /* process template */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_ForwardFormat"));
+
+ /* create our relp client */
+ CHKiRet(relpEngineCltConstruct(pRelpEngine, &pData->pRelpClt)); /* we use CHKiRet as librelp has a similar return value range */
+
+ /* TODO: do we need to call freeInstance if we failed - this is a general question for
+ * all output modules. I'll address it later as the interface evolves. rgerhards, 2007-07-25
+ */
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ relpEngineDestruct(&pRelpEngine);
+
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ /* create our relp engine */
+ CHKiRet(relpEngineConstruct(&pRelpEngine));
+ CHKiRet(relpEngineSetDbgprint(pRelpEngine, dbgprintf));
+ CHKiRet(relpEngineSetEnableCmd(pRelpEngine, (uchar*) "syslog", eRelpCmdState_Required));
+
+ /* tell which objects we need */
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/omsnmp/.cvsignore b/plugins/omsnmp/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/omsnmp/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/omsnmp/Makefile.am b/plugins/omsnmp/Makefile.am
new file mode 100644
index 00000000..d74f7bb4
--- /dev/null
+++ b/plugins/omsnmp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = omsnmp.la
+
+omsnmp_la_SOURCES = omsnmp.c omsnmp.h
+omsnmp_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+omsnmp_la_LDFLAGS = -module -avoid-version
+omsnmp_la_LIBADD = $(snmp_libs)
diff --git a/plugins/omsnmp/mibs/ADISCON-MIB.txt b/plugins/omsnmp/mibs/ADISCON-MIB.txt
new file mode 100644
index 00000000..741ea84f
--- /dev/null
+++ b/plugins/omsnmp/mibs/ADISCON-MIB.txt
@@ -0,0 +1,38 @@
+-- *****************************************************************
+-- ADISCON-RSYSLOG-MIB.txt: Adiscon RSyslog message MIB file
+--
+-- March 2008, Andre Lorbach
+--
+-- Copyright (c) 2008 by Adiscon GmbH
+-- All rights reserved.
+-- *****************************************************************
+--
+-- This is a basic MIB which defines our main enterprise OID
+
+ADISCON-MIB DEFINITIONS ::= BEGIN
+
+--
+-- Top-level infrastructure for the Adiscon enterprise MIB tree
+--
+
+IMPORTS
+ MODULE-IDENTITY, enterprises FROM SNMPv2-SMI;
+
+adiscon MODULE-IDENTITY
+ LAST-UPDATED "200803040000Z"
+ ORGANIZATION "www.adiscon.com"
+ CONTACT-INFO
+ "postal: Adiscon GmbH
+ Mozartstrasse 21
+ D-97950 Großrinderfeld
+ Deutschland
+
+ email: info@adiscon.com"
+ DESCRIPTION
+ "Top-level infrastructure for the Adiscon enterprise MIB tree"
+ REVISION "200803040000Z"
+ DESCRIPTION
+ "First draft"
+ ::= { enterprises 19406}
+
+END
diff --git a/plugins/omsnmp/mibs/ADISCON-MONITORWARE-MIB.txt b/plugins/omsnmp/mibs/ADISCON-MONITORWARE-MIB.txt
new file mode 100644
index 00000000..d26d7746
--- /dev/null
+++ b/plugins/omsnmp/mibs/ADISCON-MONITORWARE-MIB.txt
@@ -0,0 +1,362 @@
+-- *****************************************************************
+-- ADISCON-MONITORWARE-MIB.txt: Adiscon Monitorware message MIB file
+--
+-- March 2008, Andre Lorbach
+--
+-- Copyright (c) 2008 by Adiscon GmbH
+-- All rights reserved.
+-- *****************************************************************
+--
+-- This MIB defines traps and variables to wrap syslog messages into
+-- snmp traps.
+
+ADISCON-MONITORWARE-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ enterprises,
+ MODULE-IDENTITY, OBJECT-TYPE, Integer32,
+ NOTIFICATION-TYPE FROM SNMPv2-SMI,
+ adiscon FROM ADISCON-MIB
+;
+
+monitorware MODULE-IDENTITY
+ LAST-UPDATED "200803050000Z"
+ ORGANIZATION "www.adiscon-com"
+ CONTACT-INFO
+ "postal: Adiscon GmbH
+ Mozartstrasse 21
+ D-97950 Großrinderfeld
+ Deutschland
+
+ email: info@adiscon.com"
+ DESCRIPTION
+ "This MIB defines traps and variables to wrap syslog messages into snmp traps."
+ REVISION "200803040000Z"
+ DESCRIPTION
+ "Added a few new variables for the representation of MonitorWare properties. Also added a few new traps."
+ REVISION "200803050000Z"
+ DESCRIPTION
+ "First draft"
+ ::= { adiscon 1 }
+
+-- Printable string, using the ISO 8859-1 character set.
+DisplayString ::= OCTET STRING (SIZE (0..255))
+SmallString ::= OCTET STRING (SIZE (0..64))
+--
+
+--
+-- top level structure
+--
+-- adiscon OBJECT IDENTIFIER ::= { enterprises 19406 }
+monitorware OBJECT IDENTIFIER ::= { adiscon 1 }
+monitorwarevars OBJECT IDENTIFIER ::= { monitorware 1 }
+monitorwaretraps OBJECT IDENTIFIER ::= { monitorware 2 }
+genericvars OBJECT IDENTIFIER ::= { monitorwarevars 1 }
+syslogvars OBJECT IDENTIFIER ::= { monitorwarevars 2 }
+eventlogvars OBJECT IDENTIFIER ::= { monitorwarevars 3 }
+filemonvars OBJECT IDENTIFIER ::= { monitorwarevars 4 }
+ntservicemonvars OBJECT IDENTIFIER ::= { monitorwarevars 5 }
+
+-- *****************************************************************
+-- Trap variables
+-- *****************************************************************
+
+syslogMsg OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Syslog Message, this will contain the full
+ syslog message including the full syslog header"
+ ::= { syslogvars 1 }
+
+syslogSeverity OBJECT-TYPE
+ SYNTAX INTEGER {
+ emergency (0),
+ alert (1),
+ critical (2),
+ error (3),
+ warning (4),
+ notice (5),
+ info (6),
+ debug (7)
+ }
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Syslog severity(priority)."
+ DEFVAL { 5 }
+ ::= { syslogvars 2 }
+
+syslogFacility OBJECT-TYPE
+ SYNTAX INTEGER {
+ kern (0),
+ user (1),
+ mail (2),
+ daemon (3),
+ auth (4),
+ syslog (5),
+ lpr (6),
+ news (7),
+ uucp (8),
+ cron (9),
+ local0 (16),
+ local1 (17),
+ local2 (18),
+ local3 (19),
+ local4 (20),
+ local5 (21),
+ local6 (22),
+ local7 (23)
+ }
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Syslog facility."
+ DEFVAL { 16 }
+ ::= { syslogvars 3 }
+
+syslogTag OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Contains the SyslogTag Value."
+ ::= { syslogvars 4 }
+
+genCustomerID OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generic Property CustomerID."
+ ::= { genericvars 1 }
+
+genSystemID OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generic Property SystemID."
+ ::= { genericvars 2 }
+
+genSource OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generic Source Property."
+ ::= { genericvars 3 }
+
+genTimereported OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Timestamp of when the event was reported."
+ ::= { genericvars 4 }
+
+genTimegenerated OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Timestamp of when the event was generated."
+ ::= { genericvars 5 }
+
+genIut OBJECT-TYPE
+ SYNTAX INTEGER {
+ Unknown (0),
+ Syslog (1),
+ Heartbeat (2),
+ NTEventReport (3),
+ SNMPTrap (4),
+ FileMonitor (5),
+ PingProbe (8),
+ PortProbe (9),
+ NTServiceMonitor (10),
+ DiskSpaceMonitor (11),
+ DBMonitor (12),
+ SerialMonitor (13),
+ CPUMonitor (14),
+ AliveMonRequest (16),
+ SMTPProbe (17),
+ FTPProbe (18),
+ HTTPProbe (19),
+ POP3Probe (20),
+ IMAPProbe (21),
+ NNTPProbe (22),
+ WEVTMONV2 (23),
+ SMTPLISTENER (24),
+ SNMPMONITOR (25),
+ AliveMonECHO (1999998)
+ }
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "InfoUnit TypeID, defines from which Source the event is derived from."
+ DEFVAL { 0 }
+ ::= { genericvars 6 }
+
+genMsg OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generic Message for this event"
+ ::= { genericvars 7 }
+
+eventlogEventID OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "EventID of the EventLog Entry"
+ ::= { eventlogvars 1 }
+
+eventlogEventType OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "EventLog Type of the EventLog Entry (Like Application, Security or System)"
+ ::= { eventlogvars 2 }
+
+eventlogEventSource OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "EventLog Source of the EventLog Entry"
+ ::= { eventlogvars 3 }
+
+eventlogEventCategoryID OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Event Category number of the EventLog Entry"
+ ::= { eventlogvars 4 }
+
+eventlogEventCategoryName OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Event Category name of the EventLog Entry"
+ ::= { eventlogvars 5 }
+
+eventlogEventUser OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Event User of the EventLog Entry"
+ ::= { eventlogvars 6 }
+
+filemonGenericFilename OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generic Filename template used to create the filename"
+ ::= { filemonvars 1 }
+
+filemonGeneratedFilename OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Generated Filename, the source file of this event."
+ ::= { filemonvars 2 }
+
+filemonMsgseperator OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "The message seperator which was used."
+ ::= { filemonvars 3 }
+
+ntserviceServiceName OBJECT-TYPE
+ SYNTAX SmallString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Internal Name of the monitored service."
+ ::= { ntservicemonvars 1 }
+
+ntserviceServiceDisplayName OBJECT-TYPE
+ SYNTAX DisplayString
+ MAX-ACCESS accessible-for-notify
+ STATUS current
+ DESCRIPTION
+ "Display Name of the monitored service."
+ ::= { ntservicemonvars 2 }
+
+
+-- *****************************************************************
+-- Trap definitions
+-- *****************************************************************
+
+syslogtrap NOTIFICATION-TYPE
+ OBJECTS { syslogMsg,
+ syslogSeverity,
+ syslogFacility
+ }
+ STATUS current
+ DESCRIPTION
+ "Syslogmessage Trap."
+::= { monitorwaretraps 1 }
+
+monitorwaretrap NOTIFICATION-TYPE
+ OBJECTS { genMsg,
+ genSource,
+ genTimegenerated,
+ genIut
+ }
+ STATUS current
+ DESCRIPTION
+ "Generic Trap from monitorware events."
+::= { monitorwaretraps 2 }
+
+eventmontrap NOTIFICATION-TYPE
+ OBJECTS { genMsg,
+ genSource,
+ eventlogEventID,
+ eventlogEventType,
+ eventlogEventSource,
+ eventlogEventCategoryID,
+ eventlogEventCategoryName,
+ eventlogEventUser
+ }
+ STATUS current
+ DESCRIPTION
+ "Trap generated by the EventLog Monitor."
+::= { monitorwaretraps 3 }
+
+filemontrap NOTIFICATION-TYPE
+ OBJECTS { genMsg,
+ genSource,
+ genTimegenerated,
+ filemonGenericFilename,
+ filemonGeneratedFilename
+ }
+ STATUS current
+ DESCRIPTION
+ "Trap generated by the FileMonitor."
+::= { monitorwaretraps 4 }
+
+ntservicetrap NOTIFICATION-TYPE
+ OBJECTS { genMsg,
+ genSource,
+ genTimegenerated,
+ ntserviceServiceName,
+ ntserviceServiceDisplayName
+ }
+ STATUS current
+ DESCRIPTION
+ "Trap generated by the NT Service Monitor."
+::= { monitorwaretraps 5 }
+
+END
diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c
new file mode 100644
index 00000000..22d48340
--- /dev/null
+++ b/plugins/omsnmp/omsnmp.c
@@ -0,0 +1,533 @@
+/* omsnmp.c
+ *
+ * This module sends an snmp trap.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <assert.h>
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "cfsysline.h"
+#include "module-template.h"
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+#include "omsnmp.h"
+#include "errmsg.h"
+
+MODULE_TYPE_OUTPUT
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/* Default static snmp OID's */
+/*unused
+static oid objid_enterprise[] = { 1, 3, 6, 1, 4, 1, 3, 1, 1 };
+static oid objid_sysdescr[] = { 1, 3, 6, 1, 2, 1, 1, 1, 0 };
+*/
+static oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 };
+static oid objid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 };
+
+static uchar* pszTransport = NULL; /* default transport */
+static uchar* pszTarget = NULL;
+/* note using an unsigned for a port number is not a good idea from an IPv6 point of view */
+static int iPort = 0;
+static int iSNMPVersion = 1; /* 0 Means SNMPv1, 1 Means SNMPv2c */
+static uchar* pszCommunity = NULL;
+static uchar* pszEnterpriseOID = NULL;
+static uchar* pszSnmpTrapOID = NULL;
+static uchar* pszSyslogMessageOID = NULL;
+static int iSpecificType = 0;
+static int iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;/*Default is SNMP_TRAP_ENTERPRISESPECIFIC */
+/*
+ Possible Values
+ SNMP_TRAP_COLDSTART (0)
+ SNMP_TRAP_WARMSTART (1)
+ SNMP_TRAP_LINKDOWN (2)
+ SNMP_TRAP_LINKUP (3)
+ SNMP_TRAP_AUTHFAIL (4)
+ SNMP_TRAP_EGPNEIGHBORLOSS (5)
+ SNMP_TRAP_ENTERPRISESPECIFIC (6)
+*/
+
+typedef struct _instanceData {
+ uchar szTransport[OMSNMP_MAXTRANSPORLENGTH+1]; /* Transport - Can be udp, tcp, udp6, tcp6 and other types supported by NET-SNMP */
+ uchar *szTarget; /* IP/hostname of Snmp Target*/
+ uchar *szTargetAndPort; /* IP/hostname + Port,needed format for SNMP LIB */
+ uchar szCommunity[OMSNMP_MAXCOMMUNITYLENGHT+1]; /* Snmp Community */
+ uchar szEnterpriseOID[OMSNMP_MAXOIDLENGHT+1]; /* Snmp Enterprise OID - default is (1.3.6.1.4.1.3.1.1 = enterprises.cmu.1.1) */
+ uchar szSnmpTrapOID[OMSNMP_MAXOIDLENGHT+1]; /* Snmp Trap OID - default is (1.3.6.1.4.1.19406.1.2.1 = ADISCON-MONITORWARE-MIB::syslogtrap) */
+ uchar szSyslogMessageOID[OMSNMP_MAXOIDLENGHT+1]; /* Snmp OID used for the Syslog Message:
+ * default is 1.3.6.1.4.1.19406.1.1.2.1 - ADISCON-MONITORWARE-MIB::syslogMsg
+ * You will need the ADISCON-MONITORWARE-MIB and ADISCON-MIB mibs installed on the receiver
+ * side in order to decode this mib.
+ * Downloads of these mib files can be found here:
+ * http://www.adiscon.org/download/ADISCON-MONITORWARE-MIB.txt
+ * http://www.adiscon.org/download/ADISCON-MIB.txt
+ */
+ int iPort; /* Target Port */
+ int iSNMPVersion; /* SNMP Version to use */
+ int iTrapType; /* Snmp TrapType or GenericType */
+ int iSpecificType; /* Snmp Specific Type */
+
+ netsnmp_session *snmpsession; /* Holds to SNMP Session, NULL if not initialized */
+} instanceData;
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("SNMPTransport: %s\n", pData->szTransport);
+ dbgprintf("SNMPTarget: %s\n", pData->szTarget);
+ dbgprintf("SNMPPort: %d\n", pData->iPort);
+ dbgprintf("SNMPTarget+PortStr: %s\n", pData->szTargetAndPort);
+ dbgprintf("SNMPVersion (0=v1, 1=v2c): %d\n", pData->iSNMPVersion);
+ dbgprintf("Community: %s\n", pData->szCommunity);
+ dbgprintf("EnterpriseOID: %s\n", pData->szEnterpriseOID);
+ dbgprintf("SnmpTrapOID: %s\n", pData->szSnmpTrapOID);
+ dbgprintf("SyslogMessageOID: %s\n", pData->szSyslogMessageOID);
+ dbgprintf("TrapType: %d\n", pData->iTrapType);
+ dbgprintf("SpecificType: %d\n", pData->iSpecificType);
+ENDdbgPrintInstInfo
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* we are not compatible with repeated msg reduction feature, so do not allow it */
+ENDisCompatibleWithFeature
+
+/* Exit SNMP Session
+ * alorbach, 2008-02-12
+ */
+static rsRetVal omsnmp_exitSession(instanceData *pData)
+{
+ DEFiRet;
+
+ if(pData->snmpsession != NULL) {
+ dbgprintf( "omsnmp_exitSession: Clearing Session to '%s' on Port = '%d'\n", pData->szTarget, pData->iPort);
+ snmp_close(pData->snmpsession);
+ pData->snmpsession = NULL;
+ }
+
+ RETiRet;
+}
+
+/* Init SNMP Session
+ * alorbach, 2008-02-12
+ */
+static rsRetVal omsnmp_initSession(instanceData *pData)
+{
+ DEFiRet;
+ netsnmp_session session;
+
+ /* should not happen, but if session is not cleared yet - we do it now! */
+ if (pData->snmpsession != NULL)
+ omsnmp_exitSession(pData);
+
+ dbgprintf( "omsnmp_initSession: ENTER - Target = '%s' on Port = '%d'\n", pData->szTarget, pData->iPort);
+
+ putenv(strdup("POSIXLY_CORRECT=1"));
+
+ snmp_sess_init(&session);
+ session.version = pData->iSNMPVersion;
+ session.callback = NULL; /* NOT NEEDED */
+ session.callback_magic = NULL;
+ session.peername = (char*) pData->szTargetAndPort;
+
+ /* Set SNMP Community */
+ if (session.version == SNMP_VERSION_1 || session.version == SNMP_VERSION_2c)
+ {
+ session.community = (unsigned char *) pData->szCommunity;
+ session.community_len = strlen((char*) pData->szCommunity);
+ }
+
+ pData->snmpsession = snmp_open(&session);
+ if (pData->snmpsession == NULL) {
+ errmsg.LogError(NO_ERRCODE, "omsnmp_initSession: snmp_open to host '%s' on Port '%d' failed\n", pData->szTarget, pData->iPort);
+ /* Stay suspended */
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz)
+{
+ DEFiRet;
+
+ netsnmp_pdu *pdu = NULL;
+ oid enterpriseoid[MAX_OID_LEN];
+ size_t enterpriseoidlen = MAX_OID_LEN;
+ oid oidSyslogMessage[MAX_OID_LEN];
+ size_t oLen = MAX_OID_LEN;
+ int status;
+ char *trap = NULL;
+ const char *strErr = NULL;
+
+ /* Init SNMP Session if necessary */
+ if (pData->snmpsession == NULL) {
+ CHKiRet(omsnmp_initSession(pData));
+ }
+
+ /* String should not be NULL */
+ ASSERT(psz != NULL);
+ dbgprintf( "omsnmp_sendsnmp: ENTER - Syslogmessage = '%s'\n", (char*)psz);
+
+ /* If SNMP Version1 is configured !*/
+ if ( pData->snmpsession->version == SNMP_VERSION_1)
+ {
+ pdu = snmp_pdu_create(SNMP_MSG_TRAP);
+
+ /* Set enterprise */
+ if (!snmp_parse_oid( (char*) pData->szEnterpriseOID, enterpriseoid, &enterpriseoidlen ))
+ {
+ strErr = snmp_api_errstring(snmp_errno);
+ errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Parsing EnterpriseOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr);
+
+ ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
+ }
+ pdu->enterprise = (oid *) malloc(enterpriseoidlen * sizeof(oid));
+ memcpy(pdu->enterprise, enterpriseoid, enterpriseoidlen * sizeof(oid));
+ pdu->enterprise_length = enterpriseoidlen;
+
+ /* Set Traptype */
+ pdu->trap_type = pData->iTrapType;
+
+ /* Set SpecificType */
+ pdu->specific_type = pData->iSpecificType;
+
+ /* Set Updtime */
+ pdu->time = get_uptime();
+ }
+ /* If SNMP Version2c is configured !*/
+ else if (pData->snmpsession->version == SNMP_VERSION_2c)
+ {
+ long sysuptime;
+ char csysuptime[20];
+
+ /* Create PDU */
+ pdu = snmp_pdu_create(SNMP_MSG_TRAP2);
+
+ /* Set uptime */
+ sysuptime = get_uptime();
+ snprintf( csysuptime, sizeof(csysuptime) , "%ld", sysuptime);
+ trap = csysuptime;
+ snmp_add_var(pdu, objid_sysuptime, sizeof(objid_sysuptime) / sizeof(oid), 't', trap);
+
+ /* Now set the SyslogMessage Trap OID */
+ if ( snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', (char*) pData->szSnmpTrapOID ) != 0)
+ {
+ strErr = snmp_api_errstring(snmp_errno);
+ errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Adding trap OID failed '%s' with error '%s' \n", pData->szSnmpTrapOID, strErr);
+ ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
+ }
+ }
+
+ /* SET TRAP PARAMETER for SyslogMessage! */
+/* dbgprintf( "omsnmp_sendsnmp: SyslogMessage '%s'\n", psz );*/
+
+ /* First create new OID object */
+ if (snmp_parse_oid( (char*) pData->szSyslogMessageOID, oidSyslogMessage, &oLen))
+ {
+ int iErrCode = snmp_add_var(pdu, oidSyslogMessage, oLen, 's', (char*) psz);
+ if (iErrCode)
+ {
+ const char *str = snmp_api_errstring(iErrCode);
+ errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Invalid SyslogMessage OID, error code '%d' - '%s'\n", iErrCode, str );
+ ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
+ }
+ }
+ else
+ {
+ strErr = snmp_api_errstring(snmp_errno);
+ errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: Parsing SyslogMessageOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr);
+
+ ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
+ }
+
+ /* Send the TRAP */
+ status = snmp_send(pData->snmpsession, pdu) == 0;
+ if (status)
+ {
+ /* Debug Output! */
+ int iErrorCode = pData->snmpsession->s_snmp_errno;
+ errmsg.LogError(NO_ERRCODE, "omsnmp_sendsnmp: snmp_send failed error '%d', Description='%s'\n", iErrorCode*(-1), api_errors[iErrorCode*(-1)]);
+
+ /* Clear Session */
+ omsnmp_exitSession(pData);
+
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pdu != NULL) {
+ snmp_free_pdu(pdu);
+ }
+ }
+
+ dbgprintf( "omsnmp_sendsnmp: LEAVE\n");
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = omsnmp_initSession(pData);
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ /* Abort if the STRING is not set, should never happen */
+ if (ppString[0] == NULL) {
+ ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
+ }
+
+ /* This will generate and send the SNMP Trap */
+ iRet = omsnmp_sendsnmp(pData, ppString[0]);
+finalize_it:
+ENDdoAction
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* free snmp Session here */
+ omsnmp_exitSession(pData);
+
+ if(pData->szTarget != NULL)
+ free(pData->szTarget);
+ if(pData->szTargetAndPort != NULL)
+ free(pData->szTargetAndPort);
+
+ENDfreeInstance
+
+
+BEGINparseSelectorAct
+ uchar szTargetAndPort[MAXHOSTNAMELEN+128]; /* work buffer for specifying a full target and port string */
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ if(!strncmp((char*) p, ":omsnmp:", sizeof(":omsnmp:") - 1)) {
+ p += sizeof(":omsnmp:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ if((iRet = createInstance(&pData)) != RS_RET_OK)
+ FINALIZE;
+
+ /* Check Transport */
+ if (pszTransport == NULL) {
+ /*
+ * Default transport is UDP. Other values supported by NETSNMP are possible as well
+ */
+ strncpy( (char*) pData->szTransport, "udp", sizeof("udp") );
+ } else {
+ /* Copy Transport */
+ strncpy( (char*) pData->szTransport, (char*) pszTransport, strlen((char*) pszTransport) );
+ }
+
+ /* Check Target */
+ if (pszTarget == NULL) {
+ ABORT_FINALIZE( RS_RET_PARAM_ERROR );
+ } else {
+ /* Copy Target */
+ CHKmalloc(pData->szTarget = (uchar*) strdup((char*)pszTarget));
+ }
+
+ /* Copy Community */
+ if (pszCommunity == NULL) /* Failsave */
+ strncpy( (char*) pData->szCommunity, "public", sizeof("public") );
+ else /* Copy Target */
+ strncpy( (char*) pData->szCommunity, (char*) pszCommunity, strlen((char*) pszCommunity) );
+
+ /* Copy Enterprise OID */
+ if (pszEnterpriseOID == NULL) /* Failsave */
+ strncpy( (char*) pData->szEnterpriseOID, "1.3.6.1.4.1.3.1.1", sizeof("1.3.6.1.4.1.3.1.1") );
+ else /* Copy Target */
+ strncpy( (char*) pData->szEnterpriseOID, (char*) pszEnterpriseOID, strlen((char*) pszEnterpriseOID) );
+
+ /* Copy SnmpTrap OID */
+ if (pszSnmpTrapOID == NULL) /* Failsave */
+ strncpy( (char*) pData->szSnmpTrapOID, "1.3.6.1.4.1.19406.1.2.1", sizeof("1.3.6.1.4.1.19406.1.2.1") );
+ else /* Copy Target */
+ strncpy( (char*) pData->szSnmpTrapOID, (char*) pszSnmpTrapOID, strlen((char*) pszSnmpTrapOID) );
+
+
+ /* Copy SyslogMessage OID */
+ if (pszSyslogMessageOID == NULL) /* Failsave */
+ strncpy( (char*) pData->szSyslogMessageOID, "1.3.6.1.4.1.19406.1.1.2.1", sizeof("1.3.6.1.4.1.19406.1.1.2.1") );
+ else /* Copy Target */
+ strncpy( (char*) pData->szSyslogMessageOID, (char*) pszSyslogMessageOID, strlen((char*) pszSyslogMessageOID) );
+
+ /* Copy Port */
+ if ( iPort == 0) /* If no Port is set we use the default Port 162 */
+ pData->iPort = 162;
+ else
+ pData->iPort = iPort;
+
+ /* Set SNMPVersion */
+ if ( iSNMPVersion < 0 || iSNMPVersion > 1) /* Set default to 1 if out of range */
+ pData->iSNMPVersion = 1;
+ else
+ pData->iSNMPVersion = iSNMPVersion;
+
+ /* Copy SpecificType */
+ if ( iSpecificType == 0) /* If no iSpecificType is set, we use the default 0 */
+ pData->iSpecificType = 0;
+ else
+ pData->iSpecificType = iSpecificType;
+
+ /* Copy TrapType */
+ if ( iTrapType < 0 && iTrapType >= 6) /* Only allow values from 0 to 6 !*/
+ pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
+ else
+ pData->iTrapType = iTrapType;
+
+ /* Create string for session peername! */
+ snprintf((char*)szTargetAndPort, sizeof(szTargetAndPort), "%s:%s:%d", pData->szTransport, pData->szTarget, pData->iPort);
+ CHKmalloc(pData->szTargetAndPort = (uchar*)strdup((char*)szTargetAndPort));
+
+ /* Print Debug info */
+ dbgprintf("SNMPTransport: %s\n", pData->szTransport);
+ dbgprintf("SNMPTarget: %s\n", pData->szTarget);
+ dbgprintf("SNMPPort: %d\n", pData->iPort);
+ dbgprintf("SNMPTarget+PortStr: %s\n", pData->szTargetAndPort);
+ dbgprintf("SNMPVersion (0=v1, 1=v2c): %d\n", pData->iSNMPVersion);
+ dbgprintf("Community: %s\n", pData->szCommunity);
+ dbgprintf("EnterpriseOID: %s\n", pData->szEnterpriseOID);
+ dbgprintf("SnmpTrapOID: %s\n", pData->szSnmpTrapOID);
+ dbgprintf("SyslogMessageOID: %s\n", pData->szSyslogMessageOID);
+ dbgprintf("TrapType: %d\n", pData->iTrapType);
+ dbgprintf("SpecificType: %d\n", pData->iSpecificType);
+
+ /* process template */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS, (uchar*) "RSYSLOG_TraditionalForwardFormat"));
+
+ /* Init NetSNMP library and read in MIB database */
+ init_snmp("rsyslog");
+
+ /* Set some defaults in the NetSNMP library */
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DEFAULT_PORT, pData->iPort );
+
+ /* Init Session Pointer */
+ pData->snmpsession = NULL;
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+/* Reset config variables for this module to default values.
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ DEFiRet;
+
+ if (pszTarget != NULL)
+ free(pszTarget);
+ pszTarget = NULL;
+
+ if (pszCommunity != NULL)
+ free(pszCommunity);
+ pszCommunity = NULL;
+
+ if (pszEnterpriseOID != NULL)
+ free(pszEnterpriseOID);
+ pszEnterpriseOID = NULL;
+
+ if (pszSnmpTrapOID != NULL)
+ free(pszSnmpTrapOID);
+ pszSnmpTrapOID = NULL;
+
+ if (pszSyslogMessageOID != NULL)
+ free(pszSyslogMessageOID);
+ pszSyslogMessageOID = NULL;
+
+ iPort = 0;
+ iSNMPVersion = 1;
+ iSpecificType = 0;
+ iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC;
+
+ RETiRet;
+}
+
+
+BEGINmodExit
+CODESTARTmodExit
+ if (pszTarget != NULL)
+ free(pszTarget);
+ if (pszCommunity != NULL)
+ free(pszCommunity);
+ if (pszEnterpriseOID != NULL)
+ free(pszEnterpriseOID);
+ if (pszSnmpTrapOID != NULL)
+ free(pszSnmpTrapOID);
+ if (pszSyslogMessageOID != NULL)
+ free(pszSyslogMessageOID);
+
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &pszTransport, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &pszTarget, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &iPort, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &iSNMPVersion, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &pszCommunity, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &pszEnterpriseOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &pszSnmpTrapOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &pszSyslogMessageOID, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &iSpecificType, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &iTrapType, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/*
+ * vi:set ai:
+ */
diff --git a/plugins/omsnmp/omsnmp.h b/plugins/omsnmp/omsnmp.h
new file mode 100644
index 00000000..be3eb2a2
--- /dev/null
+++ b/plugins/omsnmp/omsnmp.h
@@ -0,0 +1,106 @@
+/* omsnmp.h
+ * These are the definitions for the build-in MySQL output module.
+ *
+ * File begun on 2007-07-13 by RGerhards (extracted from syslogd.c)
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#ifndef OMSNMP_H_INCLUDED
+#define OMSNMP_H_INCLUDED 1
+
+#define OMSNMP_MAXTRANSPORLENGTH 10
+#define OMSNMP_MAXPORTLENGHT 5
+#define OMSNMP_MAXCOMMUNITYLENGHT 255
+#define OMSNMP_MAXOIDLENGHT 255
+
+
+#endif /* #ifndef OMMYSQL_H_INCLUDED */
+/*
+ * vi:set ai:
+ */
+
+#include <net-snmp/library/snmp_api.h>
+
+static const char *api_errors[-SNMPERR_MAX + 1] = {
+ "No error", /* SNMPERR_SUCCESS */
+ "Generic error", /* SNMPERR_GENERR */
+ "Invalid local port", /* SNMPERR_BAD_LOCPORT */
+ "Unknown host", /* SNMPERR_BAD_ADDRESS */
+ "Unknown session", /* SNMPERR_BAD_SESSION */
+ "Too long", /* SNMPERR_TOO_LONG */
+ "No socket", /* SNMPERR_NO_SOCKET */
+ "Cannot send V2 PDU on V1 session", /* SNMPERR_V2_IN_V1 */
+ "Cannot send V1 PDU on V2 session", /* SNMPERR_V1_IN_V2 */
+ "Bad value for non-repeaters", /* SNMPERR_BAD_REPEATERS */
+ "Bad value for max-repetitions", /* SNMPERR_BAD_REPETITIONS */
+ "Error building ASN.1 representation", /* SNMPERR_BAD_ASN1_BUILD */
+ "Failure in sendto", /* SNMPERR_BAD_SENDTO */
+ "Bad parse of ASN.1 type", /* SNMPERR_BAD_PARSE */
+ "Bad version specified", /* SNMPERR_BAD_VERSION */
+ "Bad source party specified", /* SNMPERR_BAD_SRC_PARTY */
+ "Bad destination party specified", /* SNMPERR_BAD_DST_PARTY */
+ "Bad context specified", /* SNMPERR_BAD_CONTEXT */
+ "Bad community specified", /* SNMPERR_BAD_COMMUNITY */
+ "Cannot send noAuth/Priv", /* SNMPERR_NOAUTH_DESPRIV */
+ "Bad ACL definition", /* SNMPERR_BAD_ACL */
+ "Bad Party definition", /* SNMPERR_BAD_PARTY */
+ "Session abort failure", /* SNMPERR_ABORT */
+ "Unknown PDU type", /* SNMPERR_UNKNOWN_PDU */
+ "Timeout", /* SNMPERR_TIMEOUT */
+ "Failure in recvfrom", /* SNMPERR_BAD_RECVFROM */
+ "Unable to determine contextEngineID", /* SNMPERR_BAD_ENG_ID */
+ "No securityName specified", /* SNMPERR_BAD_SEC_NAME */
+ "Unable to determine securityLevel", /* SNMPERR_BAD_SEC_LEVEL */
+ "ASN.1 parse error in message", /* SNMPERR_ASN_PARSE_ERR */
+ "Unknown security model in message", /* SNMPERR_UNKNOWN_SEC_MODEL */
+ "Invalid message (e.g. msgFlags)", /* SNMPERR_INVALID_MSG */
+ "Unknown engine ID", /* SNMPERR_UNKNOWN_ENG_ID */
+ "Unknown user name", /* SNMPERR_UNKNOWN_USER_NAME */
+ "Unsupported security level", /* SNMPERR_UNSUPPORTED_SEC_LEVEL */
+ "Authentication failure (incorrect password, community or key)", /* SNMPERR_AUTHENTICATION_FAILURE */
+ "Not in time window", /* SNMPERR_NOT_IN_TIME_WINDOW */
+ "Decryption error", /* SNMPERR_DECRYPTION_ERR */
+ "SCAPI general failure", /* SNMPERR_SC_GENERAL_FAILURE */
+ "SCAPI sub-system not configured", /* SNMPERR_SC_NOT_CONFIGURED */
+ "Key tools not available", /* SNMPERR_KT_NOT_AVAILABLE */
+ "Unknown Report message", /* SNMPERR_UNKNOWN_REPORT */
+ "USM generic error", /* SNMPERR_USM_GENERICERROR */
+ "USM unknown security name (no such user exists)", /* SNMPERR_USM_UNKNOWNSECURITYNAME */
+ "USM unsupported security level (this user has not been configured for that level of security)", /* SNMPERR_USM_UNSUPPORTEDSECURITYLEVEL */
+ "USM encryption error", /* SNMPERR_USM_ENCRYPTIONERROR */
+ "USM authentication failure (incorrect password or key)", /* SNMPERR_USM_AUTHENTICATIONFAILURE */
+ "USM parse error", /* SNMPERR_USM_PARSEERROR */
+ "USM unknown engineID", /* SNMPERR_USM_UNKNOWNENGINEID */
+ "USM not in time window", /* SNMPERR_USM_NOTINTIMEWINDOW */
+ "USM decryption error", /* SNMPERR_USM_DECRYPTIONERROR */
+ "MIB not initialized", /* SNMPERR_NOMIB */
+ "Value out of range", /* SNMPERR_RANGE */
+ "Sub-id out of range", /* SNMPERR_MAX_SUBID */
+ "Bad sub-id in object identifier", /* SNMPERR_BAD_SUBID */
+ "Object identifier too long", /* SNMPERR_LONG_OID */
+ "Bad value name", /* SNMPERR_BAD_NAME */
+ "Bad value notation", /* SNMPERR_VALUE */
+ "Unknown Object Identifier", /* SNMPERR_UNKNOWN_OBJID */
+ "No PDU in snmp_send", /* SNMPERR_NULL_PDU */
+ "Missing variables in PDU", /* SNMPERR_NO_VARS */
+ "Bad variable type", /* SNMPERR_VAR_TYPE */
+ "Out of memory (malloc failure)", /* SNMPERR_MALLOC */
+ "Kerberos related error", /* SNMPERR_KRB5 */
+};
diff --git a/plugins/omtesting/.cvsignore b/plugins/omtesting/.cvsignore
new file mode 100644
index 00000000..9730646f
--- /dev/null
+++ b/plugins/omtesting/.cvsignore
@@ -0,0 +1,6 @@
+.deps
+.libs
+Makefile
+Makefile.in
+*.la
+*.lo
diff --git a/plugins/omtesting/Makefile.am b/plugins/omtesting/Makefile.am
new file mode 100644
index 00000000..7e376683
--- /dev/null
+++ b/plugins/omtesting/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = omtesting.la
+
+omtesting_la_SOURCES = omtesting.c
+omtesting_la_CPPFLAGS = -I$(top_srcdir) $(pthreads_cflags)
+omtesting_la_LDFLAGS = -module -avoid-version
+omtesting_la_LIBADD =
diff --git a/plugins/omtesting/omtesting.c b/plugins/omtesting/omtesting.c
new file mode 100644
index 00000000..15d3cb80
--- /dev/null
+++ b/plugins/omtesting/omtesting.c
@@ -0,0 +1,183 @@
+/* omtesting.c
+ *
+ * This module is a testing aid. It is not meant to be used in production. I have
+ * initially written it to introduce delays of custom length to action processing.
+ * This is needed for development of new message queueing methods. However, I think
+ * there are other uses for this module. For example, I can envision that it is a good
+ * thing to have an output module that requests a retry on every "n"th invocation
+ * and such things. I implement only what I need. But should further testing needs
+ * arise, it makes much sense to add them here.
+ *
+ * This module will become part of the CVS and the rsyslog project because I think
+ * it is a generally useful debugging, testing and development aid for everyone
+ * involved with rsyslog.
+ *
+ * CURRENT SUPPORTED COMMANDS:
+ *
+ * :omtesting:sleep <seconds> <milliseconds>
+ *
+ * Must be specified exactly as above. Keep in mind milliseconds are a millionth
+ * of a second!
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * Copyright 2007 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 <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include "syslogd.h"
+#include "syslogd-types.h"
+#include "module-template.h"
+
+MODULE_TYPE_OUTPUT
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+typedef struct _instanceData {
+ int iWaitSeconds;
+ int iWaitUSeconds; /* milli-seconds (one million of a second, just to make sure...) */
+} instanceData;
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ pData->iWaitSeconds = 1;
+ pData->iWaitUSeconds = 0;
+ENDcreateInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("Action delays rule by %d second(s) and %d millisecond(s)\n",
+ pData->iWaitSeconds, pData->iWaitUSeconds);
+ /* do nothing */
+ENDdbgPrintInstInfo
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ /* we are not compatible with repeated msg reduction feature, so do not allow it */
+ENDisCompatibleWithFeature
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ struct timeval tvSelectTimeout;
+
+ dbgprintf("sleep(%d, %d)\n", pData->iWaitSeconds, pData->iWaitUSeconds);
+ tvSelectTimeout.tv_sec = pData->iWaitSeconds;
+ tvSelectTimeout.tv_usec = pData->iWaitUSeconds; /* milli seconds */
+ select(0, NULL, NULL, NULL, &tvSelectTimeout);
+ //dbgprintf(":omtesting: end doAction(), iRet %d\n", iRet);
+ENDdoAction
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* we do not have instance data, so we do not need to
+ * do anything here. -- rgerhards, 2007-07-25
+ */
+ENDfreeInstance
+
+
+BEGINparseSelectorAct
+ int i;
+ uchar szBuf[1024];
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(0)
+ /* code here is quick and dirty - if you like, clean it up. But keep
+ * in mind it is just a testing aid ;) -- rgerhards, 2007-12-31
+ */
+ if(!strncmp((char*) p, ":omtesting:", sizeof(":omtesting:") - 1)) {
+ p += sizeof(":omtesting:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ } else {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ if((iRet = createInstance(&pData)) != RS_RET_OK)
+ goto finalize_it;
+
+ /* check mode */
+ for(i = 0 ; *p && !isspace((char) *p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
+ szBuf[i] = (uchar) *p++;
+ }
+ szBuf[i] = '\0';
+ if(isspace(*p))
+ ++p;
+
+ if(!strcmp((char*) szBuf, "sleep")) {
+ /* parse seconds */
+ for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
+ szBuf[i] = *p++;
+ }
+ szBuf[i] = '\0';
+ if(isspace(*p))
+ ++p;
+ pData->iWaitSeconds = atoi((char*) szBuf);
+ /* parse milliseconds */
+ for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
+ szBuf[i] = *p++;
+ }
+ szBuf[i] = '\0';
+ if(isspace(*p))
+ ++p;
+ pData->iWaitUSeconds = atoi((char*) szBuf);
+ }
+ /* once there are other modes, here is the spot to add it! */
+ else {
+ dbgprintf("invalid mode '%s', doing 'sleep 1 0' - fix your config\n", szBuf);
+ }
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ENDmodInit
+/*
+ * vi:set ai:
+ */