diff options
Diffstat (limited to 'plugins')
66 files changed, 5619 insertions, 3949 deletions
diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c index 28ca8856..c75e0e34 100644 --- a/plugins/im3195/im3195.c +++ b/plugins/im3195/im3195.c @@ -51,12 +51,18 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("im3195") /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(errmsg) /* configuration settings */ + +struct modConfData_s { + EMPTY_STRUCT; +}; + static int listenPort = 601; /* we use a global API object below, because this listener is @@ -84,10 +90,37 @@ void OnReceive(srAPIObj __attribute__((unused)) *pMyAPI, srSLMGObj* pSLMG) srSLMGGetRawMSG(pSLMG, &pszRawMsg); parseAndSubmitMessage(fromHost, fromHostIP, pszRawMsg, strlen((char*)pszRawMsg), - PARSE_HOSTNAME, eFLOWCTL_FULL_DELAY, (uchar*)"im3195", NULL, 0); + PARSE_HOSTNAME, eFLOWCTL_FULL_DELAY, (uchar*)"im3195", NULL, 0, NULL); } +#if 0 +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf +#endif + + BEGINrunInput CODESTARTrunInput /* this is an endless loop - it is terminated when the thread is diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c index b3468921..6ea615a1 100644 --- a/plugins/imdiag/imdiag.c +++ b/plugins/imdiag/imdiag.c @@ -57,6 +57,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imdiag") /* static data */ DEF_IMOD_STATIC_DATA @@ -77,6 +78,10 @@ static prop_t *pRcvIPDummy = NULL; /* config settings */ +struct modConfData_s { + EMPTY_STRUCT; +}; + static int iTCPSessMax = 20; /* max number of sessions */ static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ @@ -376,7 +381,8 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa /* initialized, now add socket */ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ? UCHAR_CONSTANT("imdiag") : pszInputName)); - tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal); + /* we support octect-cuunted frame (constant 1 below) */ + tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal, 1); finalize_it: if(iRet != RS_RET_OK) { @@ -387,6 +393,33 @@ finalize_it: RETiRet; } + +#if 0 /* can be used to integrate into new config system */ +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf +#endif + /* This function is called to gather input. */ BEGINrunInput diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c index ba8318df..440f5991 100644 --- a/plugins/imfile/imfile.c +++ b/plugins/imfile/imfile.c @@ -51,6 +51,7 @@ MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imfile") /* defines */ @@ -63,6 +64,7 @@ DEFobjCurrIf(strm) DEFobjCurrIf(prop) DEFobjCurrIf(ruleset) +#define NUM_MULTISUB 1024 /* max number of submits -- TODO: make configurable */ typedef struct fileInfo_s { uchar *pszFileName; uchar *pszTag; @@ -70,11 +72,13 @@ typedef struct fileInfo_s { uchar *pszStateFile; /* file in which state between runs is to be stored */ int iFacility; int iSeverity; + int maxLinesAtOnce; int nRecords; /**< How many records did we process before persisting the stream? */ int iPersistStateInterval; /**< how often should state be persisted? (0=on close only) */ strm_t *pStrm; /* its stream (NULL if not assigned) */ int readMode; /* which mode to use in ReadMulteLine call? */ ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + multi_submit_t multiSub; } fileInfo_t; @@ -82,6 +86,10 @@ typedef struct fileInfo_s { static rsRetVal persistStrmState(fileInfo_t *pInfo); /* config variables */ +struct modConfData_s { + EMPTY_STRUCT; +}; + static uchar *pszFileName = NULL; static uchar *pszFileTag = NULL; static uchar *pszStateFile = NULL; @@ -90,6 +98,7 @@ static int iPersistStateInterval = 0; /* how often if state file to be persisted static int iFacility = 128; /* local0 */ static int iSeverity = 5; /* notice, as of rfc 3164 */ static int readMode = 0; /* mode to use for ReadMultiLine call */ +static int maxLinesAtOnce = 10240; /* how many lines to process in a row? */ static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */ static int iFilPtr = 0; /* number of files to be monitored; pointer to next free spot during config */ @@ -121,7 +130,9 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine) pMsg->iFacility = LOG_FAC(pInfo->iFacility); pMsg->iSeverity = LOG_PRI(pInfo->iSeverity); MsgSetRuleset(pMsg, pInfo->pRuleset); - CHKiRet(submitMsg(pMsg)); + pInfo->multiSub.ppMsgs[pInfo->multiSub.nElem++] = pMsg; + if(pInfo->multiSub.nElem == pInfo->multiSub.maxElem) + CHKiRet(multiSubmitMsg(&pInfo->multiSub)); finalize_it: RETiRet; } @@ -205,6 +216,7 @@ static void pollFileCancelCleanup(void *pArg) static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData) { cstr_t *pCStr = NULL; + int nProcessed = 0; DEFiRet; ASSERT(pbHadFileData != NULL); @@ -219,7 +231,10 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData) /* loop below will be exited when strmReadLine() returns EOF */ while(glbl.GetGlobalInputTermState() == 0) { + if(pThis->maxLinesAtOnce != 0 && nProcessed >= pThis->maxLinesAtOnce) + break; CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode)); + ++nProcessed; *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!) */ @@ -230,6 +245,10 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData) } finalize_it: + if(pThis->multiSub.nElem > 0) { + /* submit everything that was not yet submitted */ + CHKiRet(multiSubmitMsg(&pThis->multiSub)); + } ; /*EMPTY STATEMENT - needed to keep compiler happy - see below! */ /* Note: the problem above is that pthread:cleanup_pop() is a macro which * evaluates to something like "} while(0);". So the code would become @@ -321,6 +340,7 @@ ENDrunInput * ------------------------------------------------------------------------------------------ */ + /* 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) @@ -474,6 +494,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a iSeverity = 5; /* notice, as of rfc 3164 */ readMode = 0; pBindRuleset = NULL; + maxLinesAtOnce = 10240; RETiRet; } @@ -512,8 +533,12 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal) pThis->pszStateFile = (uchar*) strdup((char*) pszStateFile); } + CHKmalloc(pThis->multiSub.ppMsgs = MALLOC(NUM_MULTISUB * sizeof(msg_t*))); + pThis->multiSub.maxElem = NUM_MULTISUB; + pThis->multiSub.nElem = 0; pThis->iSeverity = iSeverity; pThis->iFacility = iFacility; + pThis->maxLinesAtOnce = maxLinesAtOnce; pThis->iPersistStateInterval = iPersistStateInterval; pThis->nRecords = 0; pThis->readMode = readMode; @@ -542,7 +567,7 @@ setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) rsRetVal localRet; DEFiRet; - localRet = ruleset.GetRuleset(&pRuleset, pszName); + localRet = ruleset.GetRuleset(ourConf, &pRuleset, pszName); if(localRet == RS_RET_NOT_FOUND) { errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName); } @@ -591,6 +616,8 @@ CODEmodInit_QueryRegCFSLineHdlr NULL, &iPollInterval, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilereadmode", 0, eCmdHdlrInt, NULL, &readMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilemaxlinesatonce", 0, eCmdHdlrSize, + NULL, &maxLinesAtOnce, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilepersiststateinterval", 0, eCmdHdlrInt, NULL, &iPersistStateInterval, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilebindruleset", 0, eCmdHdlrGetWord, diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c index 446795d6..4e3a70ab 100644 --- a/plugins/imgssapi/imgssapi.c +++ b/plugins/imgssapi/imgssapi.c @@ -63,6 +63,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imgssapi") /* defines */ #define ALLOWEDMETHOD_GSS 2 @@ -104,6 +105,10 @@ typedef struct gss_sess_s { /* config variables */ +struct modConfData_s { + EMPTY_STRUCT; +}; + 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? */ @@ -335,7 +340,7 @@ addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal) CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose)); CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose)); CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, UCHAR_CONSTANT("imgssapi"))); - tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal); + tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal, 1); CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv)); } @@ -640,6 +645,33 @@ TCPSessGSSDeinit(void) RETiRet; } + +#if 0 /* can be used to integrate into new config system */ +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf +#endif + /* This function is called to gather input. */ BEGINrunInput diff --git a/plugins/imklog/Makefile.am b/plugins/imklog/Makefile.am index 5d4d0465..7d0d37c9 100644 --- a/plugins/imklog/Makefile.am +++ b/plugins/imklog/Makefile.am @@ -1,5 +1,4 @@ pkglib_LTLIBRARIES = imklog.la - imklog_la_SOURCES = imklog.c imklog.h # select klog "driver" @@ -8,7 +7,7 @@ imklog_la_SOURCES += bsd.c endif if ENABLE_IMKLOG_LINUX -imklog_la_SOURCES += linux.c module.h ksym.c ksyms.h ksym_mod.c +imklog_la_SOURCES += bsd.c endif imklog_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c index 0a4c7cd4..80ff9494 100644 --- a/plugins/imklog/bsd.c +++ b/plugins/imklog/bsd.c @@ -1,69 +1,30 @@ -/* klog for BSD, based on the FreeBSD syslogd implementation. +/* combined imklog driver for BSD and Linux * * This contains OS-specific functionality to read the BSD - * kernel log. For a general overview, see head comment in - * imklog.c. + * or Linux kernel log. For a general overview, see head comment in + * imklog.c. This started out as the BSD-specific drivers, but it + * turned out that on modern Linux the implementation details + * are very small, and so we use a single driver for both OS's with + * a little help of conditional compilation. * - * Copyright (C) 2008 by Rainer Gerhards for the modifications of - * the original FreeBSD sources. - * - * I would like to express my gratitude to those folks which - * layed an important foundation for rsyslog to build on. + * Copyright 2008-2012 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. - * - * This file is based on earlier work included in the FreeBSD sources. We - * integrated it into the rsyslog project. The copyright below applies, and - * I also reproduce the original license under which we aquired the code: - * - * Copyright (c) 1983, 1988, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * If you would like to use the code under the BSD license, you should - * aquire your own copy of BSD's syslogd, from which we have taken it. The - * code in this file is modified and may only be used under the terms of - * the GPLv3+ as specified above. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ - #ifdef HAVE_CONFIG_H # include "config.h" #endif @@ -72,46 +33,165 @@ #include <fcntl.h> #include <errno.h> #include <string.h> +#include <ctype.h> +#ifdef OS_LINUX +# include <sys/klog.h> +#endif #include "rsyslog.h" -#include "imklog.h" +#include "srUtils.h" #include "debug.h" +#include "imklog.h" /* globals */ -static int fklog = -1; /* /dev/klog */ +static int fklog = -1; /* kernel log fd */ #ifndef _PATH_KLOG -# define _PATH_KLOG "/dev/klog" +# ifdef OS_LINUX +# define _PATH_KLOG "/proc/kmsg" +# else +# define _PATH_KLOG "/dev/klog" +# endif #endif -static uchar *GetPath(void) + +#ifdef OS_LINUX +/* submit a message to imklog Syslog() API. In this function, we check if + * a kernel timestamp is present and, if so, extract and strip it. + * Note: this is an extra processing step. We should revisit the whole + * idea in v6 and remove all that old stuff that we do not longer need + * (like symbol resolution). <-- TODO + * Note that this is heavily Linux specific and thus is not compiled or + * used for BSD. + * Special thanks to Lennart Poettering for suggesting on how to convert + * the kernel timestamp to a realtime timestamp. This method depends on + * the fact the the kernel timestamp is written using the monotonic clock. + * Shall that change (very unlikely), this code must be changed as well. Note + * that due to the way we generate the delta, we are unable to write the + * absolutely correct timestamp (system call overhead of the clock calls + * prevents us from doing so). However, the difference is very minor. + * rgerhards, 2011-06-24 + */ +static void +submitSyslog(int pri, uchar *buf) +{ + long secs; + long nsecs; + long secOffs; + long nsecOffs; + unsigned i; + unsigned bufsize; + struct timespec monotonic, realtime; + struct timeval tv; + struct timeval *tp = NULL; + + if(buf[3] != '[') + goto done; + DBGPRINTF("imklog: kernel timestamp detected, extracting it\n"); + + /* we now try to parse the timestamp. iff it parses, we assume + * it is a timestamp. Otherwise we know for sure it is no ts ;) + */ + i = 4; /* first digit after '[' */ + secs = 0; + while(buf[i] && isdigit(buf[i])) { + secs = secs * 10 + buf[i] - '0'; + ++i; + } + if(buf[i] != '.') { + DBGPRINTF("no dot --> no kernel timestamp\n"); + goto done; /* no TS! */ + } + + ++i; /* skip dot */ + nsecs = 0; + while(buf[i] && isdigit(buf[i])) { + nsecs = nsecs * 10 + buf[i] - '0'; + ++i; + } + if(buf[i] != ']') { + DBGPRINTF("no trailing ']' --> no kernel timestamp\n"); + goto done; /* no TS! */ + } + ++i; /* skip ']' */ + + /* we have a timestamp */ + DBGPRINTF("kernel timestamp is %ld %ld\n", secs, nsecs); + bufsize= strlen((char*)buf); + memcpy(buf+3, buf+i, bufsize - i + 1); + + clock_gettime(CLOCK_MONOTONIC, &monotonic); + clock_gettime(CLOCK_REALTIME, &realtime); + secOffs = realtime.tv_sec - monotonic.tv_sec; + nsecOffs = realtime.tv_nsec - monotonic.tv_nsec; + if(nsecOffs < 0) { + secOffs--; + nsecOffs += 1000000000l; + } + + nsecs +=nsecOffs; + if(nsecs > 999999999l) { + secs++; + nsecs -= 1000000000l; + } + secs += secOffs; + tv.tv_sec = secs; + tv.tv_usec = nsecs / 1000; + tp = &tv; + +done: + Syslog(pri, buf, tp); +} +#else /* now comes the BSD "code" (just a shim) */ +static void +submitSyslog(int pri, uchar *buf) { - return pszPath ? pszPath : (uchar*) _PATH_KLOG; + Syslog(pri, buf, NULL); +} +#endif /* #ifdef LINUX */ + + +static uchar *GetPath(modConfData_t *pModConf) +{ + return pModConf->pszPath ? pModConf->pszPath : (uchar*) _PATH_KLOG; } /* open the kernel log - will be called inside the willRun() imklog * entry point. -- rgerhards, 2008-04-09 */ rsRetVal -klogWillRun(void) +klogWillRun(modConfData_t *pModConf) { + char errmsg[2048]; + int r; DEFiRet; - fklog = open((char*)GetPath(), O_RDONLY, 0); + fklog = open((char*)GetPath(pModConf), O_RDONLY, 0); if (fklog < 0) { - dbgprintf("can't open %s (%d)\n", GetPath(), errno); - iRet = RS_RET_ERR; // TODO: better error code + imklogLogIntMsg(RS_RET_ERR_OPEN_KLOG, "imklog: cannot open kernel log(%s): %s.", + GetPath(pModConf), rs_strerror_r(errno, errmsg, sizeof(errmsg))); + ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG); + } + +# ifdef OS_LINUX + /* Set level of kernel console messaging.. */ + if(pModConf->console_log_level != -1) { + r = klogctl(8, NULL, pModConf->console_log_level); + if(r != 0) { + imklogLogIntMsg(LOG_WARNING, "imklog: cannot set console log level: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); + /* make sure we do not try to re-set! */ + pModConf->console_log_level = -1; + } } +# endif /* #ifdef OS_LINUX */ +finalize_it: RETiRet; } -/* Read /dev/klog while data are available, split into lines. - * Contrary to standard BSD syslogd, we do a blocking read. We can - * afford this as imklog is running on its own threads. So if we have - * a single file, it really doesn't matter if we wait inside a 1-file - * select or the read() directly. +/* Read kernel log while data are available, split into lines. */ static void readklog(void) @@ -119,13 +199,14 @@ readklog(void) char *p, *q; int len, i; int iMaxLine; - uchar bufRcv[4096+1]; + uchar bufRcv[128*1024+1]; + char errmsg[2048]; uchar *pRcv = NULL; /* receive buffer */ iMaxLine = klog_getMaxLine(); - /* we optimize performance: if iMaxLine is below 4K (which it is in almost all - * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory + /* we optimize performance: if iMaxLine is below our fixed size buffer (which + * usually is sufficiently large), we use this buffer. if it is higher, heap memory * is used. We could use alloca() to achive a similar aspect, but there are so * many issues with alloca() that I do not want to take that route. * rgerhards, 2008-09-02 @@ -139,15 +220,15 @@ readklog(void) len = 0; for (;;) { - dbgprintf("----------imklog(BSD) waiting for kernel log line\n"); + dbgprintf("imklog(BSD/Linux) waiting for kernel log line\n"); i = read(fklog, pRcv + len, iMaxLine - len); if (i > 0) { pRcv[i + len] = '\0'; } else { if (i < 0 && errno != EINTR && errno != EAGAIN) { imklogLogIntMsg(LOG_ERR, - "imklog error %d reading kernel log - shutting down imklog", - errno); + "imklog: error reading kernel log - shutting down: %s", + rs_strerror_r(errno, errmsg, sizeof(errmsg))); fklog = -1; } break; @@ -155,18 +236,18 @@ readklog(void) for (p = (char*)pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) { *q = '\0'; - Syslog(LOG_INFO, (uchar*) p); + submitSyslog(LOG_INFO, (uchar*) p); } len = strlen(p); if (len >= iMaxLine - 1) { - Syslog(LOG_INFO, (uchar*)p); + submitSyslog(LOG_INFO, (uchar*)p); len = 0; } - if (len > 0) + if(len > 0) memmove(pRcv, p, len + 1); } if (len > 0) - Syslog(LOG_INFO, pRcv); + submitSyslog(LOG_INFO, pRcv); if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) free(pRcv); @@ -176,11 +257,16 @@ readklog(void) /* to be called in the module's AfterRun entry point * rgerhards, 2008-04-09 */ -rsRetVal klogAfterRun(void) +rsRetVal klogAfterRun(modConfData_t *pModConf) { DEFiRet; if(fklog != -1) close(fklog); +# ifdef OS_LINUX + /* Turn on logging of messages to console, but only if a log level was speficied */ + if(pModConf->console_log_level != -1) + klogctl(7, NULL, 0); +# endif RETiRet; } @@ -190,7 +276,7 @@ rsRetVal klogAfterRun(void) * "message pull" mechanism. * rgerhards, 2008-04-09 */ -rsRetVal klogLogKMsg(void) +rsRetVal klogLogKMsg(modConfData_t __attribute__((unused)) *pModConf) { DEFiRet; readklog(); diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c index 16adbc21..f476c5ff 100644 --- a/plugins/imklog/imklog.c +++ b/plugins/imklog/imklog.c @@ -18,6 +18,9 @@ * Please note that this file replaces the klogd daemon that was * also present in pre-v3 versions of rsyslog. * + * To test under Linux: + * echo test1 > /dev/kmsg + * * Copyright (C) 2008-2012 Adiscon GmbH * * This file is part of rsyslog. @@ -44,6 +47,7 @@ #include <stdarg.h> #include <ctype.h> #include <stdlib.h> +#include <sys/socket.h> #include "dirty.h" #include "cfsysline.h" @@ -52,55 +56,78 @@ #include "module-template.h" #include "datetime.h" #include "imklog.h" +#include "net.h" #include "glbl.h" #include "prop.h" #include "unicode-helper.h" MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imklog") /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(datetime) DEFobjCurrIf(glbl) DEFobjCurrIf(prop) +DEFobjCurrIf(net) -/* configuration settings */ -int dbgPrintSymbols = 0; /* this one is extern so the helpers can access it! */ -int symbols_twice = 0; -int use_syscall = 0; -int symbol_lookup = 0; /* on recent kernels > 2.6, the kernel does this */ -int bPermitNonKernel = 0; /* permit logging of messages not having LOG_KERN facility */ -int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */ -uchar *pszPath = NULL; -int console_log_level = -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 - */ -char *symfile = NULL; +/* config settings */ +typedef struct configSettings_s { + int dbgPrintSymbols; /* this one is extern so the helpers can access it! */ + int symbols_twice; + int use_syscall; + int symbol_lookup; /* on recent kernels > 2.6, the kernel does this */ + int bPermitNonKernel; /* permit logging of messages not having LOG_KERN facility */ + int iFacilIntMsg; /* the facility to use for internal messages (set by driver) */ + uchar *pszPath; + int console_log_level; +} configSettings_t; +static configSettings_t cs; + +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */ static prop_t *pLocalHostIP = NULL; /* a pseudo-constant propterty for 127.0.0.1 */ + +static inline void +initConfigSettings(void) +{ + cs.dbgPrintSymbols = 0; + cs.symbols_twice = 0; + cs.use_syscall = 0; + cs.symbol_lookup = 0; + cs.bPermitNonKernel = 0; + cs.console_log_level = -1; + cs.pszPath = NULL; + cs.iFacilIntMsg = klogFacilIntMsg(); +} + + /* enqueue the the kernel message into the message queue. * The provided msg string is not freed - thus must be done * by the caller. * rgerhards, 2008-04-12 */ static rsRetVal -enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity) +enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity, struct timeval *tp) { - DEFiRet; + struct syslogTime st; msg_t *pMsg; + DEFiRet; assert(msg != NULL); assert(pszTag != NULL); - CHKiRet(msgConstruct(&pMsg)); + if(tp == NULL) { + CHKiRet(msgConstruct(&pMsg)); + } else { + datetime.timeval2syslogTime(tp, &st); + CHKiRet(msgConstructWithTime(&pMsg, &st, tp->tv_sec)); + } MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY); MsgSetInputName(pMsg, pInputName); MsgSetRawMsgWOSize(pMsg, (char*)msg); @@ -165,39 +192,52 @@ rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) DEFiRet; va_list ap; uchar msgBuf[2048]; /* we use the same size as sysklogd to remain compatible */ - uchar *pLogMsg; va_start(ap, fmt); vsnprintf((char*)msgBuf, sizeof(msgBuf) / sizeof(char), fmt, ap); - pLogMsg = msgBuf; va_end(ap); - iRet = enqMsg((uchar*)pLogMsg, (uchar*) ((iFacilIntMsg == LOG_KERN) ? "kernel:" : "imklog:"), - iFacilIntMsg, LOG_PRI(priority)); + logmsgInternal(NO_ERRCODE ,priority, msgBuf, 0); RETiRet; } -/* log a kernel message +/* log a kernel message. If tp is non-NULL, it contains the message creation + * time to use. * rgerhards, 2008-04-14 */ -rsRetVal Syslog(int priority, uchar *pMsg) +rsRetVal Syslog(int priority, uchar *pMsg, struct timeval *tp) { - DEFiRet; + int pri = -1; rsRetVal localRet; + DEFiRet; - /* Output using syslog */ - localRet = parsePRI(&pMsg, &priority); - if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK) - FINALIZE; + /* then check if we have two PRIs. This can happen in case of systemd, + * in which case the second PRI is the right one. + */ + if(pMsg[3] == '<' || (pMsg[3] == ' ' && pMsg[4] == '<')) { /* could be a pri... */ + uchar *pMsgTmp = pMsg + ((pMsg[3] == '<') ? 3 : 4); + localRet = parsePRI(&pMsgTmp, &pri); + if(localRet == RS_RET_OK && pri >= 8 && pri <= 192) { + /* *this* is our PRI */ + DBGPRINTF("imklog detected secondary PRI(%d) in klog msg\n", pri); + pMsg = pMsgTmp; + priority = pri; + } + } + if(pri == -1) { + localRet = parsePRI(&pMsg, &priority); + if(localRet != RS_RET_INVALID_PRI && localRet != RS_RET_OK) + FINALIZE; + } /* if we don't get the pri, we use whatever we were supplied */ /* ignore non-kernel messages if not permitted */ - if(bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN) + if(cs.bPermitNonKernel == 0 && LOG_FAC(priority) != LOG_KERN) FINALIZE; /* silently ignore */ - iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority)); + iRet = enqMsg((uchar*)pMsg, (uchar*) "kernel:", LOG_FAC(priority), LOG_PRI(priority), tp); finalize_it: RETiRet; @@ -226,63 +266,110 @@ CODESTARTrunInput * and then submits it to the rsyslog main queue. * rgerhards, 2008-04-09 */ - CHKiRet(klogLogKMsg()); + CHKiRet(klogLogKMsg(runModConf)); } finalize_it: ENDrunInput +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config vars */ + initConfigSettings(); +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + loadModConf->dbgPrintSymbols = cs.dbgPrintSymbols; + loadModConf->symbols_twice = cs.symbols_twice; + loadModConf->use_syscall = cs.use_syscall; + loadModConf->bPermitNonKernel = cs.bPermitNonKernel; + loadModConf->iFacilIntMsg = cs.iFacilIntMsg; + loadModConf->console_log_level = cs.console_log_level; + if((cs.pszPath == NULL) || (cs.pszPath[0] == '\0')) { + loadModConf->pszPath = NULL; + if(cs.pszPath != NULL) + free(cs.pszPath); + } else { + loadModConf->pszPath = cs.pszPath; + } + cs.pszPath = NULL; + + loadModConf = NULL; /* done loading */ +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; + iRet = klogWillRun(runModConf); +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + + BEGINwillRun CODESTARTwillRun - /* we need to create the inputName property (only once during our lifetime) */ - CHKiRet(prop.CreateStringProp(&pInputName, UCHAR_CONSTANT("imklog"), sizeof("imklog") - 1)); - CHKiRet(prop.CreateStringProp(&pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1)); - - iRet = klogWillRun(); -finalize_it: ENDwillRun BEGINafterRun CODESTARTafterRun - iRet = klogAfterRun(); + iRet = klogAfterRun(runModConf); +ENDafterRun + +BEGINmodExit +CODESTARTmodExit if(pInputName != NULL) prop.Destruct(&pInputName); if(pLocalHostIP != NULL) prop.Destruct(&pLocalHostIP); -ENDafterRun - -BEGINmodExit -CODESTARTmodExit /* release objects we used */ objRelease(glbl, CORE_COMPONENT); + objRelease(net, CORE_COMPONENT); objRelease(datetime, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); - if(pszPath != NULL) - free(pszPath); ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_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 = 0; - bPermitNonKernel = 0; - if(pszPath != NULL) { - free(pszPath); - pszPath = NULL; + cs.dbgPrintSymbols = 0; + cs.symbols_twice = 0; + cs.use_syscall = 0; + cs.symbol_lookup = 0; + cs.bPermitNonKernel = 0; + if(cs.pszPath != NULL) { + free(cs.pszPath); + cs.pszPath = NULL; } - iFacilIntMsg = klogFacilIntMsg(); + cs.iFacilIntMsg = klogFacilIntMsg(); return RS_RET_OK; } @@ -293,18 +380,33 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); + CHKiRet(objUse(net, CORE_COMPONENT)); + + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.CreateStringProp(&pInputName, UCHAR_CONSTANT("imklog"), sizeof("imklog") - 1)); + CHKiRet(prop.CreateStringProp(&pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1)); - iFacilIntMsg = klogFacilIntMsg(); - - CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, NULL, &dbgPrintSymbols, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpath", 0, eCmdHdlrGetWord, NULL, &pszPath, 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 *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary, NULL, &bPermitNonKernel, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt, NULL, &console_log_level, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility, NULL, &iFacilIntMsg, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + /* init legacy config settings */ + initConfigSettings(); + + CHKiRet(omsdRegCFSLineHdlr((uchar *)"debugprintkernelsymbols", 0, eCmdHdlrBinary, + NULL, &cs.dbgPrintSymbols, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpath", 0, eCmdHdlrGetWord, + NULL, &cs.pszPath, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbollookup", 0, eCmdHdlrBinary, + NULL, &cs.symbol_lookup, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogsymbolstwice", 0, eCmdHdlrBinary, + NULL, &cs.symbols_twice, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogusesyscallinterface", 0, eCmdHdlrBinary, + NULL, &cs.use_syscall, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogpermitnonkernelfacility", 0, eCmdHdlrBinary, + NULL, &cs.bPermitNonKernel, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"klogconsoleloglevel", 0, eCmdHdlrInt, + NULL, &cs.console_log_level, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"kloginternalmsgfacility", 0, eCmdHdlrFacility, + NULL, &cs.iFacilIntMsg, 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/imklog.h b/plugins/imklog/imklog.h index 22f10053..795dd68c 100644 --- a/plugins/imklog/imklog.h +++ b/plugins/imklog/imklog.h @@ -29,37 +29,37 @@ #include "rsyslog.h" #include "dirty.h" +/* we need to have the modConf type present in all submodules */ +struct modConfData_s { + int dbgPrintSymbols; + int symbols_twice; + int use_syscall; + int symbol_lookup; + int bPermitNonKernel; + int iFacilIntMsg; + uchar *pszPath; + int console_log_level; + rsconf_t *pConf; +}; + /* interface to "drivers" * the platform specific drivers must implement these entry points. Only one * driver may be active at any given time, thus we simply rely on the linker * to resolve the addresses. * rgerhards, 2008-04-09 */ -rsRetVal klogLogKMsg(void); -rsRetVal klogWillRun(void); -rsRetVal klogAfterRun(void); -int klogFacilIntMsg(void); - -/* the following data members may be accessed by the "drivers" - * I admit this is not the cleanest way to doing things, but I honestly - * believe it is appropriate for the job that needs to be done. - * rgerhards, 2008-04-09 - */ -extern int symbols_twice; -extern int use_syscall; -extern int symbol_lookup; -extern char *symfile; -extern int console_log_level; -extern int dbgPrintSymbols; -extern uchar *pszPath; +rsRetVal klogLogKMsg(modConfData_t *pModConf); +rsRetVal klogWillRun(modConfData_t *pModConf); +rsRetVal klogAfterRun(modConfData_t *pModConf); +int klogFacilIntMsg(); /* the functions below may be called by the drivers */ rsRetVal imklogLogIntMsg(int priority, char *fmt, ...) __attribute__((format(printf,2, 3))); -rsRetVal Syslog(int priority, uchar *msg); +rsRetVal Syslog(int priority, uchar *msg, struct timeval *tp); /* prototypes */ extern int klog_getMaxLine(void); /* work-around for klog drivers to get configured max line size */ -extern int InitKsyms(char *); +extern int InitKsyms(modConfData_t*); extern void DeinitKsyms(void); extern int InitMsyms(void); extern void DeinitMsyms(void); diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c deleted file mode 100644 index ebaec011..00000000 --- a/plugins/imklog/ksym.c +++ /dev/null @@ -1,832 +0,0 @@ -/* 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" -#include "debug.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; - - BEGINfunc - - /* 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 != NULL ) { - if ( (sym_file = fopen(mapfile, "r")) == NULL ) - { - imklogLogIntMsg(LOG_WARNING, "Cannot open map file: %s.", mapfile); - return(0); - } - } else { - if ( (mapfile = FindSymbolFile()) == NULL ) { - imklogLogIntMsg(LOG_WARNING, "Cannot find map file."); - dbgprintf("Cannot find map file.\n"); - return(0); - } - - if ( (sym_file = fopen(mapfile, "r")) == NULL ) { - imklogLogIntMsg(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 ) { - imklogLogIntMsg(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 ) { - imklogLogIntMsg(LOG_ERR, "Error adding symbol - %s.", sym); - fclose(sym_file); - return(0); - } - - if ( version == 0 ) - version = CheckVersion(sym); - } - - - imklogLogIntMsg(LOG_INFO, "Loaded %d symbols from %s.", num_syms, mapfile); - switch(version) { - case -1: - imklogLogIntMsg(LOG_WARNING, "Symbols do not match kernel version."); - num_syms = 0; - break; - - case 0: - imklogLogIntMsg(LOG_WARNING, "Cannot verify that symbols match kernel version."); - break; - - case 1: - imklogLogIntMsg(LOG_INFO, "Symbols match kernel version %s.", vstring); - break; - } - - fclose(sym_file); - ENDfunc - 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 = NULL, - **mf = system_maps; - auto struct utsname utsname; - static char mysymfile[100]; - auto FILE *sym_file = NULL; - BEGINfunc - - if(uname(&utsname) < 0) { - imklogLogIntMsg(LOG_ERR, "Cannot get kernel version information."); - return(0); - } - - dbgprintf("Searching for symbol map.\n"); - - for(mf = system_maps; *mf != NULL && file == NULL; ++mf) { - snprintf(mysymfile, sizeof(mysymfile), "%s-%s", *mf, utsname.release); - dbgprintf("Trying %s.\n", mysymfile); - if((sym_file = fopen(mysymfile, "r")) != NULL) { - if(CheckMapVersion(mysymfile) == 1) - file = mysymfile; - fclose(sym_file); - } - if(sym_file == NULL || file == NULL) { - sprintf (mysymfile, "%s", *mf); - dbgprintf("Trying %s.\n", mysymfile); - if((sym_file = fopen(mysymfile, "r")) != NULL ) { - if (CheckMapVersion(mysymfile) == 1) - file = mysymfile; - 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"); - ENDfunc - 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 ) { - imklogLogIntMsg(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 ) { - imklogLogIntMsg(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")) != NULL ) { - /* - * At this point a map file was successfully opened. We - * now need to search this file and look for version - * information. - */ - imklogLogIntMsg(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 ) { - imklogLogIntMsg(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: - imklogLogIntMsg(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 == NULL ) - 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(NULL); - - last = sym_array[0].name; - ksym.offset = 0; - ksym.size = 0; - if ( value < sym_array[0].value ) - return(NULL); - - 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(NULL); - } - - 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(NULL); -} - -/************************************************************************** - * 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 *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:") != NULL) && !InitMsyms() ) - imklogLogIntMsg(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, "[<")) == NULL ) { -#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, ">]")) == NULL ) { - strcpy(el, sl); - return(el); - } - strncpy(num,sl+1,kp-sl-1); - num[kp-sl-1] = '\0'; - value = strtoul(num, (char **) 0, 16); - if ( (symbol = LookupSymbol(value, &sym)) == NULL ) - 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, "[<")) == NULL ) - strcat(elp, sl); - } - while ( kp != NULL); - - 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 deleted file mode 100644 index 82978892..00000000 --- a/plugins/imklog/ksym_mod.c +++ /dev/null @@ -1,485 +0,0 @@ -/* 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-2009 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" -#include "debug.h" - -#define KSYMS "/proc/kallsyms" - -static int num_modules = 0; -struct Module *sym_array_modules = (struct Module *) NULL; - -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 ) - imklogLogIntMsg(LOG_INFO, "No module symbols loaded - " - "kernel modules not enabled.\n"); - else - imklogLogIntMsg(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 ) - imklogLogIntMsg(LOG_INFO, "No module symbols loaded."); - else - imklogLogIntMsg(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 *) NULL; - 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 ) - { - imklogLogIntMsg(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 ) - { - imklogLogIntMsg(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 *) NULL ) - return(0); - - mp->sym_array[mp->num_syms].name = strdup(p); - if ( mp->sym_array[mp->num_syms].name == (char *) NULL ) - 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. - * - * TODO: We are using int values for the offset, but longs for the value - * values. This may create some trouble in the future (on 64 Bit OS?). - * Anyhow, I have not changed this, because we do not seem to have any - * issue and my understanding of this code is limited (and I don't see - * need to invest more time to dig much deeper). - * rgerhards, 2009-04-17 - **************************************************************************/ -extern char * LookupModuleSymbol(value, sym) - unsigned long value; - struct symbol *sym; -{ - int nmod, nsym; - struct sym_table *last; - 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 || - (int) (value - last->value) < sym->offset || - ( (sym->offset == (int) (value - last->value)) && - (int) (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(NULL); -} diff --git a/plugins/imklog/ksyms.h b/plugins/imklog/ksyms.h deleted file mode 100644 index a168947b..00000000 --- a/plugins/imklog/ksyms.h +++ /dev/null @@ -1,37 +0,0 @@ -/* 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-2009 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 -{ - uchar *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/linux.c b/plugins/imklog/linux.c deleted file mode 100644 index 727708a5..00000000 --- a/plugins/imklog/linux.c +++ /dev/null @@ -1,542 +0,0 @@ -/* klog for linux, based on the FreeBSD syslogd implementation. - * - * This contains OS-specific functionality to read the BSD - * kernel log. For a general overview, see head comment in - * imklog.c. - * - * This file heavily borrows from the klogd daemon provided by - * the sysklogd project. Many thanks for this piece of software. - * - * 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 "cfsysline.h" -#include "template.h" -#include "msg.h" -#include "module-template.h" -#include "imklog.h" -#include "unicode-helper.h" - - -/* 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); - - -static uchar *GetPath(void) -{ - return pszPath ? pszPath : UCHAR_CONSTANT(_PATH_KLOG); -} - -static void CloseLogSrc(void) -{ - /* Turn on logging of messages to console, but only if a log level was speficied */ - if(console_log_level != -1) - ksyslog(7, NULL, 0); - - /* Shutdown the log sources. */ - switch(logsrc) { - case kernel: - ksyslog(0, NULL, 0); - imklogLogIntMsg(LOG_INFO, "Kernel logging (ksyslog) stopped."); - break; - case proc: - close(kmsg); - imklogLogIntMsg(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. - */ - imklogLogIntMsg(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((char*)GetPath(), &sb) < 0) && (errno == ENOENT)) ) - { - /* Initialize kernel logging. */ - ksyslog(1, NULL, 0); - imklogLogIntMsg(LOG_INFO, "imklog %s, log source = ksyslog " - "started.", VERSION); - return(kernel); - } - - if ( (kmsg = open((char*)GetPath(), O_RDONLY|O_CLOEXEC)) < 0 ) - { - imklogLogIntMsg(LOG_ERR, "imklog: Cannot open proc file system, %d.\n", errno); - ksyslog(7, NULL, 0); - return(none); - } - - imklogLogIntMsg(LOG_INFO, "imklog %s, log source = %s started.", VERSION, GetPath()); - 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( uchar *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 uchar line_buff[LOG_LINE_LENGTH]; - - static uchar *line =line_buff; - static enum parse_state_enum parse_state = PARSING_TEXT; - static int space = sizeof(line_buff)-1; - - static uchar *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, 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, 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((char*)(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; - } - - // TODO: sprintf!!!! - delta = sprintf( (char*) 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 ) - { - if(errno == EINTR) - return; - imklogLogIntMsg(LOG_ERR, "imklog Error return from sys_sycall: %d\n", errno); - } - 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; - imklogLogIntMsg(LOG_ERR, "Cannot read proc file system: %d - %s.", errno, strerror(errno)); - } else { - LogLine(log_buffer, rdcnt); - } - - return; -} - - -/* to be called in the module's WillRun entry point - * rgerhards, 2008-04-09 - */ -rsRetVal klogLogKMsg(void) -{ - DEFiRet; - 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; -} - - -/* to be called in the module's WillRun entry point - * rgerhards, 2008-04-09 - */ -rsRetVal klogWillRun(void) -{ - DEFiRet; - /* 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) { - imklogLogIntMsg(LOG_WARNING, "cannot find any symbols, turning off symbol lookups"); - } - } - } - - RETiRet; -} - - -/* to be called in the module's AfterRun entry point - * rgerhards, 2008-04-09 - */ -rsRetVal klogAfterRun(void) -{ - DEFiRet; - /* cleanup here */ - if(logsrc != none) - CloseLogSrc(); - - DeinitKsyms(); - DeinitMsyms(); - - RETiRet; -} - - -/* provide the (system-specific) default facility for internal messages - * rgerhards, 2008-04-14 - */ -int -klogFacilIntMsg(void) -{ - return LOG_KERN; -} - - -/* vi:set ai: - */ diff --git a/plugins/imklog/module.h b/plugins/imklog/module.h deleted file mode 100644 index 38a26fea..00000000 --- a/plugins/imklog/module.h +++ /dev/null @@ -1,35 +0,0 @@ -/* 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/imklog/solaris.c b/plugins/imklog/solaris.c index 8a6d5af1..0a169cdd 100644 --- a/plugins/imklog/solaris.c +++ b/plugins/imklog/solaris.c @@ -80,74 +80,6 @@ klogWillRun(void) } -#if 0 -/* Read /dev/klog while data are available, split into lines. - * Contrary to standard BSD syslogd, we do a blocking read. We can - * afford this as imklog is running on its own threads. So if we have - * a single file, it really doesn't matter if we wait inside a 1-file - * select or the read() directly. - */ -static void -readklog(void) -{ - char *p, *q; - int len, i; - int iMaxLine; - uchar bufRcv[4096+1]; - uchar *pRcv = NULL; /* receive buffer */ - - iMaxLine = klog_getMaxLine(); - - /* we optimize performance: if iMaxLine is below 4K (which it is in almost all - * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory - * is used. We could use alloca() to achive a similar aspect, but there are so - * many issues with alloca() that I do not want to take that route. - * rgerhards, 2008-09-02 - */ - if((size_t) iMaxLine < sizeof(bufRcv) - 1) { - pRcv = bufRcv; - } else { - if((pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))) == NULL) - iMaxLine = sizeof(bufRcv) - 1; /* better this than noting */ - } - - len = 0; - for (;;) { - dbgprintf("----------imklog(BSD) waiting for kernel log line\n"); - i = read(fklog, pRcv + len, iMaxLine - len); - if (i > 0) { - pRcv[i + len] = '\0'; - } else { - if (i < 0 && errno != EINTR && errno != EAGAIN) { - imklogLogIntMsg(LOG_ERR, - "imklog error %d reading kernel log - shutting down imklog", - errno); - fklog = -1; - } - break; - } - - for(p = pRcv; (q = strchr(p, '\n')) != NULL; p = q + 1) { - *q = '\0'; - Syslog(LOG_INFO, (uchar*) p); - } - len = strlen(p); - if (len >= iMaxLine - 1) { - Syslog(LOG_INFO, (uchar*)p); - len = 0; - } - if (len > 0) - memmove(pRcv, p, len + 1); - } - if (len > 0) - Syslog(LOG_INFO, pRcv); - - if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) - free(pRcv); -} -#endif - - /* to be called in the module's AfterRun entry point * rgerhards, 2008-04-09 */ diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c index 358b3b18..273af021 100644 --- a/plugins/immark/immark.c +++ b/plugins/immark/immark.c @@ -46,6 +46,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("immark") /* defines */ #define DEFAULT_MARK_PERIOD (20 * 60) @@ -53,7 +54,12 @@ MODULE_TYPE_NOKEEP /* Module static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(glbl) +DEFobjCurrIf(errmsg) + static int iMarkMessagePeriod = DEFAULT_MARK_PERIOD; +struct modConfData_s { + int iMarkMessagePeriod; +}; BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature @@ -62,6 +68,43 @@ CODESTARTisCompatibleWithFeature ENDisCompatibleWithFeature +BEGINafterRun +CODESTARTafterRun +ENDafterRun + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + pModConf->iMarkMessagePeriod = iMarkMessagePeriod; +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf + if(pModConf->iMarkMessagePeriod == 0) { + errmsg.LogError(0, NO_ERRCODE, "immark: mark message period must not be 0, can not run"); + ABORT_FINALIZE(RS_RET_NO_RUN); /* we can not run with this error */ + } +finalize_it: +ENDcheckCnf + + +BEGINactivateCnf +CODESTARTactivateCnf + MarkInterval = pModConf->iMarkMessagePeriod; +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + + /* 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) @@ -81,7 +124,7 @@ CODESTARTrunInput * right into the sleep below. */ while(1) { - srSleep(iMarkMessagePeriod, 0); /* seconds, micro seconds */ + srSleep(MarkInterval, 0); /* seconds, micro seconds */ if(glbl.GetGlobalInputTermState() == 1) break; /* terminate input! */ @@ -94,33 +137,25 @@ ENDrunInput BEGINwillRun CODESTARTwillRun - /* We set the global MarkInterval to what is configured here -- rgerhards, 2008-07-15 */ - MarkInterval = iMarkMessagePeriod; - if(iMarkMessagePeriod == 0) - iRet = RS_RET_NO_RUN; ENDwillRun -BEGINafterRun -CODESTARTafterRun -ENDafterRun - - BEGINmodExit CODESTARTmodExit + objRelease(errmsg, CORE_COMPONENT); ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { iMarkMessagePeriod = DEFAULT_MARK_PERIOD; - return RS_RET_OK; } @@ -129,8 +164,13 @@ CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(glbl, CORE_COMPONENT)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"markmessageperiod", 0, eCmdHdlrInt, NULL, &iMarkMessagePeriod, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + /* legacy config handlers */ + 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/impstats/impstats.c b/plugins/impstats/impstats.c index 3012136c..4fec8e70 100644 --- a/plugins/impstats/impstats.c +++ b/plugins/impstats/impstats.c @@ -40,6 +40,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("impstats") /* defines */ #define DEFAULT_STATS_PERIOD (5 * 60) @@ -57,12 +58,23 @@ typedef struct configSettings_s { int iStatsInterval; int iFacility; int iSeverity; + int bJSON; } configSettings_t; +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + int iStatsInterval; + int iFacility; + int iSeverity; + statsFmtType_t statsFmt; +}; +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + + static configSettings_t cs; static prop_t *pInputName = NULL; -static prop_t *pLocalHostIP = NULL; BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature @@ -76,6 +88,7 @@ initConfigSettings(void) cs.iStatsInterval = DEFAULT_STATS_PERIOD; cs.iFacility = DEFAULT_FACILITY; cs.iSeverity = DEFAULT_SEVERITY; + cs.bJSON = 0; } @@ -92,11 +105,11 @@ doSubmitMsg(uchar *line) MsgSetRawMsgWOSize(pMsg, (char*)line); MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())); MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp()); - MsgSetRcvFromIP(pMsg, pLocalHostIP); + MsgSetRcvFromIP(pMsg, glbl.GetLocalHostIP()); MsgSetMSGoffs(pMsg, 0); MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd-pstats:"), sizeof("rsyslogd-pstats:") - 1); - pMsg->iFacility = cs.iFacility; - pMsg->iSeverity = cs.iSeverity; + pMsg->iFacility = runModConf->iFacility; + pMsg->iSeverity = runModConf->iSeverity; pMsg->msgFlags = 0; submitMsg(pMsg); @@ -125,10 +138,58 @@ doStatsLine(void __attribute__((unused)) *usrptr, cstr_t *cstr) static inline void generateStatsMsgs(void) { - statsobj.GetAllStatsLines(doStatsLine, NULL); + statsobj.GetAllStatsLines(doStatsLine, NULL, runModConf->statsFmt); } +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config vars */ + initConfigSettings(); +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + loadModConf->iStatsInterval = cs.iStatsInterval; + loadModConf->iFacility = cs.iFacility; + loadModConf->iSeverity = cs.iSeverity; + loadModConf->statsFmt = cs.bJSON ? statsFmt_JSON : statsFmt_Legacy; +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf + if(pModConf->iStatsInterval == 0) { + errmsg.LogError(0, NO_ERRCODE, "impstats: stats interval zero not permitted, using " + "defaul of %d seconds", DEFAULT_STATS_PERIOD); + pModConf->iStatsInterval = DEFAULT_STATS_PERIOD; + } +ENDcheckCnf + + +BEGINactivateCnf + rsRetVal localRet; +CODESTARTactivateCnf + runModConf = pModConf; + DBGPRINTF("impstats: stats interval %d seconds\n", runModConf->iStatsInterval); + localRet = statsobj.EnableStats(); + if(localRet != RS_RET_OK) { + errmsg.LogError(0, localRet, "impstats: error enabling statistics gathering"); + ABORT_FINALIZE(RS_RET_NO_RUN); + } +finalize_it: +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + + BEGINrunInput CODESTARTrunInput /* this is an endless loop - it is terminated when the thread is @@ -136,7 +197,7 @@ CODESTARTrunInput * right into the sleep below. */ while(1) { - srSleep(cs.iStatsInterval, 0); /* seconds, micro seconds */ + srSleep(runModConf->iStatsInterval, 0); /* seconds, micro seconds */ if(glbl.GetGlobalInputTermState() == 1) break; /* terminate input! */ @@ -147,17 +208,7 @@ ENDrunInput BEGINwillRun - rsRetVal localRet; CODESTARTwillRun - DBGPRINTF("impstats: stats interval %d seconds\n", cs.iStatsInterval); - if(cs.iStatsInterval == 0) - ABORT_FINALIZE(RS_RET_NO_RUN); - localRet = statsobj.EnableStats(); - if(localRet != RS_RET_OK) { - errmsg.LogError(0, localRet, "impstat: error enabling statistics gathering"); - ABORT_FINALIZE(RS_RET_NO_RUN); - } -finalize_it: ENDwillRun @@ -169,7 +220,6 @@ ENDafterRun BEGINmodExit CODESTARTmodExit prop.Destruct(&pInputName); - prop.Destruct(&pLocalHostIP); /* release objects we used */ objRelease(glbl, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); @@ -182,6 +232,7 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt @@ -207,15 +258,12 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatfacility", 0, eCmdHdlrInt, NULL, &cs.iFacility, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatseverity", 0, eCmdHdlrInt, NULL, &cs.iSeverity, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatjson", 0, eCmdHdlrBinary, NULL, &cs.bJSON, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(prop.Construct(&pInputName)); CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("impstats"), sizeof("impstats") - 1)); CHKiRet(prop.ConstructFinalize(pInputName)); - - CHKiRet(prop.Construct(&pLocalHostIP)); - CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1)); - CHKiRet(prop.ConstructFinalize(pLocalHostIP)); ENDmodInit /* vi:set ai: */ diff --git a/plugins/imptcp/imptcp.c b/plugins/imptcp/imptcp.c index 92383f90..aa1ad81e 100644 --- a/plugins/imptcp/imptcp.c +++ b/plugins/imptcp/imptcp.c @@ -49,6 +49,7 @@ #include <sys/types.h> #include <sys/socket.h> #include <sys/epoll.h> +#include <netinet/tcp.h> #if HAVE_FCNTL_H #include <fcntl.h> #endif @@ -65,6 +66,7 @@ #include "datetime.h" #include "ruleset.h" #include "msg.h" +#include "statsobj.h" #include "net.h" /* for permittedPeers, may be removed when this is removed */ /* the define is from tcpsrv.h, we need to find a new (but easier!!!) abstraction layer some time ... */ @@ -73,6 +75,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imptcp") /* static data */ DEF_IMOD_STATIC_DATA @@ -82,22 +85,54 @@ DEFobjCurrIf(prop) DEFobjCurrIf(datetime) DEFobjCurrIf(errmsg) DEFobjCurrIf(ruleset) +DEFobjCurrIf(statsobj) /* forward references */ static void * wrkr(void *myself); /* config settings */ typedef struct configSettings_s { + int bKeepAlive; /* support keep-alive packets */ + int iKeepAliveIntvl; + int iKeepAliveProbes; + int iKeepAliveTime; int bEmitMsgOnClose; /* emit an informational message on close by remote peer */ + int bSuppOctetFram; /* support octet-counted framing? */ int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */ uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */ uchar *lstnIP; /* which IP we should listen on? */ - ruleset_t *pRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + uchar *pszBindRuleset; int wrkrMax; /* max number of workers (actually "helper workers") */ } configSettings_t; - static configSettings_t cs; +struct instanceConf_s { + int bKeepAlive; /* support keep-alive packets */ + int iKeepAliveIntvl; + int iKeepAliveProbes; + int iKeepAliveTime; + int bEmitMsgOnClose; + int bSuppOctetFram; /* support octet-counted framing? */ + int iAddtlFrameDelim; + uchar *pszBindPort; /* port to bind to */ + uchar *pszBindAddr; /* IP to bind socket to */ + uchar *pszBindRuleset; /* name of ruleset to bind to */ + uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */ + ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + struct instanceConf_s *next; +}; + + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + int wrkrMax; +}; + +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + +#include "im-helper.h" /* must be included AFTER the type definitions! */ /* data elements describing our running config */ typedef struct ptcpsrv_s ptcpsrv_t; typedef struct ptcplstn_s ptcplstn_t; @@ -112,27 +147,34 @@ struct ptcpsrv_s { ptcpsrv_t *pNext; /* linked list maintenance */ uchar *port; /* Port to listen to */ uchar *lstnIP; /* which IP we should listen on? */ - int bEmitMsgOnClose; int iAddtlFrameDelim; + int iKeepAliveIntvl; + int iKeepAliveProbes; + int iKeepAliveTime; uchar *pszInputName; prop_t *pInputName; /* InputName in (fast to process) property format */ ruleset_t *pRuleset; ptcplstn_t *pLstn; /* root of our listeners */ ptcpsess_t *pSess; /* root of our sessions */ pthread_mutex_t mutSessLst; + sbool bKeepAlive; /* support keep-alive packets */ + sbool bEmitMsgOnClose; + sbool bSuppOctetFram; }; /* the ptcp session object. Describes a single active session. * includes support for doubly-linked list. */ struct ptcpsess_s { - ptcpsrv_t *pSrv; /* our server */ +// ptcpsrv_t *pSrv; /* our server TODO: check remove! */ + ptcplstn_t *pLstn; /* our listener */ ptcpsess_t *prev, *next; int sock; epolld_t *epd; //--- from tcps_sess.h int iMsg; /* index of next char to store in msg */ int bAtStrtOfFram; /* are we at the very beginning of a new frame? */ + sbool bSuppOctetFram; /**< copy from listener, to speed up access */ enum { eAtStrtFram, eInOctetCnt, @@ -153,7 +195,10 @@ struct ptcplstn_s { ptcpsrv_t *pSrv; /* our server */ ptcplstn_t *prev, *next; int sock; + sbool bSuppOctetFram; epolld_t *epd; + statsobj_t *stats; /* listener stats */ + STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) }; @@ -195,7 +240,7 @@ static int iMaxLine; /* maximum size of a single message */ /* forward definitions */ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); -static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock); +static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock, int isIPv6); /* some simple constructors/destructors */ @@ -217,6 +262,7 @@ destructSrv(ptcpsrv_t *pSrv) { prop.Destruct(&pSrv->pInputName); pthread_mutex_destroy(&pSrv->mutSessLst); + free(pSrv->pszInputName); free(pSrv->port); free(pSrv); } @@ -241,10 +287,11 @@ startupSrv(ptcpsrv_t *pSrv) int sockflags; struct addrinfo hints, *res = NULL, *r; uchar *lstnIP; + int isIPv6 = 0; lstnIP = pSrv->lstnIP == NULL ? UCHAR_CONSTANT("") : pSrv->lstnIP; - DBGPRINTF("imptcp creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port); + DBGPRINTF("imptcp: creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port); memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; @@ -273,8 +320,9 @@ startupSrv(ptcpsrv_t *pSrv) continue; } -#ifdef IPV6_V6ONLY if(r->ai_family == AF_INET6) { + isIPv6 = 1; +#ifdef IPV6_V6ONLY int iOn = 1; if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&iOn, sizeof (iOn)) < 0) { @@ -282,8 +330,10 @@ startupSrv(ptcpsrv_t *pSrv) sock = -1; continue; } - } #endif + } else { + isIPv6 = 0; + } if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) { DBGPRINTF("error %d setting tcp socket option\n", errno); close(sock); @@ -345,7 +395,7 @@ startupSrv(ptcpsrv_t *pSrv) /* if we reach this point, we were able to obtain a valid socket, so we can * create our listener object. -- rgerhards, 2010-08-10 */ - CHKiRet(addLstn(pSrv, sock)); + CHKiRet(addLstn(pSrv, sock, isIPv6)); ++numSocks; } @@ -436,12 +486,80 @@ finalize_it: } +/* Enable KEEPALIVE handling on the socket. */ +static inline rsRetVal +EnableKeepAlive(ptcplstn_t *pLstn, int sock) +{ + int ret; + int optval; + socklen_t optlen; + DEFiRet; + + optval = 1; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); + if(ret < 0) { + dbgprintf("EnableKeepAlive socket call returns error %d\n", ret); + ABORT_FINALIZE(RS_RET_ERR); + } + +# if defined(TCP_KEEPCNT) + if(pLstn->pSrv->iKeepAliveProbes > 0) { + optval = pLstn->pSrv->iKeepAliveProbes; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &optval, optlen); + } else { + ret = 0; + } +# else + ret = -1; +# endif + if(ret < 0) { + errmsg.LogError(ret, NO_ERRCODE, "imptcp cannot set keepalive probes - ignored"); + } + +# if defined(TCP_KEEPCNT) + if(pLstn->pSrv->iKeepAliveTime > 0) { + optval = pLstn->pSrv->iKeepAliveTime; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &optval, optlen); + } else { + ret = 0; + } +# else + ret = -1; +# endif + if(ret < 0) { + errmsg.LogError(ret, NO_ERRCODE, "imptcp cannot set keepalive time - ignored"); + } + +# if defined(TCP_KEEPCNT) + if(pLstn->pSrv->iKeepAliveIntvl > 0) { + optval = pLstn->pSrv->iKeepAliveIntvl; + optlen = sizeof(optval); + ret = setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &optval, optlen); + } else { + ret = 0; + } +# else + ret = -1; +# endif + if(ret < 0) { + errmsg.LogError(errno, NO_ERRCODE, "imptcp cannot set keepalive intvl - ignored"); + } + + dbgprintf("KEEPALIVE enabled for socket %d\n", sock); + +finalize_it: + RETiRet; +} + /* accept an incoming connection request * rgerhards, 2008-04-22 */ static rsRetVal -AcceptConnReq(int sock, int *newSock, prop_t **peerName, prop_t **peerIP) +AcceptConnReq(ptcplstn_t *pLstn, int *newSock, prop_t **peerName, prop_t **peerIP) { int sockflags; struct sockaddr_storage addr; @@ -450,13 +568,17 @@ AcceptConnReq(int sock, int *newSock, prop_t **peerName, prop_t **peerIP) DEFiRet; - iNewSock = accept(sock, (struct sockaddr*) &addr, &addrlen); + iNewSock = accept(pLstn->sock, (struct sockaddr*) &addr, &addrlen); if(iNewSock < 0) { if(errno == EAGAIN || errno == EWOULDBLOCK) ABORT_FINALIZE(RS_RET_NO_MORE_DATA); ABORT_FINALIZE(RS_RET_ACCEPT_ERR); } + if(pLstn->pSrv->bKeepAlive) + EnableKeepAlive(pLstn, iNewSock);/* we ignore errors, best to do! */ + + CHKiRet(getPeerNames(peerName, peerIP, (struct sockaddr*) &addr)); /* set the new socket to non-blocking IO */ @@ -500,22 +622,25 @@ static rsRetVal doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub) { msg_t *pMsg; + ptcpsrv_t *pSrv; DEFiRet; if(pThis->iMsg == 0) { DBGPRINTF("discarding zero-sized message\n"); FINALIZE; } + pSrv = pThis->pLstn->pSrv; /* we now create our own message object and submit it to the queue */ CHKiRet(msgConstructWithTime(&pMsg, stTime, ttGenTime)); MsgSetRawMsg(pMsg, (char*)pThis->pMsg, pThis->iMsg); - MsgSetInputName(pMsg, pThis->pSrv->pInputName); + MsgSetInputName(pMsg, pSrv->pInputName); MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY); pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME; MsgSetRcvFrom(pMsg, pThis->peerName); CHKiRet(MsgSetRcvFromIP(pMsg, pThis->peerIP)); - MsgSetRuleset(pMsg, pThis->pSrv->pRuleset); + MsgSetRuleset(pMsg, pSrv->pRuleset); + STATSCOUNTER_INC(pThis->pLstn->ctrSubmit, pThis->pLstn->mutCtrSubmit); if(pMultiSub == NULL) { CHKiRet(submitMsg(pMsg)); @@ -548,7 +673,7 @@ processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttG DEFiRet; if(pThis->inputState == eAtStrtFram) { - if(isdigit((int) c)) { + if(pThis->bSuppOctetFram && isdigit((int) c)) { pThis->inputState = eInOctetCnt; pThis->iOctetsRemain = 0; pThis->eFraming = TCP_FRAMING_OCTET_COUNTING; @@ -597,7 +722,8 @@ processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttG } if(( (c == '\n') - || ((pThis->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->iAddtlFrameDelim)) + || ((pThis->pLstn->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) + && (c == pThis->pLstn->pSrv->iAddtlFrameDelim)) ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub); pThis->inputState = eAtStrtFram; @@ -687,9 +813,11 @@ initConfigSettings(void) { cs.bEmitMsgOnClose = 0; cs.wrkrMax = 2; + cs.bSuppOctetFram = 1; cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; cs.pszInputName = NULL; - cs.pRuleset = NULL; + cs.pszBindRuleset = NULL; + cs.pszInputName = NULL; cs.lstnIP = NULL; } @@ -702,7 +830,7 @@ addEPollSock(epolld_type_t typ, void *ptr, int sock, epolld_t **pEpd) DEFiRet; epolld_t *epd = NULL; - CHKmalloc(epd = malloc(sizeof(epolld_t))); + CHKmalloc(epd = calloc(sizeof(epolld_t), 1)); epd->typ = typ; epd->ptr = ptr; *pEpd = epd; @@ -756,14 +884,27 @@ finalize_it: /* add a listener to the server */ static rsRetVal -addLstn(ptcpsrv_t *pSrv, int sock) +addLstn(ptcpsrv_t *pSrv, int sock, int isIPv6) { DEFiRet; ptcplstn_t *pLstn; + uchar statname[64]; CHKmalloc(pLstn = malloc(sizeof(ptcplstn_t))); pLstn->pSrv = pSrv; + pLstn->bSuppOctetFram = pSrv->bSuppOctetFram; pLstn->sock = sock; + /* support statistics gathering */ + CHKiRet(statsobj.Construct(&(pLstn->stats))); + snprintf((char*)statname, sizeof(statname), "imptcp(%s/%s/%s)", + (pSrv->lstnIP == NULL) ? "*" : (char*)pSrv->lstnIP, pSrv->port, + isIPv6 ? "IPv6" : "IPv4"); + statname[sizeof(statname)-1] = '\0'; /* just to be on the save side... */ + CHKiRet(statsobj.SetName(pLstn->stats, statname)); + STATSCOUNTER_INIT(pLstn->ctrSubmit, pLstn->mutCtrSubmit); + CHKiRet(statsobj.AddCounter(pLstn->stats, UCHAR_CONSTANT("submitted"), + ctrType_IntCtr, &(pLstn->ctrSubmit))); + CHKiRet(statsobj.ConstructFinalize(pLstn->stats)); /* add to start of server's listener list */ pLstn->prev = NULL; @@ -782,15 +923,17 @@ finalize_it: /* add a session to the server */ static rsRetVal -addSess(ptcpsrv_t *pSrv, int sock, prop_t *peerName, prop_t *peerIP) +addSess(ptcplstn_t *pLstn, int sock, prop_t *peerName, prop_t *peerIP) { DEFiRet; ptcpsess_t *pSess = NULL; + ptcpsrv_t *pSrv = pLstn->pSrv; CHKmalloc(pSess = malloc(sizeof(ptcpsess_t))); CHKmalloc(pSess->pMsg = malloc(iMaxLine * sizeof(uchar))); - pSess->pSrv = pSrv; + pSess->pLstn = pLstn; pSess->sock = sock; + pSess->bSuppOctetFram = pLstn->bSuppOctetFram; pSess->inputState = eAtStrtFram; pSess->iMsg = 0; pSess->bAtStrtOfFram = 1; @@ -827,17 +970,17 @@ closeSess(ptcpsess_t *pSess) CHKiRet(removeEPollSock(sock, pSess->epd)); close(sock); - pthread_mutex_lock(&pSess->pSrv->mutSessLst); + pthread_mutex_lock(&pSess->pLstn->pSrv->mutSessLst); /* finally unlink session from structures */ if(pSess->next != NULL) pSess->next->prev = pSess->prev; if(pSess->prev == NULL) { /* need to update root! */ - pSess->pSrv->pSess = pSess->next; + pSess->pLstn->pSrv->pSess = pSess->next; } else { pSess->prev->next = pSess->next; } - pthread_mutex_unlock(&pSess->pSrv->mutSessLst); + pthread_mutex_unlock(&pSess->pLstn->pSrv->mutSessLst); /* unlinked, now remove structure */ destructSess(pSess); @@ -848,47 +991,91 @@ finalize_it: } -/* accept a new ruleset to bind. Checks if it exists and complains, if not */ -static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) +/* This function is called when a new listener instace shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + */ +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) { - ruleset_t *pRuleset; - rsRetVal localRet; + instanceConf_t *inst; DEFiRet; - localRet = ruleset.GetRuleset(&pRuleset, pszName); - if(localRet == RS_RET_NOT_FOUND) { - errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName); + CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); + if(pNewVal == NULL || *pNewVal == '\0') { + errmsg.LogError(0, NO_ERRCODE, "imptcp: port number must be specified, listener ignored"); + } + if((pNewVal == NULL) || (pNewVal == '\0')) { + inst->pszBindPort = NULL; + } else { + CHKmalloc(inst->pszBindPort = ustrdup(pNewVal)); + } + if((cs.lstnIP == NULL) || (cs.lstnIP[0] == '\0')) { + inst->pszBindAddr = NULL; + } else { + CHKmalloc(inst->pszBindAddr = ustrdup(cs.lstnIP)); + } + if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { + inst->pszBindRuleset = NULL; + } else { + CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset)); + } + if((cs.pszInputName == NULL) || (cs.pszInputName[0] == '\0')) { + inst->pszInputName = NULL; + } else { + CHKmalloc(inst->pszInputName = ustrdup(cs.pszInputName)); + } + inst->pBindRuleset = NULL; + inst->bSuppOctetFram = cs.bSuppOctetFram; + inst->bKeepAlive = cs.bKeepAlive; + inst->iKeepAliveIntvl = cs.iKeepAliveTime; + inst->iKeepAliveProbes = cs.iKeepAliveProbes; + inst->iKeepAliveTime = cs.iKeepAliveTime; + inst->bEmitMsgOnClose = cs.bEmitMsgOnClose; + inst->iAddtlFrameDelim = cs.iAddtlFrameDelim; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; } - CHKiRet(localRet); - cs.pRuleset = pRuleset; - DBGPRINTF("imptcp current bind ruleset %p: '%s'\n", pRuleset, pszName); finalize_it: - free(pszName); /* no longer needed */ + free(pNewVal); RETiRet; } -static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal) +static inline rsRetVal +addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst) { DEFiRet; ptcpsrv_t *pSrv; - CHKmalloc(pSrv = malloc(sizeof(ptcpsrv_t))); + CHKmalloc(pSrv = MALLOC(sizeof(ptcpsrv_t))); pthread_mutex_init(&pSrv->mutSessLst, NULL); pSrv->pSess = NULL; pSrv->pLstn = NULL; - pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose; - pSrv->port = pNewVal; - pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim; - pSrv->lstnIP = cs.lstnIP; - pSrv->pRuleset = cs.pRuleset; - pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : cs.pszInputName; + pSrv->bSuppOctetFram = inst->bSuppOctetFram; + pSrv->bKeepAlive = inst->bKeepAlive; + pSrv->iKeepAliveIntvl = inst->iKeepAliveTime; + pSrv->iKeepAliveProbes = inst->iKeepAliveProbes; + pSrv->iKeepAliveTime = inst->iKeepAliveTime; + pSrv->bEmitMsgOnClose = inst->bEmitMsgOnClose; + CHKmalloc(pSrv->port = ustrdup(inst->pszBindPort)); + pSrv->iAddtlFrameDelim = inst->iAddtlFrameDelim; + if(inst->pszBindAddr == NULL) + pSrv->lstnIP = NULL; + else { + CHKmalloc(pSrv->lstnIP = ustrdup(inst->pszBindAddr)); + } + pSrv->pRuleset = inst->pBindRuleset; + pSrv->pszInputName = ustrdup((inst->pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : inst->pszInputName); CHKiRet(prop.Construct(&pSrv->pInputName)); CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName))); CHKiRet(prop.ConstructFinalize(pSrv->pInputName)); - cs.pszInputName = NULL; /* moved over to pSrv, we do not own */ - cs.lstnIP = NULL; /* moved over to pSrv, we do not own */ /* add to linked list */ pSrv->pNext = pSrvRoot; @@ -914,11 +1101,11 @@ startWorkerPool(void) { int i; wrkrRunning = 0; - if(cs.wrkrMax > 16) - cs.wrkrMax = 16; /* TODO: make dynamic? */ + if(runModConf->wrkrMax > 16) + runModConf->wrkrMax = 16; /* TODO: make dynamic? */ pthread_mutex_init(&wrkrMut, NULL); pthread_cond_init(&wrkrIdle, NULL); - for(i = 0 ; i < cs.wrkrMax ; ++i) { + for(i = 0 ; i < runModConf->wrkrMax ; ++i) { /* init worker info structure! */ pthread_cond_init(&wrkrInfo[i].run, NULL); wrkrInfo[i].event = NULL; @@ -934,7 +1121,7 @@ static inline void stopWorkerPool(void) { int i; - for(i = 0 ; i < cs.wrkrMax ; ++i) { + for(i = 0 ; i < runModConf->wrkrMax ; ++i) { pthread_cond_signal(&wrkrInfo[i].run); /* awake wrkr if not running */ pthread_join(wrkrInfo[i].tid, NULL); DBGPRINTF("imptcp: info: worker %d was called %llu times\n", i, wrkrInfo[i].numCalled); @@ -954,15 +1141,29 @@ static inline rsRetVal startupServers() { DEFiRet; + rsRetVal localRet, lastErr; + int iOK; + int iAll; ptcpsrv_t *pSrv; + iAll = iOK = 0; + lastErr = RS_RET_ERR; pSrv = pSrvRoot; while(pSrv != NULL) { DBGPRINTF("imptcp: starting up server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName); - startupSrv(pSrv); + localRet = startupSrv(pSrv); + if(localRet == RS_RET_OK) + iOK++; + else + lastErr = localRet; + ++iAll; pSrv = pSrv->pNext; } + DBGPRINTF("imptcp: %d out of %d servers started successfully\n", iOK, iAll); + if(iOK == 0) /* iff all fails, we report an error */ + iRet = lastErr; + RETiRet; } @@ -981,11 +1182,11 @@ lstnActivity(ptcplstn_t *pLstn) DBGPRINTF("imptcp: new connection on listen socket %d\n", pLstn->sock); while(glbl.GetGlobalInputTermState() == 0) { - localRet = AcceptConnReq(pLstn->sock, &newSock, &peerName, &peerIP); + localRet = AcceptConnReq(pLstn, &newSock, &peerName, &peerIP); if(localRet == RS_RET_NO_MORE_DATA || glbl.GetGlobalInputTermState() == 1) break; CHKiRet(localRet); - CHKiRet(addSess(pLstn->pSrv, newSock, peerName, peerIP)); + CHKiRet(addSess(pLstn, newSock, peerName, peerIP)); } finalize_it: @@ -1016,7 +1217,7 @@ sessActivity(ptcpsess_t *pSess) CHKiRet(DataRcvd(pSess, rcvBuf, lenRcv)); } else if (lenRcv == 0) { /* session was closed, do clean-up */ - if(pSess->pSrv->bEmitMsgOnClose) { + if(pSess->pLstn->pSrv->bEmitMsgOnClose) { uchar *peerName; int lenPeer; prop.GetString(pSess->peerName, &peerName, &lenPeer); @@ -1082,9 +1283,9 @@ processWorkSet(int nEvents, struct epoll_event events[]) } else { pthread_mutex_lock(&wrkrMut); /* check if there is a free worker */ - for(i = 0 ; (i < cs.wrkrMax) && (wrkrInfo[i].event != NULL) ; ++i) + for(i = 0 ; (i < runModConf->wrkrMax) && (wrkrInfo[i].event != NULL) ; ++i) /*do search*/; - if(i < cs.wrkrMax) { + if(i < runModConf->wrkrMax) { /* worker free -> use it! */ wrkrInfo[i].event = events+iEvt; ++wrkrRunning; @@ -1144,33 +1345,57 @@ wrkr(void *myself) } -/* This function is called to gather input. - */ -BEGINrunInput - int nEvents; - struct epoll_event events[128]; -CODESTARTrunInput - startWorkerPool(); - DBGPRINTF("imptcp: now beginning to process input data\n"); - while(glbl.GetGlobalInputTermState() == 0) { - DBGPRINTF("imptcp going on epoll_wait\n"); - nEvents = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1); - DBGPRINTF("imptcp: epoll returned %d events\n", nEvents); - processWorkSet(nEvents, events); +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config vars */ + initConfigSettings(); +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + loadModConf->wrkrMax = cs.wrkrMax; + + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(cs.pszInputName); + free(cs.lstnIP); + cs.pszInputName = NULL; + cs.lstnIP = NULL; +ENDendCnfLoad + + +/* function to generate error message if framework does not find requested ruleset */ +static inline void +std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst) +{ + errmsg.LogError(0, NO_ERRCODE, "imptcp: ruleset '%s' for port %s not found - " + "using default ruleset instead", inst->pszBindRuleset, + inst->pszBindPort); +} +BEGINcheckCnf + instanceConf_t *inst; +CODESTARTcheckCnf + for(inst = pModConf->root ; inst != NULL ; inst = inst->next) { + std_checkRuleset(pModConf, inst); } - DBGPRINTF("imptcp: successfully terminated\n"); - /* we stop the worker pool in AfterRun, in case we get cancelled for some reason (old Interface) */ -ENDrunInput +ENDcheckCnf -/* initialize and return if will run or not */ -BEGINwillRun -CODESTARTwillRun - /* first apply some config settings */ +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; +CODESTARTactivateCnfPrePrivDrop iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */ + runModConf = pModConf; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(pModConf, inst); + } if(pSrvRoot == NULL) { - errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "error: no ptcp server defined, module can not run."); + errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "imptcp: no ptcp server defined, module can not run."); ABORT_FINALIZE(RS_RET_NO_RUN); } @@ -1197,6 +1422,52 @@ CODESTARTwillRun CHKiRet(startupServers()); DBGPRINTF("imptcp started up, but not yet receiving data\n"); finalize_it: +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf + /* nothing to do, all done pre priv drop */ +ENDactivateCnf + + +BEGINfreeCnf + instanceConf_t *inst, *del; +CODESTARTfreeCnf + for(inst = pModConf->root ; inst != NULL ; ) { + free(inst->pszBindPort); + free(inst->pszBindAddr); + free(inst->pszBindRuleset); + free(inst->pszInputName); + del = inst; + inst = inst->next; + free(del); + } +ENDfreeCnf + + +/* This function is called to gather input. + */ +BEGINrunInput + int nEvents; + struct epoll_event events[128]; +CODESTARTrunInput + startWorkerPool(); + DBGPRINTF("imptcp: now beginning to process input data\n"); + while(glbl.GetGlobalInputTermState() == 0) { + DBGPRINTF("imptcp going on epoll_wait\n"); + nEvents = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1); + DBGPRINTF("imptcp: epoll returned %d events\n", nEvents); + processWorkSet(nEvents, events); + } + DBGPRINTF("imptcp: successfully terminated\n"); + /* we stop the worker pool in AfterRun, in case we get cancelled for some reason (old Interface) */ +ENDrunInput + + +/* initialize and return if will run or not */ +BEGINwillRun +CODESTARTwillRun ENDwillRun @@ -1213,6 +1484,8 @@ shutdownSrv(ptcpsrv_t *pSrv) pLstn = pSrv->pLstn; while(pLstn != NULL) { close(pLstn->sock); + statsobj.Destruct(&(pLstn->stats)); + /* now unlink listner */ lstnDel = pLstn; pLstn = pLstn->next; DBGPRINTF("imptcp shutdown listen socket %d\n", lstnDel->sock); @@ -1255,6 +1528,7 @@ CODESTARTmodExit pthread_attr_destroy(&wrkrThrdAttr); /* release objects we used */ objRelease(glbl, CORE_COMPONENT); + objRelease(statsobj, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); objRelease(net, LM_NET_FILENAME); objRelease(datetime, CORE_COMPONENT); @@ -1268,6 +1542,11 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus { cs.bEmitMsgOnClose = 0; cs.wrkrMax = 2; + cs.bKeepAlive = 0; + cs.iKeepAliveProbes = 0; + cs.iKeepAliveTime = 0; + cs.iKeepAliveIntvl = 0; + cs.bSuppOctetFram = 1; cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; free(cs.pszInputName); cs.pszInputName = NULL; @@ -1287,6 +1566,8 @@ ENDisCompatibleWithFeature BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt @@ -1295,9 +1576,9 @@ BEGINmodInit() CODESTARTmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr - initConfigSettings(); /* request objects we use */ CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); CHKiRet(objUse(errmsg, CORE_COMPONENT)); @@ -1306,11 +1587,24 @@ CODEmodInit_QueryRegCFSLineHdlr /* initialize "read-only" thread attributes */ pthread_attr_init(&wrkrThrdAttr); - pthread_attr_setstacksize(&wrkrThrdAttr, 2048*1024); + pthread_attr_setstacksize(&wrkrThrdAttr, 4096*1024); + + /* init legacy config settings */ + initConfigSettings(); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverrun"), 0, eCmdHdlrGetWord, - addTCPListener, NULL, STD_LOADABLE_MODULE_ID)); + addInstance, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive"), 0, eCmdHdlrBinary, + NULL, &cs.bKeepAlive, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_probes"), 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveProbes, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_time"), 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveTime, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverkeepalive_intvl"), 0, eCmdHdlrInt, + NULL, &cs.iKeepAliveIntvl, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserversupportoctetcountedframing"), 0, eCmdHdlrBinary, + NULL, &cs.bSuppOctetFram, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpservernotifyonconnectionclose"), 0, eCmdHdlrBinary, NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserveraddtlframedelimiter"), 0, eCmdHdlrInt, @@ -1322,7 +1616,7 @@ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverlistenip"), 0, eCmdHdlrGetWord, NULL, &cs.lstnIP, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverbindruleset"), 0, - eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID)); + eCmdHdlrGetWord, NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c index 602809ff..99fabd18 100644 --- a/plugins/imrelp/imrelp.c +++ b/plugins/imrelp/imrelp.c @@ -38,39 +38,56 @@ #include <librelp.h> #include "rsyslog.h" #include "dirty.h" +#include "errmsg.h" #include "cfsysline.h" #include "module-template.h" #include "net.h" #include "msg.h" #include "unicode-helper.h" #include "prop.h" +#include "ruleset.h" MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imrelp") /* static data */ DEF_IMOD_STATIC_DATA DEFobjCurrIf(net) DEFobjCurrIf(prop) +DEFobjCurrIf(errmsg) +DEFobjCurrIf(ruleset) + +/* forward definitions */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + /* Module static data */ +/* config vars for legacy config system */ static relpEngine_t *pRelpEngine; /* our relp engine */ static prop_t *pInputName = NULL; /* there is only one global inputName for all messages generated by this module */ +static struct configSettings_s { + uchar *pszBindRuleset; /* name of Ruleset to bind to */ +} cs; +struct instanceConf_s { + uchar *pszBindPort; /* port to bind to */ + struct instanceConf_s *next; +}; -/* config settings */ -/* ------------------------------ 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); -} +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + uchar *pszBindRuleset; /* name of Ruleset to bind to */ + ruleset_t *pBindRuleset; /* due to librelp limitation, we need to bind all listerns to the same set */ +}; -#endif // #if 0 +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + + +/* ------------------------------ callbacks ------------------------------ */ /* callback for receiving syslog messages. This function is invoked from the * RELP engine when a syslog message arrived. It must return a relpRetVal, @@ -87,7 +104,7 @@ onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *pMsg, size_t lenMsg) { DEFiRet; parseAndSubmitMessage(pHostname, pIP, pMsg, lenMsg, PARSE_HOSTNAME, - eFLOWCTL_LIGHT_DELAY, pInputName, NULL, 0); + eFLOWCTL_LIGHT_DELAY, pInputName, NULL, 0, runModConf->pBindRuleset); RETiRet; } @@ -96,7 +113,48 @@ onSyslogRcv(uchar *pHostname, uchar *pIP, uchar *pMsg, size_t lenMsg) /* ------------------------------ end callbacks ------------------------------ */ -static rsRetVal addListener(void __attribute__((unused)) *pVal, uchar *pNewVal) +/* modified to work for module, not instance (as usual) */ +static inline void +std_checkRuleset_genErrMsg(modConfData_t *modConf, __attribute__((unused)) instanceConf_t *inst) +{ + errmsg.LogError(0, NO_ERRCODE, "imrelp: ruleset '%s' not found - " + "using default ruleset instead", modConf->pszBindRuleset); +} + + +/* This function is called when a new listener instace shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + * rgerhards, 2011-05-04 + */ +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) +{ + instanceConf_t *inst; + DEFiRet; + + CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); + + if(pNewVal == NULL || *pNewVal == '\0') { + errmsg.LogError(0, NO_ERRCODE, "imrelp: port number must be specified, listener ignored"); + } + inst->pszBindPort = pNewVal; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; + } + +finalize_it: + RETiRet; +} + + +static rsRetVal +addListner(modConfData_t __attribute__((unused)) *modConf, instanceConf_t *inst) { DEFiRet; if(pRelpEngine == NULL) { @@ -106,14 +164,78 @@ static rsRetVal addListener(void __attribute__((unused)) *pVal, uchar *pNewVal) CHKiRet(relpEngineSetSyslogRcv(pRelpEngine, onSyslogRcv)); } - CHKiRet(relpEngineAddListner(pRelpEngine, pNewVal)); - - free(pNewVal); /* we do no longer need it */ + CHKiRet(relpEngineAddListner(pRelpEngine, inst->pszBindPort)); finalize_it: RETiRet; } + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config variables */ + cs.pszBindRuleset = NULL; +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { + loadModConf->pszBindRuleset = NULL; + } else { + CHKmalloc(loadModConf->pszBindRuleset = ustrdup(cs.pszBindRuleset)); + } + loadModConf->pBindRuleset = NULL; +finalize_it: + free(cs.pszBindRuleset); + loadModConf = NULL; /* done loading */ +ENDendCnfLoad + + +BEGINcheckCnf + rsRetVal localRet; + ruleset_t *pRuleset; +CODESTARTcheckCnf + /* we emulate the standard "ruleset query" code provided by the framework + * for *instances* (which we can currently not support due to librelp). + */ + if(pModConf->pszBindRuleset == NULL) { + pModConf->pBindRuleset = NULL; + } else { + localRet = ruleset.GetRuleset(pModConf->pConf, &pRuleset, pModConf->pszBindRuleset); + if(localRet == RS_RET_NOT_FOUND) { + std_checkRuleset_genErrMsg(pModConf, NULL); + } + CHKiRet(localRet); + pModConf->pBindRuleset = pRuleset; + } +finalize_it: +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(pModConf, inst); + } + if(pRelpEngine == NULL) + ABORT_FINALIZE(RS_RET_NO_RUN); +finalize_it: +ENDactivateCnfPrePrivDrop + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + /* This function is called to gather input. */ BEGINrunInput @@ -125,34 +247,14 @@ CODESTARTrunInput 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); - - /* we need to create the inputName property (only once during our lifetime) */ - CHKiRet(prop.Construct(&pInputName)); - CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imrelp"), sizeof("imrelp") - 1)); - CHKiRet(prop.ConstructFinalize(pInputName)); -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 - - if(pInputName != NULL) - prop.Destruct(&pInputName); ENDafterRun @@ -161,15 +263,23 @@ CODESTARTmodExit if(pRelpEngine != NULL) iRet = relpEngineDestruct(&pRelpEngine); + /* global variable cleanup */ + if(pInputName != NULL) + prop.Destruct(&pInputName); + /* release objects we used */ + objRelease(ruleset, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); objRelease(net, LM_NET_FILENAME); + objRelease(errmsg, CORE_COMPONENT); ENDmodExit static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { + free(cs.pszBindRuleset); + cs.pszBindRuleset = NULL; return RS_RET_OK; } @@ -178,6 +288,8 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES ENDqueryEtryPt @@ -188,13 +300,22 @@ CODEmodInit_QueryRegCFSLineHdlr pRelpEngine = NULL; /* request objects we use */ CHKiRet(objUse(prop, CORE_COMPONENT)); + CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); + CHKiRet(objUse(ruleset, CORE_COMPONENT)); /* register config file handlers */ + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverbindruleset", 0, eCmdHdlrGetWord, + NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrelpserverrun", 0, eCmdHdlrGetWord, - addListener, NULL, STD_LOADABLE_MODULE_ID)); + addInstance, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.Construct(&pInputName)); + CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imrelp"), sizeof("imrelp") - 1)); + CHKiRet(prop.ConstructFinalize(pInputName)); ENDmodInit diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c index ee9ec5c6..8b607a84 100644 --- a/plugins/imsolaris/imsolaris.c +++ b/plugins/imsolaris/imsolaris.c @@ -86,6 +86,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imsolaris") /* defines */ #define PATH_LOG "/dev/log" @@ -99,6 +100,10 @@ DEFobjCurrIf(prop) /* config settings */ +struct modConfData_s { + EMPTY_STRUCT; +}; + static prop_t *pInputName = NULL; /* our inputName currently is always "imuxsock", and this will hold it */ static char *LogName = NULL; /* the log socket name TODO: make configurable! */ @@ -302,6 +307,31 @@ finalize_it: } +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + + /* This function is called to gather input. */ BEGINrunInput CODESTARTrunInput diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c index b537cbb8..33404fee 100644 --- a/plugins/imtcp/imtcp.c +++ b/plugins/imtcp/imtcp.c @@ -66,6 +66,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imtcp") /* static data */ DEF_IMOD_STATIC_DATA @@ -76,23 +77,58 @@ DEFobjCurrIf(netstrm) DEFobjCurrIf(errmsg) DEFobjCurrIf(ruleset) +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + /* Module static data */ static tcpsrv_t *pOurTcpsrv = NULL; /* our TCP server(listener) TODO: change for multiple instances */ static permittedPeers_t *pPermPeersRoot = NULL; /* config settings */ -static int iTCPSessMax = 200; /* max number of sessions */ -static int iTCPLstnMax = 20; /* max number of sessions */ -static int iStrmDrvrMode = 0; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ -static int bEmitMsgOnClose = 0; /* emit an informational message on close by remote peer */ -static int iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; /* addtl frame delimiter, e.g. for netscreen, default none */ -static int bDisableLFDelim = 0; /* disbale standard LF delimiter */ -static int bUseFlowControl = 1; /* use flow control, what means indicate ourselfs a "light delayable" */ -static uchar *pszStrmDrvrAuthMode = NULL; /* authentication mode to use */ -static uchar *pszInputName = NULL; /* value for inputname property, NULL is OK and handled by core engine */ -static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */ - +static struct configSettings_s { + int iTCPSessMax; + int iTCPLstnMax; + int bSuppOctetFram; + int iStrmDrvrMode; + int bKeepAlive; + int bEmitMsgOnClose; + int iAddtlFrameDelim; + int bDisableLFDelim; + int bUseFlowControl; + uchar *pszStrmDrvrAuthMode; + uchar *pszInputName; + uchar *pszBindRuleset; +} cs; + +struct instanceConf_s { + uchar *pszBindPort; /* port to bind to */ + uchar *pszBindRuleset; /* name of ruleset to bind to */ + ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + uchar *pszInputName; /* value for inputname property, NULL is OK and handled by core engine */ + int bSuppOctetFram; + struct instanceConf_s *next; +}; + + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + int iTCPSessMax; /* max number of sessions */ + int iTCPLstnMax; /* max number of sessions */ + int iStrmDrvrMode; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */ + int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */ + int bSuppOctetFram; + sbool bDisableLFDelim; /* disable standard LF delimiter */ + sbool bUseFlowControl; /* use flow control, what means indicate ourselfs a "light delayable" */ + sbool bKeepAlive; + sbool bEmitMsgOnClose; /* emit an informational message on close by remote peer */ + uchar *pszStrmDrvrAuthMode; /* authentication mode to use */ +}; + +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + +#include "im-helper.h" /* must be included AFTER the type definitions! */ /* callbacks */ /* this shall go into a specific ACL module! */ @@ -165,48 +201,72 @@ finalize_it: } -/* accept a new ruleset to bind. Checks if it exists and complains, if not */ -static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) +/* This function is called when a new listener instace shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + * rgerhards, 2011-05-04 + */ +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) { - ruleset_t *pRuleset; - rsRetVal localRet; + instanceConf_t *inst; DEFiRet; - localRet = ruleset.GetRuleset(&pRuleset, pszName); - if(localRet == RS_RET_NOT_FOUND) { - errmsg.LogError(0, RS_RET_RULESET_NOT_FOUND, "error: ruleset '%s' not found - ignored", pszName); + CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); + + CHKmalloc(inst->pszBindPort = ustrdup((pNewVal == NULL || *pNewVal == '\0') + ? (uchar*) "10514" : pNewVal)); + if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { + inst->pszBindRuleset = NULL; + } else { + CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset)); + } + if((cs.pszInputName == NULL) || (cs.pszInputName[0] == '\0')) { + inst->pszInputName = NULL; + } else { + CHKmalloc(inst->pszInputName = ustrdup(cs.pszInputName)); + } + inst->bSuppOctetFram = cs.bSuppOctetFram; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; } - CHKiRet(localRet); - pBindRuleset = pRuleset; - DBGPRINTF("imtcp current bind ruleset %p: '%s'\n", pRuleset, pszName); finalize_it: - free(pszName); /* no longer needed */ + free(pNewVal); RETiRet; } -static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal) +static rsRetVal +addListner(modConfData_t *modConf, instanceConf_t *inst) { DEFiRet; if(pOurTcpsrv == NULL) { CHKiRet(tcpsrv.Construct(&pOurTcpsrv)); - CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, iTCPSessMax)); - CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, iTCPLstnMax)); + /* callbacks */ 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)); - CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode)); - CHKiRet(tcpsrv.SetUseFlowControl(pOurTcpsrv, bUseFlowControl)); - CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim)); - CHKiRet(tcpsrv.SetbDisableLFDelim(pOurTcpsrv, bDisableLFDelim)); - CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, bEmitMsgOnClose)); + /* params */ + CHKiRet(tcpsrv.SetKeepAlive(pOurTcpsrv, modConf->bKeepAlive)); + CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, modConf->iTCPSessMax)); + CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, modConf->iTCPLstnMax)); + CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, modConf->iStrmDrvrMode)); + CHKiRet(tcpsrv.SetUseFlowControl(pOurTcpsrv, modConf->bUseFlowControl)); + CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, modConf->iAddtlFrameDelim)); + CHKiRet(tcpsrv.SetbDisableLFDelim(pOurTcpsrv, modConf->bDisableLFDelim)); + CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, modConf->bEmitMsgOnClose)); /* now set optional params, but only if they were actually configured */ - if(pszStrmDrvrAuthMode != NULL) { - CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, pszStrmDrvrAuthMode)); + if(modConf->pszStrmDrvrAuthMode != NULL) { + CHKiRet(tcpsrv.SetDrvrAuthMode(pOurTcpsrv, modConf->pszStrmDrvrAuthMode)); } if(pPermPeersRoot != NULL) { CHKiRet(tcpsrv.SetDrvrPermPeers(pOurTcpsrv, pPermPeersRoot)); @@ -214,41 +274,113 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa } /* initialized, now add socket and listener params */ - CHKiRet(tcpsrv.SetRuleset(pOurTcpsrv, pBindRuleset)); - CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, pszInputName == NULL ? - UCHAR_CONSTANT("imtcp") : pszInputName)); - tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal); + DBGPRINTF("imtcp: trying to add port *:%s\n", inst->pszBindPort); + CHKiRet(tcpsrv.SetRuleset(pOurTcpsrv, inst->pBindRuleset)); + CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, inst->pszInputName == NULL ? + UCHAR_CONSTANT("imtcp") : inst->pszInputName)); + tcpsrv.configureTCPListen(pOurTcpsrv, inst->pszBindPort, inst->bSuppOctetFram); finalize_it: if(iRet != RS_RET_OK) { - errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet); - if(pOurTcpsrv != NULL) - tcpsrv.Destruct(&pOurTcpsrv); + errmsg.LogError(0, NO_ERRCODE, "imtcp: error %d trying to add listener", iRet); } RETiRet; } + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config variables */ + cs.pszStrmDrvrAuthMode = NULL; + resetConfigVariables(NULL, NULL); /* dummy parameters just to fulfill interface def */ +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + pModConf->iTCPSessMax = cs.iTCPSessMax; + pModConf->iTCPLstnMax = cs.iTCPLstnMax; + pModConf->iStrmDrvrMode = cs.iStrmDrvrMode; + pModConf->bEmitMsgOnClose = cs.bEmitMsgOnClose; + pModConf->bSuppOctetFram = cs.bSuppOctetFram; + pModConf->iAddtlFrameDelim = cs.iAddtlFrameDelim; + pModConf->bDisableLFDelim = cs.bDisableLFDelim; + pModConf->bUseFlowControl = cs.bUseFlowControl; + pModConf->bKeepAlive = cs.bKeepAlive; + if((cs.pszStrmDrvrAuthMode == NULL) || (cs.pszStrmDrvrAuthMode[0] == '\0')) { + loadModConf->pszStrmDrvrAuthMode = NULL; + free(cs.pszStrmDrvrAuthMode); + } else { + loadModConf->pszStrmDrvrAuthMode = cs.pszStrmDrvrAuthMode; + } + cs.pszStrmDrvrAuthMode = NULL; + + loadModConf = NULL; /* done loading */ +ENDendCnfLoad + + +/* function to generate error message if framework does not find requested ruleset */ +static inline void +std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst) +{ + errmsg.LogError(0, NO_ERRCODE, "imtcp: ruleset '%s' for port %s not found - " + "using default ruleset instead", inst->pszBindRuleset, + inst->pszBindPort); +} + +BEGINcheckCnf + instanceConf_t *inst; +CODESTARTcheckCnf + for(inst = pModConf->root ; inst != NULL ; inst = inst->next) { + std_checkRuleset(pModConf, inst); + } + if(pModConf->root == NULL) { + errmsg.LogError(0, RS_RET_NO_LISTNERS , "imtcp: module loaded, but " + "no listeners defined - no input will be gathered"); + iRet = RS_RET_NO_LISTNERS; + } +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(pModConf, inst); + } + if(pOurTcpsrv == NULL) + ABORT_FINALIZE(RS_RET_NO_RUN); + CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv)); +finalize_it: +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf + /* sorry, nothing to do here... */ +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + /* 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 - */ - CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv)); iRet = tcpsrv.Run(pOurTcpsrv); -finalize_it: 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 @@ -288,17 +420,19 @@ ENDmodExit static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { - iTCPSessMax = 200; - iTCPLstnMax = 20; - iStrmDrvrMode = 0; - bUseFlowControl = 0; - bEmitMsgOnClose = 0; - iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; - bDisableLFDelim = 0; - free(pszInputName); - pszInputName = NULL; - free(pszStrmDrvrAuthMode); - pszStrmDrvrAuthMode = NULL; + cs.iTCPSessMax = 200; + cs.iTCPLstnMax = 20; + cs.bSuppOctetFram = 1; + cs.iStrmDrvrMode = 0; + cs.bUseFlowControl = 0; + cs.bKeepAlive = 0; + cs.bEmitMsgOnClose = 0; + cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER; + cs.bDisableLFDelim = 0; + free(cs.pszInputName); + cs.pszInputName = NULL; + free(cs.pszStrmDrvrAuthMode); + cs.pszStrmDrvrAuthMode = NULL; return RS_RET_OK; } @@ -307,6 +441,8 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt @@ -326,31 +462,35 @@ CODEmodInit_QueryRegCFSLineHdlr /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverrun"), 0, eCmdHdlrGetWord, - addTCPListener, NULL, STD_LOADABLE_MODULE_ID)); + addInstance, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverkeepalive"), 0, eCmdHdlrBinary, + NULL, &cs.bKeepAlive, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserversupportoctetcountedframing"), 0, eCmdHdlrBinary, + NULL, &cs.bSuppOctetFram, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxsessions"), 0, eCmdHdlrInt, - NULL, &iTCPSessMax, STD_LOADABLE_MODULE_ID)); + NULL, &cs.iTCPSessMax, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpmaxlisteners"), 0, eCmdHdlrInt, - NULL, &iTCPLstnMax, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpservernotifyonconnectionclose"), 0, - eCmdHdlrBinary, NULL, &bEmitMsgOnClose, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0, - eCmdHdlrInt, NULL, &iStrmDrvrMode, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0, - eCmdHdlrGetWord, NULL, &pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0, - eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID)); + NULL, &cs.iTCPLstnMax, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpservernotifyonconnectionclose"), 0, eCmdHdlrBinary, + NULL, &cs.bEmitMsgOnClose, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdrivermode"), 0, eCmdHdlrInt, + NULL, &cs.iStrmDrvrMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverauthmode"), 0, eCmdHdlrGetWord, + NULL, &cs.pszStrmDrvrAuthMode, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverstreamdriverpermittedpeer"), 0, eCmdHdlrGetWord, + setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserveraddtlframedelimiter"), 0, eCmdHdlrInt, - NULL, &iAddtlFrameDelim, STD_LOADABLE_MODULE_ID)); + NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverdisablelfdelimiter"), 0, eCmdHdlrBinary, - NULL, &bDisableLFDelim, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0, - eCmdHdlrGetWord, NULL, &pszInputName, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverbindruleset"), 0, - eCmdHdlrGetWord, setRuleset, NULL, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpflowcontrol"), 0, - eCmdHdlrBinary, NULL, &bUseFlowControl, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bDisableLFDelim, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverinputname"), 0, eCmdHdlrGetWord, + NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserverbindruleset"), 0, eCmdHdlrGetWord, + NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpflowcontrol"), 0, eCmdHdlrBinary, + NULL, &cs.bUseFlowControl, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler, - resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit diff --git a/plugins/imtemplate/Makefile.am b/plugins/imtemplate/Makefile.am deleted file mode 100644 index 1825b5bc..00000000 --- a/plugins/imtemplate/Makefile.am +++ /dev/null @@ -1,6 +0,0 @@ -pkglib_LTLIBRARIES = imtemplate.la - -imtemplate_la_SOURCES = imtemplate.c -imtemplate_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) -imtemplate_la_LDFLAGS = -module -avoid-version -imtemplate_la_LIBADD = diff --git a/plugins/imtemplate/imtemplate.c b/plugins/imtemplate/imtemplate.c deleted file mode 100644 index 0e2cac11..00000000 --- a/plugins/imtemplate/imtemplate.c +++ /dev/null @@ -1,436 +0,0 @@ -/* 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 */ -#include "debug.h" /* some debug helper functions */ - -MODULE_TYPE_INPUT /* must be present for input modules, do not remove */ -MODULE_TYPE_NOKEEP - -/* 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"); - * - * To submit the message to the queue engine, we must create the message - * object and fill it with data. If it contains a syslog message that must - * be parsed, we can add a flag that requests parsing. Otherwise, we must - * fill the properties ourselves. That is 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)); - MsgSetRawMsg(pMsg, msg); - MsgSetHOSTNAME(pMsg, LocalHostName); - MsgSetTAG(pMsg, "rsyslogd:"); - pMsg->iFacility = LOG_FAC(pri); - pMsg->iSeverity = LOG_PRI(pri); - flags |= INTERNAL_MSG; - logmsg(pMsg, flags); / * some time, CHKiRet() will work here, too [today NOT!] * / - * - * NOTE: for up-to-date usage samples, see the other provided input modules. - * A good starting point is probably imuxsock. - * - * 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(glbl.GetMaxLine * 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/imttcp/imttcp.c b/plugins/imttcp/imttcp.c index c7f546cc..c72886b3 100644 --- a/plugins/imttcp/imttcp.c +++ b/plugins/imttcp/imttcp.c @@ -122,6 +122,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imttcp") /* static data */ DEF_IMOD_STATIC_DATA @@ -135,6 +136,10 @@ DEFobjCurrIf(ruleset) /* config settings */ +struct modConfData_s { + EMPTY_STRUCT; +}; + typedef struct configSettings_s { int bEmitMsgOnClose; /* emit an informational message on close by remote peer */ int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */ @@ -808,7 +813,7 @@ static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) rsRetVal localRet; DEFiRet; - localRet = ruleset.GetRuleset(&pRuleset, pszName); + localRet = ruleset.GetRuleset(ourConf, &pRuleset, pszName); if(localRet == RS_RET_NOT_FOUND) { errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName); } @@ -984,6 +989,7 @@ startupListeners() RETiRet; } + /* This function is called to gather input. */ BEGINrunInput @@ -1121,7 +1127,7 @@ CODEmodInit_QueryRegCFSLineHdlr /* initialize "read-only" thread attributes */ pthread_attr_init(&sessThrdAttr); pthread_attr_setdetachstate(&sessThrdAttr, PTHREAD_CREATE_DETACHED); - pthread_attr_setstacksize(&sessThrdAttr, 200*1024); + pthread_attr_setstacksize(&sessThrdAttr, 4096*1024); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputttcpserverrun"), 0, eCmdHdlrGetWord, diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c index e68bcf37..6abeab07 100644 --- a/plugins/imudp/imudp.c +++ b/plugins/imudp/imudp.c @@ -6,7 +6,7 @@ * * File begun on 2007-12-21 by RGerhards (extracted from syslogd.c) * - * Copyright 2007-2009 Rainer Gerhards and Adiscon GmbH. + * Copyright 2007-2011 Rainer Gerhards and Adiscon GmbH. * * This file is part of rsyslog. * @@ -26,6 +26,7 @@ * A copy of the GPL can be found in the file "COPYING" in this distribution. */ #include "config.h" +#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> @@ -51,10 +52,12 @@ #include "datetime.h" #include "prop.h" #include "ruleset.h" +#include "statsobj.h" #include "unicode-helper.h" MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imudp") /* defines */ @@ -66,6 +69,16 @@ DEFobjCurrIf(net) DEFobjCurrIf(datetime) DEFobjCurrIf(prop) DEFobjCurrIf(ruleset) +DEFobjCurrIf(statsobj) + + +static struct lstn_s { + struct lstn_s *next; + int sock; /* socket */ + ruleset_t *pRuleset; /* bound ruleset */ + statsobj_t *stats; /* listener stats */ + STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) +} *lcnfRoot = NULL, *lcnfLast = NULL; static int bDoACLCheck; /* are ACL checks neeed? Cached once immediately before listener startup */ static int iMaxLine; /* maximum UDP message size supported */ @@ -73,141 +86,111 @@ static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitte * This shall prevent remote DoS when the "discard on disallowed sender" * message is configured to be logged on occurance of such a case. */ -static int *udpLstnSocks = NULL; /* Internet datagram sockets, first element is nbr of elements - * read-only after init(), but beware of restart! */ -static ruleset_t **udpRulesets = NULL; /* ruleset to be used with sockets in question (entry 0 is empty) */ -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 */ static prop_t *pInputName = NULL; /* our inputName currently is always "imudp", and this will hold it */ -static uchar *pszSchedPolicy = NULL; /* scheduling policy string */ -static int iSchedPolicy; /* scheduling policy as SCHED_xxx */ -static int iSchedPrio; /* scheduling priority */ -static int seen_iSchedPrio = 0; /* have we seen scheduling priority in the config file? */ -static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */ -#define TIME_REQUERY_DFLT 2 -static int iTimeRequery = TIME_REQUERY_DFLT;/* how often is time to be queried inside tight recv loop? 0=always */ - -/* config settings */ - -static rsRetVal check_scheduling_priority(int report_error) -{ - DEFiRet; - -#ifdef HAVE_SCHED_GET_PRIORITY_MAX - if (iSchedPrio < sched_get_priority_min(iSchedPolicy) || - iSchedPrio > sched_get_priority_max(iSchedPolicy)) { - if (report_error) - errmsg.LogError(errno, NO_ERRCODE, - "imudp: scheduling priority %d out of range (%d - %d)" - " for scheduling policy '%s' - ignoring settings", - iSchedPrio, - sched_get_priority_min(iSchedPolicy), - sched_get_priority_max(iSchedPolicy), - pszSchedPolicy); - ABORT_FINALIZE(RS_RET_VALIDATION_RUN); - } -#endif - -finalize_it: - RETiRet; -} -/* Set scheduling priority in the supplied variable (will be iSchedPrio) - * and record that we have seen the directive (in seen_iSchedPrio). +#define TIME_REQUERY_DFLT 2 +#define SCHED_PRIO_UNSET -12345678 /* a value that indicates that the scheduling priority has not been set */ +/* config vars for legacy config system */ +static struct configSettings_s { + uchar *pszBindAddr; /* IP to bind socket to */ + uchar *pszSchedPolicy; /* scheduling policy string */ + uchar *pszBindRuleset; /* name of Ruleset to bind to */ + int iSchedPrio; /* scheduling priority */ + int iTimeRequery; /* how often is time to be queried inside tight recv loop? 0=always */ +} cs; + +struct instanceConf_s { + uchar *pszBindAddr; /* IP to bind socket to */ + uchar *pszBindPort; /* Port to bind socket to */ + uchar *pszBindRuleset; /* name of ruleset to bind to */ + ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + struct instanceConf_s *next; +}; + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + uchar *pszSchedPolicy; /* scheduling policy string */ + int iSchedPolicy; /* scheduling policy as SCHED_xxx */ + int iSchedPrio; /* scheduling priority */ + int iTimeRequery; /* how often is time to be queried inside tight recv loop? 0=always */ +}; +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + +#include "im-helper.h" /* must be included AFTER the type definitions! */ + + + +/* This function is called when a new listener instace shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + * rgerhards, 2011-05-04 */ -static rsRetVal set_scheduling_priority(void *pVal, int value) +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) { + instanceConf_t *inst; DEFiRet; - if (seen_iSchedPrio) { - errmsg.LogError(0, NO_ERRCODE, "directive already seen"); - ABORT_FINALIZE(RS_RET_VALIDATION_RUN); - } - *(int *)pVal = value; - seen_iSchedPrio = 1; - if (pszSchedPolicy != NULL) - CHKiRet(check_scheduling_priority(1)); - -finalize_it: - RETiRet; -} - -/* Set scheduling policy in iSchedPolicy */ -static rsRetVal set_scheduling_policy(void *pVal, uchar *pNewVal) -{ - int have_sched_policy = 0; - DEFiRet; - - if (pszSchedPolicy != NULL) { - errmsg.LogError(0, NO_ERRCODE, "directive already seen"); - ABORT_FINALIZE(RS_RET_VALIDATION_RUN); + CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); + CHKmalloc(inst->pszBindPort = ustrdup((pNewVal == NULL || *pNewVal == '\0') + ? (uchar*) "514" : pNewVal)); + if((cs.pszBindAddr == NULL) || (cs.pszBindAddr[0] == '\0')) { + inst->pszBindAddr = NULL; + } else { + CHKmalloc(inst->pszBindAddr = ustrdup(cs.pszBindAddr)); } - *((uchar**)pVal) = pNewVal; /* pVal is pszSchedPolicy */ - if (0) { /* trick to use conditional compilation */ -#ifdef SCHED_FIFO - } else if (!strcasecmp((char*)pszSchedPolicy, "fifo")) { - iSchedPolicy = SCHED_FIFO; - have_sched_policy = 1; -#endif -#ifdef SCHED_RR - } else if (!strcasecmp((char*)pszSchedPolicy, "rr")) { - iSchedPolicy = SCHED_RR; - have_sched_policy = 1; -#endif -#ifdef SCHED_OTHER - } else if (!strcasecmp((char*)pszSchedPolicy, "other")) { - iSchedPolicy = SCHED_OTHER; - have_sched_policy = 1; -#endif + if((cs.pszBindRuleset == NULL) || (cs.pszBindRuleset[0] == '\0')) { + inst->pszBindRuleset = NULL; } else { - errmsg.LogError(errno, NO_ERRCODE, - "imudp: invalid scheduling policy '%s' " - "- ignoring setting", pszSchedPolicy); + CHKmalloc(inst->pszBindRuleset = ustrdup(cs.pszBindRuleset)); } - if (have_sched_policy == 0) { - free(pszSchedPolicy); - pszSchedPolicy = NULL; - ABORT_FINALIZE(RS_RET_VALIDATION_RUN); + inst->pBindRuleset = NULL; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; } - if (seen_iSchedPrio) - CHKiRet(check_scheduling_priority(1)); finalize_it: + free(pNewVal); RETiRet; } /* This function is called when a new listener shall be added. It takes - * the configured parameters, tries to bind the socket and, if that + * the instance config description, 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) +static inline rsRetVal +addListner(instanceConf_t *inst) { DEFiRet; uchar *bindAddr; int *newSocks; - int *tmpSocks; - int iSrc, iDst; - ruleset_t **tmpRulesets; + int iSrc; + struct lstn_s *newlcnfinfo; + uchar *bindName; + uchar *port; + uchar statname[64]; /* 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; +#if 0 //<<<<<<< HEAD - DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n", - (bindAddr == NULL) ? (uchar*)"*" : bindAddr, pNewVal); + DBGPRINTF("imudp: trying to open port at %s:%s.\n", + (inst->pszBindAddr == NULL) ? (uchar*)"*" : inst->pszBindAddr, inst->pszBindPort); - newSocks = net.create_udp_socket(bindAddr, (pNewVal == NULL || *pNewVal == '\0') ? (uchar*) "514" : pNewVal, 1); + newSocks = net.create_udp_socket(inst->pszBindAddr, inst->pszBindPort, 1); if(newSocks != NULL) { /* we now need to add the new sockets to the existing set */ if(udpLstnSocks == NULL) { @@ -215,7 +198,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal) udpLstnSocks = newSocks; CHKmalloc(udpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (newSocks[0] + 1))); for(iDst = 1 ; iDst <= newSocks[0] ; ++iDst) - udpRulesets[iDst] = pBindRuleset; + udpRulesets[iDst] = inst->pBindRuleset; } else { /* we need to add them */ tmpSocks = (int*) MALLOC(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0])); @@ -238,7 +221,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal) } for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc, ++iDst) { tmpSocks[iDst] = newSocks[iSrc]; - tmpRulesets[iDst] = pBindRuleset; + tmpRulesets[iDst] = inst->pBindRuleset; } tmpSocks[0] = udpLstnSocks[0] + newSocks[0]; free(newSocks); @@ -247,35 +230,64 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal) free(udpRulesets); udpRulesets = tmpRulesets; } +#else //======= + if(inst->pszBindAddr == NULL) + bindAddr = NULL; + else if(inst->pszBindAddr[0] == '*' && inst->pszBindAddr[1] == '\0') + bindAddr = NULL; + else + bindAddr = inst->pszBindAddr; + bindName = (bindAddr == NULL) ? (uchar*)"*" : bindAddr; + port = (inst->pszBindPort == NULL || *inst->pszBindPort == '\0') ? (uchar*) "514" : inst->pszBindPort; + + DBGPRINTF("Trying to open syslog UDP ports at %s:%s.\n", bindName, inst->pszBindPort); + + newSocks = net.create_udp_socket(bindAddr, port, 1); + if(newSocks != NULL) { + /* we now need to add the new sockets to the existing set */ + /* ready to copy */ + for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc) { + CHKmalloc(newlcnfinfo = (struct lstn_s*) MALLOC(sizeof(struct lstn_s))); + newlcnfinfo->next = NULL; + newlcnfinfo->sock = newSocks[iSrc]; + newlcnfinfo->pRuleset = inst->pBindRuleset; + /* support statistics gathering */ + CHKiRet(statsobj.Construct(&(newlcnfinfo->stats))); + snprintf((char*)statname, sizeof(statname), "imudp(%s:%s)", bindName, port); + statname[sizeof(statname)-1] = '\0'; /* just to be on the save side... */ + CHKiRet(statsobj.SetName(newlcnfinfo->stats, statname)); + STATSCOUNTER_INIT(newlcnfinfo->ctrSubmit, newlcnfinfo->mutCtrSubmit); + CHKiRet(statsobj.AddCounter(newlcnfinfo->stats, UCHAR_CONSTANT("submitted"), + ctrType_IntCtr, &(newlcnfinfo->ctrSubmit))); + CHKiRet(statsobj.ConstructFinalize(newlcnfinfo->stats)); + /* link to list. Order must be preserved to take care for + * conflicting matches. + */ + if(lcnfRoot == NULL) + lcnfRoot = newlcnfinfo; + if(lcnfLast == NULL) + lcnfLast = newlcnfinfo; + else { + lcnfLast->next = newlcnfinfo; + lcnfLast = newlcnfinfo; +#endif //>>>>>>> ef34821a2737799f48c3032b9616418e4f7fa34f + } } } finalize_it: - free(pNewVal); /* in any case, this is no longer needed */ - + free(newSocks); RETiRet; } -/* accept a new ruleset to bind. Checks if it exists and complains, if not */ -static rsRetVal -setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) +static inline void +std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst) { - ruleset_t *pRuleset; - rsRetVal localRet; - DEFiRet; - - localRet = ruleset.GetRuleset(&pRuleset, pszName); - if(localRet == RS_RET_NOT_FOUND) { - errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName); - } - CHKiRet(localRet); - pBindRuleset = pRuleset; - DBGPRINTF("imudp current bind ruleset %p: '%s'\n", pRuleset, pszName); - -finalize_it: - free(pszName); /* no longer needed */ - RETiRet; + errmsg.LogError(0, NO_ERRCODE, "imudp: ruleset '%s' for %s:%s not found - " + "using default ruleset instead", inst->pszBindRuleset, + inst->pszBindAddr == NULL ? "*" : (char*) inst->pszBindAddr, + inst->pszBindPort); } @@ -294,8 +306,7 @@ finalize_it: * on scheduling order. -- rgerhards, 2008-10-02 */ static inline rsRetVal -processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted, - ruleset_t *pRuleset) +processSocket(thrdInfo_t *pThrd, struct lstn_s *lstn, struct sockaddr_storage *frominetPrev, int *pbIsPermitted) { DEFiRet; int iNbrTimeUsed; @@ -315,7 +326,7 @@ processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev, if(pThrd->bShallStop == TRUE) ABORT_FINALIZE(RS_RET_FORCE_TERM); socklen = sizeof(struct sockaddr_storage); - lenRcvBuf = recvfrom(fd, (char*) pRcvBuf, iMaxLine, 0, (struct sockaddr *)&frominet, &socklen); + lenRcvBuf = recvfrom(lstn->sock, (char*) pRcvBuf, iMaxLine, 0, (struct sockaddr *)&frominet, &socklen); if(lenRcvBuf < 0) { if(errno != EINTR && errno != EAGAIN) { rs_strerror_r(errno, errStr, sizeof(errStr)); @@ -360,23 +371,24 @@ processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev, *pbIsPermitted = 1; /* no check -> everything permitted */ } - DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", fd, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf); + DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", lstn->sock, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf); if(*pbIsPermitted != 0) { - if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) { + if((runModConf->iTimeRequery == 0) || (iNbrTimeUsed++ % runModConf->iTimeRequery) == 0) { datetime.getCurrTime(&stTime, &ttGenTime); } /* we now create our own message object and submit it to the queue */ CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime)); MsgSetRawMsg(pMsg, (char*)pRcvBuf, lenRcvBuf); MsgSetInputName(pMsg, pInputName); - MsgSetRuleset(pMsg, pRuleset); + MsgSetRuleset(pMsg, lstn->pRuleset); MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY); pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME | NEEDS_DNSRESOL; if(*pbIsPermitted == 2) pMsg->msgFlags |= NEEDS_ACLCHK_U; /* request ACL check after resolution */ CHKiRet(msgSetFromSockinfo(pMsg, &frominet)); CHKiRet(submitMsg(pMsg)); + STATSCOUNTER_INC(lstn->ctrSubmit, lstn->mutCtrSubmit); } } @@ -389,42 +401,128 @@ finalize_it: RETiRet; } -static void set_thread_schedparam(void) + +/* check configured scheduling priority. + * Precondition: iSchedPolicy must have been set + */ +static inline rsRetVal +checkSchedulingPriority(modConfData_t *modConf) { - struct sched_param sparam; + DEFiRet; - if (pszSchedPolicy != NULL && seen_iSchedPrio == 0) { +#ifdef HAVE_SCHED_GET_PRIORITY_MAX + if( modConf->iSchedPrio < sched_get_priority_min(modConf->iSchedPolicy) + || modConf->iSchedPrio > sched_get_priority_max(modConf->iSchedPolicy)) { errmsg.LogError(0, NO_ERRCODE, + "imudp: scheduling priority %d out of range (%d - %d)" + " for scheduling policy '%s' - ignoring settings", + modConf->iSchedPrio, + sched_get_priority_min(modConf->iSchedPolicy), + sched_get_priority_max(modConf->iSchedPolicy), + modConf->pszSchedPolicy); + ABORT_FINALIZE(RS_RET_VALIDATION_RUN); + } +#endif + +finalize_it: + RETiRet; +} + + +/* check scheduling policy string and, if valid, set its + * numeric equivalent in current load config + */ +static rsRetVal +checkSchedulingPolicy(modConfData_t *modConf) +{ + DEFiRet; + + if (0) { /* trick to use conditional compilation */ +#ifdef SCHED_FIFO + } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "fifo")) { + modConf->iSchedPolicy = SCHED_FIFO; +#endif +#ifdef SCHED_RR + } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "rr")) { + modConf->iSchedPolicy = SCHED_RR; +#endif +#ifdef SCHED_OTHER + } else if (!strcasecmp((char*)modConf->pszSchedPolicy, "other")) { + modConf->iSchedPolicy = SCHED_OTHER; +#endif + } else { + errmsg.LogError(errno, NO_ERRCODE, + "imudp: invalid scheduling policy '%s' " + "- ignoring setting", modConf->pszSchedPolicy); + ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS); + } +finalize_it: + RETiRet; +} + +/* checks scheduling parameters during config check phase */ +static rsRetVal +checkSchedParam(modConfData_t *modConf) +{ + DEFiRet; + + if(modConf->pszSchedPolicy != NULL && modConf->iSchedPrio == SCHED_PRIO_UNSET) { + errmsg.LogError(0, RS_RET_ERR_SCHED_PARAMS, "imudp: scheduling policy set, but without priority - ignoring settings"); - } else if (pszSchedPolicy == NULL && seen_iSchedPrio != 0) { - errmsg.LogError(0, NO_ERRCODE, + ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS); + } else if(modConf->pszSchedPolicy == NULL && modConf->iSchedPrio != SCHED_PRIO_UNSET) { + errmsg.LogError(0, RS_RET_ERR_SCHED_PARAMS, "imudp: scheduling priority set, but without policy - ignoring settings"); - } else if (pszSchedPolicy != NULL && seen_iSchedPrio != 0 && - check_scheduling_priority(0) == 0) { + ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS); + } else if(modConf->pszSchedPolicy != NULL && modConf->iSchedPrio != SCHED_PRIO_UNSET) { + /* we have parameters set, so check them */ + CHKiRet(checkSchedulingPolicy(modConf)); + CHKiRet(checkSchedulingPriority(modConf)); + } else { /* nothing set */ + modConf->iSchedPrio = SCHED_PRIO_UNSET; /* prevents doing the activation call */ + } #ifndef HAVE_PTHREAD_SETSCHEDPARAM - errmsg.LogError(0, NO_ERRCODE, - "imudp: cannot set thread scheduling policy, " - "pthread_setschedparam() not available"); -#else - int err; - - memset(&sparam, 0, sizeof sparam); - sparam.sched_priority = iSchedPrio; - dbgprintf("imudp trying to set sched policy to '%s', prio %d\n", - pszSchedPolicy, iSchedPrio); - err = pthread_setschedparam(pthread_self(), iSchedPolicy, &sparam); - if (err != 0) { - errmsg.LogError(err, NO_ERRCODE, "imudp: pthread_setschedparam() failed"); - } + errmsg.LogError(0, NO_ERRCODE, + "imudp: cannot set thread scheduling policy, " + "pthread_setschedparam() not available"); + ABORT_FINALIZE(RS_RET_ERR_SCHED_PARAMS); #endif - } - if (pszSchedPolicy != NULL) { - free(pszSchedPolicy); - pszSchedPolicy = NULL; +finalize_it: + if(iRet != RS_RET_OK) + modConf->iSchedPrio = SCHED_PRIO_UNSET; /* prevents doing the activation call */ + + RETiRet; +} + +/* set the configured scheduling policy (if possible) */ +static rsRetVal +setSchedParams(modConfData_t *modConf) +{ + DEFiRet; + +# ifdef HAVE_PTHREAD_SETSCHEDPARAM + int err; + struct sched_param sparam; + + if(modConf->iSchedPrio == SCHED_PRIO_UNSET) + FINALIZE; + + memset(&sparam, 0, sizeof sparam); + sparam.sched_priority = modConf->iSchedPrio; + dbgprintf("imudp trying to set sched policy to '%s', prio %d\n", + modConf->pszSchedPolicy, modConf->iSchedPrio); + err = pthread_setschedparam(pthread_self(), modConf->iSchedPolicy, &sparam); + if(err != 0) { + errmsg.LogError(err, NO_ERRCODE, "imudp: pthread_setschedparam() failed - ignoring"); } +# endif + +finalize_it: + RETiRet; } + /* This function implements the main reception loop. Depending on the environment, * we either use the traditional (but slower) select() or the Linux-specific epoll() * interface. ./configure settings control which one is used. @@ -443,15 +541,20 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) struct epoll_event *udpEPollEvt = NULL; struct epoll_event currEvt[NUM_EPOLL_EVENTS]; char errStr[1024]; + struct lstn_s *lstn; + int nLstn; /* start "name caching" algo by making sure the previous system indicator * is invalidated. */ - set_thread_schedparam(); bIsPermitted = 0; memset(&frominetPrev, 0, sizeof(frominetPrev)); - CHKmalloc(udpEPollEvt = calloc(udpLstnSocks[0], sizeof(struct epoll_event))); + /* count num listeners -- do it here in order to avoid inconsistency */ + nLstn = 0; + for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) + ++nLstn; + CHKmalloc(udpEPollEvt = calloc(nLstn, sizeof(struct epoll_event))); #if defined(EPOLL_CLOEXEC) && defined(HAVE_EPOLL_CREATE1) DBGPRINTF("imudp uses epoll_create1()\n"); @@ -471,16 +574,18 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) /* fill the epoll set - we need to do this only once, as the set * can not change dyamically. */ - for (i = 0; i < *udpLstnSocks; i++) { - if (udpLstnSocks[i+1] != -1) { + i = 0; + for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) { + if(lstn->sock != -1) { udpEPollEvt[i].events = EPOLLIN | EPOLLET; - udpEPollEvt[i].data.u64 = i+1; - if(epoll_ctl(efd, EPOLL_CTL_ADD, udpLstnSocks[i+1], &(udpEPollEvt[i])) < 0) { + udpEPollEvt[i].data.u64 = (long long unsigned) lstn; + if(epoll_ctl(efd, EPOLL_CTL_ADD, lstn->sock, &(udpEPollEvt[i])) < 0) { rs_strerror_r(errno, errStr, sizeof(errStr)); errmsg.LogError(errno, NO_ERRCODE, "epoll_ctrl failed on fd %d with %s\n", - udpLstnSocks[i+1], errStr); + lstn->sock, errStr); } } + i++; } while(1) { @@ -492,8 +597,7 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) break; /* terminate input! */ for(i = 0 ; i < nfds ; ++i) { - processSocket(pThrd, udpLstnSocks[currEvt[i].data.u64], &frominetPrev, &bIsPermitted, - udpRulesets[currEvt[i].data.u64]); + processSocket(pThrd, (struct lstn_s*)currEvt[i].data.u64, &frominetPrev, &bIsPermitted); } } @@ -510,36 +614,31 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) DEFiRet; int maxfds; int nfds; - int i; fd_set readfds; struct sockaddr_storage frominetPrev; int bIsPermitted; + struct lstn_s *lstn; /* start "name caching" algo by making sure the previous system indicator * is invalidated. */ - set_thread_schedparam(); bIsPermitted = 0; memset(&frominetPrev, 0, sizeof(frominetPrev)); DBGPRINTF("imudp uses select()\n"); 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.. + /* Add the Unix Domain Sockets to the list of read descriptors. */ maxfds = 0; FD_ZERO(&readfds); /* Add the UDP listen sockets to the list of read descriptors. */ - for (i = 0; i < *udpLstnSocks; i++) { - if (udpLstnSocks[i+1] != -1) { + for(lstn = lcnfRoot ; lstn != NULL ; lstn = lstn->next) { + if (lstn->sock != -1) { if(Debug) - net.debugListenInfo(udpLstnSocks[i+1], "UDP"); - FD_SET(udpLstnSocks[i+1], &readfds); - if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1]; + net.debugListenInfo(lstn->sock, "UDP"); + FD_SET(lstn->sock, &readfds); + if(lstn->sock>maxfds) maxfds=lstn->sock; } } if(Debug) { @@ -555,10 +654,9 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) if(glbl.GetGlobalInputTermState() == 1) break; /* terminate input! */ - for(i = 0; nfds && i < *udpLstnSocks; i++) { - if(FD_ISSET(udpLstnSocks[i+1], &readfds)) { - processSocket(pThrd, udpLstnSocks[i+1], &frominetPrev, &bIsPermitted, - udpRulesets[i+1]); + for(lstn = lcnfRoot ; nfds && lstn != NULL ; lstn = lstn->next) { + if(FD_ISSET(lstn->sock, &readfds)) { + processSocket(pThrd, lstn, &frominetPrev, &bIsPermitted); --nfds; /* indicate we have processed one descriptor */ } } @@ -569,8 +667,95 @@ rsRetVal rcvMainLoop(thrdInfo_t *pThrd) } #endif /* #if HAVE_EPOLL_CREATE1 */ + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init legacy config vars */ + cs.pszBindRuleset = NULL; + cs.pszSchedPolicy = NULL; + cs.pszBindAddr = NULL; + cs.iSchedPrio = SCHED_PRIO_UNSET; + cs.iTimeRequery = TIME_REQUERY_DFLT; +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system + * TODO: when we add the new config system, we must decide on priority + * already-set module options should not be overwritable by the legacy + * system (though this is debatable and should at least trigger an error + * message if the equivalent legacy option is selected as well) + * rgerhards, 2011-05-04 + */ + loadModConf->iSchedPrio = cs.iSchedPrio; + loadModConf->iTimeRequery = cs.iTimeRequery; + if((cs.pszSchedPolicy == NULL) || (cs.pszSchedPolicy[0] == '\0')) { + loadModConf->pszSchedPolicy = NULL; + } else { + CHKmalloc(loadModConf->pszSchedPolicy = ustrdup(cs.pszSchedPolicy)); + } + +finalize_it: + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(cs.pszBindRuleset); + free(cs.pszSchedPolicy); + free(cs.pszBindAddr); +ENDendCnfLoad + + +BEGINcheckCnf + instanceConf_t *inst; +CODESTARTcheckCnf + checkSchedParam(pModConf); /* this can not cause fatal errors */ + for(inst = pModConf->root ; inst != NULL ; inst = inst->next) { + std_checkRuleset(pModConf, inst); + } + if(pModConf->root == NULL) { + errmsg.LogError(0, RS_RET_NO_LISTNERS , "imudp: module loaded, but " + "no listeners defined - no input will be gathered"); + iRet = RS_RET_NO_LISTNERS; + } +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(inst); + } + /* if we could not set up any listners, there is no point in running... */ + if(lcnfRoot == NULL) { + errmsg.LogError(0, NO_ERRCODE, "imudp: no listeners could be started, " + "input not activated.\n"); + ABORT_FINALIZE(RS_RET_NO_RUN); + } + + setSchedParams(pModConf); +finalize_it: +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf + /* caching various settings */ + iMaxLine = glbl.GetMaxLine(); + CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char))); +finalize_it: +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf +ENDfreeCnf + /* This function is called to gather input. - * Note that udpLstnSocks must be non-NULL because otherwise we would not have + * Note that sock must be non-NULL because otherwise we would not have * indicated that we want to run (or we have a programming error ;)). -- rgerhards, 2008-10-02 */ BEGINrunInput @@ -582,49 +767,40 @@ ENDrunInput /* initialize and return if will run or not */ BEGINwillRun CODESTARTwillRun - /* we need to create the inputName property (only once during our lifetime) */ - CHKiRet(prop.Construct(&pInputName)); - CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imudp"), sizeof("imudp") - 1)); - CHKiRet(prop.ConstructFinalize(pInputName)); - net.PrintAllowedSenders(1); /* UDP */ net.HasRestrictions(UCHAR_CONSTANT("UDP"), &bDoACLCheck); /* UDP */ - - /* if we could not set up any listners, there is no point in running... */ - if(udpLstnSocks == NULL) - ABORT_FINALIZE(RS_RET_NO_RUN); - - iMaxLine = glbl.GetMaxLine(); - - CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char))); -finalize_it: ENDwillRun BEGINafterRun + struct lstn_s *lstn, *lstnDel; CODESTARTafterRun /* do cleanup here */ net.clearAllowedSenders((uchar*)"UDP"); - if(udpLstnSocks != NULL) { - net.closeUDPListenSockets(udpLstnSocks); - udpLstnSocks = NULL; - free(udpRulesets); - udpRulesets = NULL; + for(lstn = lcnfRoot ; lstn != NULL ; ) { + statsobj.Destruct(&(lstn->stats)); + close(lstn->sock); + lstnDel = lstn; + lstn = lstn->next; + free(lstnDel); } + lcnfRoot = lcnfLast = NULL; if(pRcvBuf != NULL) { free(pRcvBuf); pRcvBuf = NULL; } - if(pInputName != NULL) - prop.Destruct(&pInputName); ENDafterRun BEGINmodExit CODESTARTmodExit + if(pInputName != NULL) + prop.Destruct(&pInputName); + /* release what we no longer need */ objRelease(errmsg, CORE_COMPONENT); objRelease(glbl, CORE_COMPONENT); + objRelease(statsobj, CORE_COMPONENT); objRelease(datetime, CORE_COMPONENT); objRelease(prop, CORE_COMPONENT); objRelease(ruleset, CORE_COMPONENT); @@ -642,16 +818,27 @@ ENDisCompatibleWithFeature BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { - if(pszBindAddr != NULL) { - free(pszBindAddr); - pszBindAddr = NULL; + if(cs.pszBindAddr != NULL) { + free(cs.pszBindAddr); + cs.pszBindAddr = NULL; + } + if(cs.pszSchedPolicy != NULL) { + free(cs.pszSchedPolicy); + cs.pszSchedPolicy = NULL; + } + if(cs.pszBindRuleset != NULL) { + free(cs.pszBindRuleset); + cs.pszBindRuleset = NULL; } - iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */ + cs.iSchedPrio = SCHED_PRIO_UNSET; + cs.iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */ return RS_RET_OK; } @@ -662,24 +849,30 @@ CODESTARTmodInit CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); CHKiRet(objUse(ruleset, CORE_COMPONENT)); CHKiRet(objUse(net, LM_NET_FILENAME)); + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.Construct(&pInputName)); + CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imudp"), sizeof("imudp") - 1)); + CHKiRet(prop.ConstructFinalize(pInputName)); + /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputudpserverbindruleset", 0, eCmdHdlrGetWord, - setRuleset, NULL, STD_LOADABLE_MODULE_ID)); + NULL, &cs.pszBindRuleset, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord, - addListner, NULL, STD_LOADABLE_MODULE_ID)); + addInstance, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord, - NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID)); + NULL, &cs.pszBindAddr, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpolicy", 0, eCmdHdlrGetWord, - &set_scheduling_policy, NULL, STD_LOADABLE_MODULE_ID)); + NULL, &cs.pszSchedPolicy, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt, - &set_scheduling_priority, NULL, STD_LOADABLE_MODULE_ID)); + NULL, &cs.iSchedPrio, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpservertimerequery", 0, eCmdHdlrInt, - NULL, &iTimeRequery, STD_LOADABLE_MODULE_ID)); + NULL, &cs.iTimeRequery, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); ENDmodInit diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c index 251f4786..fe04c8f2 100644 --- a/plugins/imuxsock/imuxsock.c +++ b/plugins/imuxsock/imuxsock.c @@ -34,6 +34,7 @@ #include <string.h> #include <errno.h> #include <unistd.h> +#include <fcntl.h> #include <sys/stat.h> #include <sys/un.h> #include <sys/socket.h> @@ -57,6 +58,7 @@ MODULE_TYPE_INPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imuxsock") /* defines */ #define MAXFUNIX 50 @@ -74,6 +76,9 @@ MODULE_TYPE_NOKEEP #define SYSTEMD_PATH_LOG SYSTEMD_JOURNAL "/syslog" #endif +/* forward definitions */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + /* emulate struct ucred for platforms that do not have it */ #ifndef HAVE_SCM_CREDENTIALS struct ucred { int pid; }; @@ -89,10 +94,12 @@ DEF_IMOD_STATIC_DATA DEFobjCurrIf(errmsg) DEFobjCurrIf(glbl) DEFobjCurrIf(prop) +DEFobjCurrIf(net) DEFobjCurrIf(parser) DEFobjCurrIf(datetime) DEFobjCurrIf(statsobj) + statsobj_t *modStats; STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) STATSCOUNTER_DEF(ctrLostRatelimit, mutCtrLostRatelimit) @@ -141,7 +148,9 @@ typedef struct lstn_s { sbool bParseHost; /* should parser parse host name? read-only after startup */ sbool bCreatePath; /* auto-creation of socket directory? */ sbool bUseCreds; /* pull original creator credentials from socket */ + sbool bAnnotate; /* annotate events with trusted properties */ sbool bWritePid; /* write original PID into tag */ + sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */ } lstn_t; static lstn_t listeners[MAXFUNIX]; @@ -154,26 +163,64 @@ static int startIndexUxLocalSockets; /* process fd from that index on (used to static int nfd = 1; /* number of Unix sockets open / read-only after startup */ static int sd_fds = 0; /* number of systemd activated sockets */ -/* config settings */ -static int bOmitLocalLogging = 0; -static uchar *pLogSockName = NULL; -static uchar *pLogHostName = NULL; /* host name to use with this socket */ -static int bUseFlowCtl = 0; /* use flow control or not (if yes, only LIGHT is used! */ -static int bIgnoreTimestamp = 1; /* ignore timestamps present in the incoming message? */ -static int bWritePid = 0; /* use credentials from recvmsg() and fixup PID in TAG */ -static int bWritePidSysSock = 0; /* use credentials from recvmsg() and fixup PID in TAG */ +/* config vars for legacy config system */ #define DFLT_bCreatePath 0 -static int bCreatePath = DFLT_bCreatePath; /* auto-create socket path? */ -#define DFLT_ratelimitInterval 5 -static int ratelimitInterval = DFLT_ratelimitInterval; /* interval in seconds, 0 = off */ -static int ratelimitIntervalSysSock = DFLT_ratelimitInterval; +#define DFLT_ratelimitInterval 0 #define DFLT_ratelimitBurst 200 -static int ratelimitBurst = DFLT_ratelimitBurst; /* max nbr of messages in interval */ -static int ratelimitBurstSysSock = DFLT_ratelimitBurst; /* max nbr of messages in interval */ #define DFLT_ratelimitSeverity 1 /* do not rate-limit emergency messages */ -static int ratelimitSeverity = DFLT_ratelimitSeverity; -static int ratelimitSeveritySysSock = DFLT_ratelimitSeverity; +static struct configSettings_s { + int bOmitLocalLogging; + uchar *pLogSockName; + uchar *pLogHostName; /* host name to use with this socket */ + int bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */ + int bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */ + int bUseSysTimeStamp; /* use timestamp from system (rather than from message) */ + int bUseSysTimeStampSysSock; /* same, for system log socket */ + int bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */ + int bWritePidSysSock; /* use credentials from recvmsg() and fixup PID in TAG */ + int bCreatePath; /* auto-create socket path? */ + int ratelimitInterval; /* interval in seconds, 0 = off */ + int ratelimitIntervalSysSock; + int ratelimitBurst; /* max nbr of messages in interval */ + int ratelimitBurstSysSock; + int ratelimitSeverity; + int ratelimitSeveritySysSock; + int bAnnotate; /* annotate trusted properties */ + int bAnnotateSysSock; /* same, for system log socket */ +} cs; + +struct instanceConf_s { + uchar *sockName; + uchar *pLogHostName; /* host name to use with this socket */ + sbool bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */ + sbool bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */ + sbool bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */ + sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */ + int bCreatePath; /* auto-create socket path? */ + int ratelimitInterval; /* interval in seconds, 0 = off */ + int ratelimitBurst; /* max nbr of messages in interval */ + int ratelimitSeverity; + int bAnnotate; /* annotate trusted properties */ + struct instanceConf_s *next; +}; + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + uchar *pLogSockName; + int ratelimitIntervalSysSock; + int ratelimitBurstSysSock; + int ratelimitSeveritySysSock; + sbool bOmitLocalLogging; + sbool bWritePidSysSock; + int bAnnotateSysSock; + sbool bUseSysTimeStamp; +}; +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ +/* we do not use this, because we do not bind to a ruleset so far + * enable when this is changed: #include "im-helper.h" */ /* must be included AFTER the type definitions! */ static void @@ -262,6 +309,56 @@ static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int RETiRet; } + +/* This function is called when a new listen socket instace shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + * rgerhards, 2011-05-12 + */ +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) +{ + instanceConf_t *inst; + DEFiRet; + + if(pNewVal == NULL || pNewVal[0] == '\0') { + errmsg.LogError(0, RS_RET_SOCKNAME_MISSING , "imuxsock: socket name must be specified, " + "but is not - listener not created\n"); + if(pNewVal != NULL) + free(pNewVal); + ABORT_FINALIZE(RS_RET_SOCKNAME_MISSING); + } + + CHKmalloc(inst = MALLOC(sizeof(instanceConf_t))); + inst->sockName = pNewVal; + inst->ratelimitInterval = cs.ratelimitInterval; + inst->pLogHostName = cs.pLogHostName; + inst->ratelimitBurst = cs.ratelimitBurst; + inst->ratelimitSeverity = cs.ratelimitSeverity; + inst->bUseFlowCtl = cs.bUseFlowCtl; + inst->bIgnoreTimestamp = cs.bIgnoreTimestamp; + inst->bCreatePath = cs.bCreatePath; + inst->bUseSysTimeStamp = cs.bUseSysTimeStamp; + inst->bWritePid = cs.bWritePid; + inst->bAnnotate = cs.bAnnotate; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; + } + + /* some legacy conf processing */ + free(cs.pLogHostName); /* reset hostname for next socket */ + cs.pLogHostName = NULL; + +finalize_it: + RETiRet; +} + + /* add an additional listen socket. Socket names are added * until the array is filled up. It is never reset, only at * module unload. @@ -271,47 +368,46 @@ static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int * added capability to specify hostname for socket -- rgerhards, 2008-08-01 */ static rsRetVal -addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal) +addListner(instanceConf_t *inst) { DEFiRet; if(nfd < MAXFUNIX) { - if(*pNewVal == ':') { + if(*inst->sockName == ':') { listeners[nfd].bParseHost = 1; } else { listeners[nfd].bParseHost = 0; } - if(pLogHostName == NULL) { + if(inst->pLogHostName == NULL) { listeners[nfd].hostName = NULL; } else { CHKiRet(prop.Construct(&(listeners[nfd].hostName))); - CHKiRet(prop.SetString(listeners[nfd].hostName, pLogHostName, ustrlen(pLogHostName))); + CHKiRet(prop.SetString(listeners[nfd].hostName, inst->pLogHostName, ustrlen(inst->pLogHostName))); CHKiRet(prop.ConstructFinalize(listeners[nfd].hostName)); - /* reset hostname for next socket */ - free(pLogHostName); - pLogHostName = NULL; } - if(ratelimitInterval > 0) { + if(inst->ratelimitInterval > 0) { if((listeners[nfd].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) { - /* in this case, we simply turn of rate-limiting */ + /* in this case, we simply turn off rate-limiting */ dbgprintf("imuxsock: turning off rate limiting because we could not " "create hash table\n"); - ratelimitInterval = 0; + inst->ratelimitInterval = 0; } } - listeners[nfd].ratelimitInterval = ratelimitInterval; - listeners[nfd].ratelimitBurst = ratelimitBurst; - listeners[nfd].ratelimitSev = ratelimitSeverity; - listeners[nfd].flowCtl = bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY; - listeners[nfd].flags = bIgnoreTimestamp ? IGNDATE : NOFLAG; - listeners[nfd].bCreatePath = bCreatePath; - listeners[nfd].sockName = pNewVal; - listeners[nfd].bUseCreds = (bWritePid || ratelimitInterval) ? 1 : 0; - listeners[nfd].bWritePid = bWritePid; + listeners[nfd].ratelimitInterval = inst->ratelimitInterval; + listeners[nfd].ratelimitBurst = inst->ratelimitBurst; + listeners[nfd].ratelimitSev = inst->ratelimitSeverity; + listeners[nfd].flowCtl = inst->bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY; + listeners[nfd].flags = inst->bIgnoreTimestamp ? IGNDATE : NOFLAG; + listeners[nfd].bCreatePath = inst->bCreatePath; + listeners[nfd].sockName = ustrdup(inst->sockName); + listeners[nfd].bUseCreds = (inst->bWritePid || inst->ratelimitInterval || inst->bAnnotate) ? 1 : 0; + listeners[nfd].bAnnotate = inst->bAnnotate; + listeners[nfd].bWritePid = inst->bWritePid; + listeners[nfd].bUseSysTimeStamp = inst->bUseSysTimeStamp; nfd++; } else { errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n", - pNewVal); + inst->sockName); } finalize_it: @@ -363,7 +459,8 @@ createLogSocket(lstn_t *pLstn) chmod((char*)pLstn->sockName, 0666) < 0) { errmsg.LogError(errno, NO_ERRCODE, "cannot create '%s'", pLstn->sockName); dbgprintf("cannot create %s (%d).\n", pLstn->sockName, errno); - close(pLstn->fd); + if(pLstn->fd != -1) + close(pLstn->fd); pLstn->fd = -1; ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); } @@ -421,15 +518,22 @@ openLogSocket(lstn_t *pLstn) errmsg.LogError(errno, NO_ERRCODE, "set SCM_CREDENTIALS failed on '%s'", pLstn->sockName); pLstn->bUseCreds = 0; } +// TODO: move to its own #if + if(setsockopt(pLstn->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) != 0) { + errmsg.LogError(errno, NO_ERRCODE, "set SO_TIMESTAMP failed on '%s'", pLstn->sockName); + } } # else /* HAVE_SCM_CREDENTIALS */ pLstn->bUseCreds = 0; + pLstn->bAnnotate = 0; # endif /* HAVE_SCM_CREDENTIALS */ finalize_it: if(iRet != RS_RET_OK) { - close(pLstn->fd); - pLstn->fd = -1; + if(pLstn->fd != -1) { + close(pLstn->fd); + pLstn->fd = -1; + } } RETiRet; @@ -506,12 +610,109 @@ fixPID(uchar *bufTAG, int *lenTag, struct ucred *cred) } +/* Get an "trusted property" from the system. Returns an empty string if the + * property can not be obtained. Inspired by similiar functionality inside + * journald. Currently works with Linux /proc filesystem, only. + */ +static rsRetVal +getTrustedProp(struct ucred *cred, char *propName, uchar *buf, size_t lenBuf, int *lenProp) +{ + int fd; + int i; + int lenRead; + char namebuf[1024]; + DEFiRet; + + if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/%s", (long unsigned) cred->pid, + propName) >= (int) sizeof(namebuf)) { + ABORT_FINALIZE(RS_RET_ERR); + } + + if((fd = open(namebuf, O_RDONLY)) == -1) { + DBGPRINTF("error reading '%s'\n", namebuf); + *lenProp = 0; + FINALIZE; + } + if((lenRead = read(fd, buf, lenBuf - 1)) == -1) { + DBGPRINTF("error reading file data for '%s'\n", namebuf); + *lenProp = 0; + close(fd); + FINALIZE; + } + + /* we strip after the first \n */ + for(i = 0 ; i < lenRead ; ++i) { + if(buf[i] == '\n') + break; + else if(iscntrl(buf[i])) + buf[i] = ' '; + } + buf[i] = '\0'; + *lenProp = i; + + close(fd); + +finalize_it: + RETiRet; +} + + +/* read the exe trusted property path (so far, /proc fs only) + */ +static rsRetVal +getTrustedExe(struct ucred *cred, uchar *buf, size_t lenBuf, int* lenProp) +{ + int lenRead; + char namebuf[1024]; + DEFiRet; + + if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/exe", (long unsigned) cred->pid) + >= (int) sizeof(namebuf)) { + ABORT_FINALIZE(RS_RET_ERR); + } + + if((lenRead = readlink(namebuf, (char*)buf, lenBuf - 1)) == -1) { + DBGPRINTF("error reading link '%s'\n", namebuf); + *lenProp = 0; + FINALIZE; + } + + buf[lenRead] = '\0'; + *lenProp = lenRead; + +finalize_it: + RETiRet; +} + + +/* copy a trusted property in escaped mode. That is, the property can contain + * any character and so it must be properly quoted AND escaped. + * It is assumed the output buffer is large enough. Returns the number of + * characters added. + */ +static inline int +copyescaped(uchar *dstbuf, uchar *inbuf, int inlen) +{ + int iDst, iSrc; + + *dstbuf = '"'; + for(iDst=1, iSrc=0 ; iSrc < inlen ; ++iDst, ++iSrc) { + if(inbuf[iSrc] == '"' || inbuf[iSrc] == '\\') { + dstbuf[iDst++] = '\\'; + } + dstbuf[iDst] = inbuf[iSrc]; + } + dstbuf[iDst++] = '"'; + return iDst; +} + + /* submit received message to the queue engine * We now parse the message according to expected format so that we * can also mangle it if necessary. */ static inline rsRetVal -SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred) +SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct timeval *ts) { msg_t *pMsg; int lenMsg; @@ -525,6 +726,12 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred) struct syslogTime st; time_t tt; rs_ratelimit_state_t *ratelimiter = NULL; + int lenProp; + uchar propBuf[1024]; + uchar msgbuf[8192]; + uchar *pmsgbuf; + int toffs; /* offset for trusted properties */ + struct syslogTime dummyTS; DEFiRet; /* TODO: handle format errors?? */ @@ -550,12 +757,58 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred) findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */ } - datetime.getCurrTime(&st, &tt); + if(ts == NULL) { + datetime.getCurrTime(&st, &tt); + } else { + datetime.timeval2syslogTime(ts, &st); + tt = ts->tv_sec; + } + if(ratelimiter != NULL && !withinRatelimit(ratelimiter, tt, cred->pid)) { STATSCOUNTER_INC(ctrLostRatelimit, mutCtrLostRatelimit); FINALIZE; } + /* created trusted properties */ + if(cred != NULL && pLstn->bAnnotate) { + if((unsigned) (lenRcv + 4096) < sizeof(msgbuf)) { + pmsgbuf = msgbuf; + } else { + CHKmalloc(pmsgbuf = malloc(lenRcv+4096)); + } + memcpy(pmsgbuf, pRcv, lenRcv); + memcpy(pmsgbuf+lenRcv, " @[", 3); + toffs = lenRcv + 3; /* next free location */ + lenProp = snprintf((char*)propBuf, sizeof(propBuf), "_PID=%lu _UID=%lu _GID=%lu", + (long unsigned) cred->pid, (long unsigned) cred->uid, + (long unsigned) cred->gid); + memcpy(pmsgbuf+toffs, propBuf, lenProp); + toffs = toffs + lenProp; + getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp); + if(lenProp) { + memcpy(pmsgbuf+toffs, " _COMM=", 7); + memcpy(pmsgbuf+toffs+7, propBuf, lenProp); + toffs = toffs + 7 + lenProp; + } + getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp); + if(lenProp) { + memcpy(pmsgbuf+toffs, " _EXE=", 6); + memcpy(pmsgbuf+toffs+6, propBuf, lenProp); + toffs = toffs + 6 + lenProp; + } + getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp); + if(lenProp) { + memcpy(pmsgbuf+toffs, " _CMDLINE=", 10); + toffs = toffs + 10 + + copyescaped(pmsgbuf+toffs+10, propBuf, lenProp); + } + /* finalize string */ + pmsgbuf[toffs] = ']'; + pmsgbuf[toffs+1] = '\0'; + pRcv = pmsgbuf; + lenRcv = toffs + 1; + } + /* we now create our own message object and submit it to the queue */ CHKiRet(msgConstructWithTime(&pMsg, &st, tt)); MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv); @@ -570,15 +823,27 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred) parse++; lenMsg--; /* '>' */ - if((pLstn->flags & IGNDATE)) { - /* in this case, we still need to find out if we have a valid - * datestamp or not .. and advance the parse pointer accordingly. - */ - struct syslogTime dummy; - datetime.ParseTIMESTAMP3164(&dummy, &parse, &lenMsg); - } else { - if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) { - DBGPRINTF("we have a problem, invalid timestamp in msg!\n"); + if(ts == NULL) { + if((pLstn->flags & IGNDATE)) { + /* in this case, we still need to find out if we have a valid + * datestamp or not .. and advance the parse pointer accordingly. + */ + datetime.ParseTIMESTAMP3164(&dummyTS, &parse, &lenMsg); + } else { + if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) { + DBGPRINTF("we have a problem, invalid timestamp in msg!\n"); + } + } + } else { /* if we pulled the time from the system, we need to update the message text */ + uchar *tmpParse = parse; /* just to check correctness of TS */ + if(datetime.ParseTIMESTAMP3164(&dummyTS, &tmpParse, &lenMsg) == RS_RET_OK) { + /* We modify the message only if it contained a valid timestamp, + * otherwise we do not touch it at all. */ + datetime.formatTimestamp3164(&st, (char*)parse, 0); + parse[15] = ' '; /* re-write \0 from fromatTimestamp3164 by SP */ + /* update "counters" to reflect processed timestamp */ + parse += 16; + lenMsg -= 16; } } @@ -594,7 +859,11 @@ SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred) fixPID(bufParseTAG, &i, cred); MsgSetTAG(pMsg, bufParseTAG, i); - MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg); + if (pLstn->bAnnotate) { + MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg - 16); + } else { + MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg); + } if(pLstn->bParseHost) { pMsg->msgFlags = pLstn->flags | PARSE_HOSTNAME; @@ -630,6 +899,7 @@ static rsRetVal readSocket(lstn_t *pLstn) struct cmsghdr *cm; # endif struct ucred *cred; + struct timeval *ts; uchar bufRcv[4096+1]; char aux[128]; uchar *pRcv = NULL; /* receive buffer */ @@ -668,21 +938,28 @@ static rsRetVal readSocket(lstn_t *pLstn) dbgprintf("Message from UNIX socket: #%d\n", pLstn->fd); if(iRcvd > 0) { cred = NULL; -# if HAVE_SCM_CREDENTIALS - if(pLstn->bUseCreds) { - dbgprintf("XXX: pre CM loop, length of control message %d\n", (int) msgh.msg_controllen); - for (cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) { - dbgprintf("XXX: in CM loop, %d, %d\n", cm->cmsg_level, cm->cmsg_type); - if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) { + ts = NULL; + if(pLstn->bUseCreds || pLstn->bUseSysTimeStamp) { + for(cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) { +# if HAVE_SCM_CREDENTIALS + if( pLstn->bUseCreds + && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) { cred = (struct ucred*) CMSG_DATA(cm); - dbgprintf("XXX: got credentials pid %d\n", (int) cred->pid); break; } +# endif /* HAVE_SCM_CREDENTIALS */ +# if HAVE_SO_TIMESTAMP + if( pLstn->bUseSysTimeStamp + && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { + ts = (struct timeval *)CMSG_DATA(cm); + dbgprintf("XXX: got timestamp %ld.%ld\n", + (long) ts->tv_sec, (long) ts->tv_usec); + break; + } +# endif /* HAVE_SO_TIMESTAMP */ } - dbgprintf("XXX: post CM loop\n"); } -# endif /* HAVE_SCM_CREDENTIALS */ - CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred)); + CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred, ts)); } else if(iRcvd < 0 && errno != EINTR) { char errStr[1024]; rs_strerror_r(errno, errStr, sizeof(errStr)); @@ -698,6 +975,127 @@ finalize_it: } +/* activate current listeners */ +static inline rsRetVal +activateListeners() +{ + register int i; + int actSocks; + DEFiRet; + + /* first apply some config settings */ +# ifdef OS_SOLARIS + /* under solaris, we must NEVER process the local log socket, because + * it is implemented there differently. If we used it, we would actually + * delete it and render the system partly unusable. So don't do that. + * rgerhards, 2010-03-26 + */ + startIndexUxLocalSockets = 1; +# else + startIndexUxLocalSockets = runModConf->bOmitLocalLogging ? 1 : 0; +# endif + if(runModConf->pLogSockName != NULL) + listeners[0].sockName = runModConf->pLogSockName; + else if(sd_booted()) { + struct stat st; + if(stat(SYSTEMD_PATH_LOG, &st) != -1 && S_ISSOCK(st.st_mode)) { + listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG; + } + } + if(runModConf->ratelimitIntervalSysSock > 0) { + if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) { + /* in this case, we simply turn of rate-limiting */ + errmsg.LogError(0, NO_ERRCODE, "imuxsock: turning off rate limiting because we could not " + "create hash table\n"); + runModConf->ratelimitIntervalSysSock = 0; + } + } + listeners[0].ratelimitInterval = runModConf->ratelimitIntervalSysSock; + listeners[0].ratelimitBurst = runModConf->ratelimitBurstSysSock; + listeners[0].ratelimitSev = runModConf->ratelimitSeveritySysSock; + listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock) ? 1 : 0; + listeners[0].bWritePid = runModConf->bWritePidSysSock; + listeners[0].bAnnotate = runModConf->bAnnotateSysSock; + listeners[0].bUseSysTimeStamp = runModConf->bUseSysTimeStamp; + + sd_fds = sd_listen_fds(0); + if(sd_fds < 0) { + errmsg.LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket"); + ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); + } + + /* initialize and return if will run or not */ + actSocks = 0; + for (i = startIndexUxLocalSockets ; i < nfd ; i++) { + if(openLogSocket(&(listeners[i])) == RS_RET_OK) { + ++actSocks; + dbgprintf("imuxsock: Opened UNIX socket '%s' (fd %d).\n", + listeners[i].sockName, listeners[i].fd); + } + } + + if(actSocks == 0) { + errmsg.LogError(0, NO_ERRCODE, "imuxsock does not run because we could not aquire any socket\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + +finalize_it: + RETiRet; +} + + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* reset legacy config vars */ + resetConfigVariables(NULL, NULL); +ENDbeginCnfLoad + + +BEGINendCnfLoad +CODESTARTendCnfLoad + /* persist module-specific settings from legacy config system */ + loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging; + loadModConf->pLogSockName = cs.pLogSockName; + + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(cs.pLogHostName); + cs.pLogSockName = NULL; + cs.pLogHostName = NULL; +ENDendCnfLoad + + +BEGINcheckCnf +CODESTARTcheckCnf +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(inst); + } + CHKiRet(activateListeners()); +finalize_it: +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf +CODESTARTfreeCnf + free(pModConf->pLogSockName); +ENDfreeCnf + + /* This function is called to gather input. */ BEGINrunInput int maxfds; @@ -765,74 +1163,12 @@ ENDrunInput BEGINwillRun CODESTARTwillRun - register int i; - int actSocks; - - /* first apply some config settings */ -# ifdef OS_SOLARIS - /* under solaris, we must NEVER process the local log socket, because - * it is implemented there differently. If we used it, we would actually - * delete it and render the system partly unusable. So don't do that. - * rgerhards, 2010-03-26 - */ - startIndexUxLocalSockets = 1; -# else - startIndexUxLocalSockets = bOmitLocalLogging ? 1 : 0; -# endif - if(pLogSockName != NULL) - listeners[0].sockName = pLogSockName; - else if(sd_booted()) { - struct stat st; - if(stat(SYSTEMD_JOURNAL, &st) != -1 && S_ISDIR(st.st_mode)) { - listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG; - } - } - if(ratelimitIntervalSysSock > 0) { - if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) { - /* in this case, we simply turn of rate-limiting */ - dbgprintf("imuxsock: turning off rate limiting because we could not " - "create hash table\n"); - ratelimitIntervalSysSock = 0; - } - } - listeners[0].ratelimitInterval = ratelimitIntervalSysSock; - listeners[0].ratelimitBurst = ratelimitBurstSysSock; - listeners[0].ratelimitSev = ratelimitSeveritySysSock; - listeners[0].bUseCreds = (bWritePidSysSock || ratelimitIntervalSysSock) ? 1 : 0; - listeners[0].bWritePid = bWritePidSysSock; - - sd_fds = sd_listen_fds(0); - if (sd_fds < 0) { - errmsg.LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket"); - ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); - } - - /* initialize and return if will run or not */ - actSocks = 0; - for (i = startIndexUxLocalSockets ; i < nfd ; i++) { - if(openLogSocket(&(listeners[i])) == RS_RET_OK) { - ++actSocks; - dbgprintf("imuxsock: Opened UNIX socket '%s' (fd %d).\n", listeners[i].sockName, listeners[i].fd); - } - } - - if(actSocks == 0) { - errmsg.LogError(0, NO_ERRCODE, "imuxsock does not run because we could not aquire any socket\n"); - ABORT_FINALIZE(RS_RET_ERR); - } - - /* we need to create the inputName property (only once during our lifetime) */ - CHKiRet(prop.Construct(&pInputName)); - CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1)); - CHKiRet(prop.ConstructFinalize(pInputName)); - -finalize_it: ENDwillRun BEGINafterRun -CODESTARTafterRun int i; +CODESTARTafterRun /* do cleanup here */ /* Close the UNIX sockets. */ for (i = 0; i < nfd; i++) @@ -842,7 +1178,6 @@ CODESTARTafterRun /* Clean-up files. */ for(i = startIndexUxLocalSockets; i < nfd; i++) if (listeners[i].sockName && listeners[i].fd != -1) { - /* If systemd passed us a socket it is systemd's job to clean it up. * Do not unlink it -- we will get same socket (node) from systemd * e.g. on restart again. @@ -855,20 +1190,17 @@ CODESTARTafterRun DBGPRINTF("imuxsock: unlinking unix socket file[%d] %s\n", i, listeners[i].sockName); unlink((char*) listeners[i].sockName); } - /* free no longer needed string */ - free(pLogSockName); - free(pLogHostName); discardLogSockets(); nfd = 1; - - if(pInputName != NULL) - prop.Destruct(&pInputName); ENDafterRun BEGINmodExit CODESTARTmodExit + if(pInputName != NULL) + prop.Destruct(&pInputName); + statsobj.Destruct(&modStats); objRelease(parser, CORE_COMPONENT); @@ -890,34 +1222,33 @@ ENDisCompatibleWithFeature BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES ENDqueryEtryPt static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) { - bOmitLocalLogging = 0; - if(pLogSockName != NULL) { - free(pLogSockName); - pLogSockName = NULL; - } - if(pLogHostName != NULL) { - free(pLogHostName); - pLogHostName = NULL; - } - - discardLogSockets(); - nfd = 1; - bIgnoreTimestamp = 1; - bUseFlowCtl = 0; - bWritePid = 0; - bWritePidSysSock = 0; - bCreatePath = DFLT_bCreatePath; - ratelimitInterval = DFLT_ratelimitInterval; - ratelimitIntervalSysSock = DFLT_ratelimitInterval; - ratelimitBurst = DFLT_ratelimitBurst; - ratelimitBurstSysSock = DFLT_ratelimitBurst; - ratelimitSeverity = DFLT_ratelimitSeverity; - ratelimitSeveritySysSock = DFLT_ratelimitSeverity; + free(cs.pLogSockName); + cs.pLogSockName = NULL; + free(cs.pLogHostName); + cs.bOmitLocalLogging = 0; + cs.pLogHostName = NULL; + cs.bIgnoreTimestamp = 1; + cs.bUseFlowCtl = 0; + cs.bUseSysTimeStamp = 1; + cs.bUseSysTimeStampSysSock = 1; + cs.bWritePid = 0; + cs.bWritePidSysSock = 0; + cs.bAnnotate = 0; + cs.bAnnotateSysSock = 0; + cs.bCreatePath = DFLT_bCreatePath; + cs.ratelimitInterval = DFLT_ratelimitInterval; + cs.ratelimitIntervalSysSock = DFLT_ratelimitInterval; + cs.ratelimitBurst = DFLT_ratelimitBurst; + cs.ratelimitBurstSysSock = DFLT_ratelimitBurst; + cs.ratelimitSeverity = DFLT_ratelimitSeverity; + cs.ratelimitSeveritySysSock = DFLT_ratelimitSeverity; return RS_RET_OK; } @@ -930,6 +1261,7 @@ CODESTARTmodInit CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(net, CORE_COMPONENT)); CHKiRet(objUse(prop, CORE_COMPONENT)); CHKiRet(objUse(statsobj, CORE_COMPONENT)); CHKiRet(objUse(datetime, CORE_COMPONENT)); @@ -937,6 +1269,22 @@ CODEmodInit_QueryRegCFSLineHdlr dbgprintf("imuxsock version %s initializing\n", PACKAGE_VERSION); + /* init legacy config vars */ + cs.pLogSockName = NULL; + cs.pLogHostName = NULL; /* host name to use with this socket */ + + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.Construct(&pInputName)); + CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1)); + CHKiRet(prop.ConstructFinalize(pInputName)); + + /* right now, glbl does not permit per-instance IP address notation. As long as this + * is the case, it is OK to query the HostIP once here at this location. HOWEVER, the + * whole concept is not 100% clean and needs to be addressed on a higher layer. + * TODO / rgerhards, 2012-04-11 + */ + pLocalHostIP = glbl.GetLocalHostIP(); + /* init system log socket settings */ listeners[0].flags = IGNDATE; listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG); @@ -945,7 +1293,9 @@ CODEmodInit_QueryRegCFSLineHdlr listeners[0].fd = -1; listeners[0].bParseHost = 0; listeners[0].bUseCreds = 0; + listeners[0].bAnnotate = 0; listeners[0].bCreatePath = 0; + listeners[0].bUseSysTimeStamp = 1; /* initialize socket names */ for(i = 1 ; i < MAXFUNIX ; ++i) { @@ -953,33 +1303,38 @@ CODEmodInit_QueryRegCFSLineHdlr listeners[i].fd = -1; } + /* now init listen socket zero, the local log socket */ CHKiRet(prop.Construct(&pLocalHostIP)); CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1)); CHKiRet(prop.ConstructFinalize(pLocalHostIP)); /* register config file handlers */ CHKiRet(omsdRegCFSLineHdlr((uchar *)"omitlocallogging", 0, eCmdHdlrBinary, - NULL, &bOmitLocalLogging, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bOmitLocalLogging, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketignoremsgtimestamp", 0, eCmdHdlrBinary, - NULL, &bIgnoreTimestamp, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bIgnoreTimestamp, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord, - NULL, &pLogSockName, STD_LOADABLE_MODULE_ID)); + NULL, &cs.pLogSockName, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensockethostname", 0, eCmdHdlrGetWord, - NULL, &pLogHostName, STD_LOADABLE_MODULE_ID)); + NULL, &cs.pLogHostName, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary, - NULL, &bUseFlowCtl, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bUseFlowCtl, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketannotate", 0, eCmdHdlrBinary, + NULL, &cs.bAnnotate, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary, - NULL, &bCreatePath, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bCreatePath, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusesystimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bUseSysTimeStamp, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"addunixlistensocket", 0, eCmdHdlrGetWord, - addLstnSocketName, NULL, STD_LOADABLE_MODULE_ID)); + addInstance, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusepidfromsystem", 0, eCmdHdlrBinary, - NULL, &bWritePid, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bWritePid, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitinterval", 0, eCmdHdlrInt, - NULL, &ratelimitInterval, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitInterval, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitburst", 0, eCmdHdlrInt, - NULL, &ratelimitBurst, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitBurst, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitseverity", 0, eCmdHdlrInt, - NULL, &ratelimitSeverity, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitSeverity, 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 @@ -992,14 +1347,18 @@ CODEmodInit_QueryRegCFSLineHdlr setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary, setSystemLogFlowControl, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary, + NULL, &cs.bAnnotateSysSock, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary, - NULL, &bWritePidSysSock, STD_LOADABLE_MODULE_ID)); + NULL, &cs.bWritePidSysSock, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt, - NULL, &ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt, - NULL, &ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt, - NULL, &ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID)); + NULL, &cs.ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID)); /* support statistics gathering */ CHKiRet(statsobj.Construct(&modStats)); diff --git a/plugins/mmaudit/Makefile.am b/plugins/mmaudit/Makefile.am new file mode 100644 index 00000000..c64d0822 --- /dev/null +++ b/plugins/mmaudit/Makefile.am @@ -0,0 +1,8 @@ +pkglib_LTLIBRARIES = mmaudit.la + +mmaudit_la_SOURCES = mmaudit.c +mmaudit_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBLOGNORM_CFLAGS) $(LIBEE_CFLAGS) +mmaudit_la_LDFLAGS = -module -avoid-version $(LIBLOGNORM_LIBS) $(LIBEE_LIBS) +mmaudit_la_LIBADD = + +EXTRA_DIST = diff --git a/plugins/mmaudit/mmaudit.c b/plugins/mmaudit/mmaudit.c new file mode 100644 index 00000000..fcefd013 --- /dev/null +++ b/plugins/mmaudit/mmaudit.c @@ -0,0 +1,390 @@ +/* mmaudit.c + * This is a message modification module supporting Linux audit format + * in various settings. The module tries to identify the provided + * message as being a Linux audit record and, if so, converts it into + * cee-enhanced syslog format. + * + * NOTE WELL: + * Right now, we do not do any trust checks. So it is possible that a + * malicous user emits something that looks like an audit record and + * tries to fool the system with that. Solving this trust issue is NOT + * an easy thing to do. This will be worked on, as the lumberjack effort + * continues. Please consider the module in its current state as a proof + * of concept. + * + * File begun on 2012-02-23 by RGerhards + * + * Copyright 2012 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <libestr.h> +#include <libee/libee.h> +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "module-template.h" +#include "errmsg.h" +#include "cfsysline.h" +#include "dirty.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("mmaudit") + +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + +/* static data */ +DEFobjCurrIf(errmsg); + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + +typedef struct _instanceData { + ee_ctx ctxee; /**< context to be used for libee */ +} instanceData; + +typedef struct configSettings_s { + int dummy; /* remove when the first real parameter is needed */ +} configSettings_t; +static configSettings_t cs; + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars + resetConfigVariables(NULL, NULL); +ENDinitConfVars + + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + ee_exitCtx(pData->ctxee); +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + dbgprintf("mmaudit\n"); +ENDdbgPrintInstInfo + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + + +static inline void +skipWhitespace(uchar **buf) +{ + while(**buf && isspace(**buf)) + ++(*buf); +} + + +static inline rsRetVal +parseName(uchar **buf, char *name, unsigned lenName) +{ + unsigned i; + skipWhitespace(buf); + --lenName; /* reserve space for '\0' */ + i = 0; + while(**buf && **buf != '=' && lenName) { +//dbgprintf("parseNAme, buf: %s\n", *buf); + name[i++] = **buf; + ++(*buf), --lenName; + } + name[i] = '\0'; + return RS_RET_OK; +} + + +static inline rsRetVal +parseValue(uchar **buf, char *val, unsigned lenval) +{ + char termc; + unsigned i; + DEFiRet; + + --lenval; /* reserve space for '\0' */ + i = 0; + if(**buf == '\0') { + FINALIZE; + } else if(**buf == '\'') { + termc = '\''; + ++(*buf); + } else if(**buf == '"') { + termc = '"'; + ++(*buf); + } else { + termc = ' '; + } + + while(**buf && **buf != termc && lenval) { +//dbgprintf("parseValue, termc '%c', buf: %s\n", termc, *buf); + val[i++] = **buf; + ++(*buf), --lenval; + } + val[i] = '\0'; + +finalize_it: + RETiRet; +} + + +/* parse the audit record and create libee structure + */ +static rsRetVal +audit_parse(instanceData *pData, uchar *buf, struct ee_event **event) +{ + es_str_t *estr; + char name[1024]; + char val[1024]; + DEFiRet; + + *event = ee_newEvent(pData->ctxee); + if(event == NULL) { + ABORT_FINALIZE(RS_RET_ERR); + } + + while(*buf) { +//dbgprintf("audit_parse, buf: '%s'\n", buf); + CHKiRet(parseName(&buf, name, sizeof(name))); + if(*buf != '=') { + ABORT_FINALIZE(RS_RET_ERR); + } + ++buf; + CHKiRet(parseValue(&buf, val, sizeof(val))); + + estr = es_newStrFromCStr(val, strlen(val)); + ee_addStrFieldToEvent(*event, name, estr); + es_deleteStr(estr); +dbgprintf("mmaudit: parsed %s=%s\n", name, val); + } + + +finalize_it: + RETiRet; +} + + +BEGINdoAction + msg_t *pMsg; + uchar *buf; + int typeID; + struct ee_event *event; + int i; + es_str_t *estr; + char auditID[1024]; + int bSuccess = 0; +CODESTARTdoAction + pMsg = (msg_t*) ppString[0]; + /* note that we can performance-optimize the interface, but this also + * requires changes to the libraries. For now, we accept message + * duplication. -- rgerhards, 2010-12-01 + */ + buf = getMSG(pMsg); + +dbgprintf("mmaudit: msg is '%s'\n", buf); + while(*buf && isspace(*buf)) { + ++buf; + } + + if(*buf == '\0' || strncmp((char*)buf, "type=", 5)) { + DBGPRINTF("mmaudit: type= undetected: '%s'\n", buf); + FINALIZE; + } + buf += 5; + + typeID = 0; + while(*buf && isdigit(*buf)) { + typeID = typeID * 10 + *buf - '0'; + ++buf; + } + + if(*buf == '\0' || strncmp((char*)buf, " audit(", sizeof(" audit(")-1)) { + DBGPRINTF("mmaudit: audit( header not found: %s'\n", buf); + FINALIZE; + } + buf += sizeof(" audit("); + + for(i = 0 ; i < (int) (sizeof(auditID)-2) && *buf && *buf != ')' ; ++i) { + auditID[i] = *buf++; + } + auditID[i] = '\0'; + if(*buf != ')' || *(buf+1) != ':') { + DBGPRINTF("mmaudit: trailer '):' not found, no audit record: %s'\n", buf); + FINALIZE; + } + buf += 2; + +dbgprintf("mmaudit: cookie found, type %d, auditID '%s', rest of message: '%s'\n", typeID, auditID, buf); + audit_parse(pData, buf, &event); + if(event == NULL) { + DBGPRINTF("mmaudit: audit parse error, assuming no " + "audit message: '%s'\n", buf); + FINALIZE; + } + + /* we now need to shuffle the "outer" properties into that stream */ + estr = es_newStrFromCStr(auditID, strlen(auditID)); + ee_addStrFieldToEvent(event, "audithdr.auditid", estr); + es_deleteStr(estr); + + /* we abuse auditID a bit to save space... (TODO: change!) */ + snprintf(auditID, sizeof(auditID), "%d", typeID); + estr = es_newStrFromCStr(auditID, strlen(auditID)); + ee_addStrFieldToEvent(event, "audithdr.type", estr); + es_deleteStr(estr); + + /* TODO: in the long term, we need to think about merging & different + name spaces (probably best to add the newly-obtained event as a child to + the existing event...) + */ + if(pMsg->event != NULL) { + ee_deleteEvent(pMsg->event); + } + pMsg->event = event; + bSuccess = 1; + +#if 1 + /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01 + { + char *cstr; + es_str_t *str; + ee_fmtEventToJSON(pMsg->event, &str); + cstr = es_str2cstr(str, NULL); + dbgprintf("mmaudit generated: %s\n", cstr); + free(cstr); + es_deleteStr(str); + } + /***END DEBUG***/ +#endif +finalize_it: + MsgSetParseSuccess(pMsg, bSuccess); +ENDdoAction + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* first check if this config line is actually for us */ + if(strncmp((char*) p, ":mmaudit:", sizeof(":mmaudit:") - 1)) { + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + } + + /* ok, if we reach this point, we have something for us */ + p += sizeof(":mmaudit:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ + CHKiRet(createInstance(&pData)); + + /* check if a non-standard template is to be applied */ + if(*(p-1) == ';') + --p; + /* we call the function below because we need to call it via our interface definition. However, + * the format specified (if any) is always ignored. + */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat")); + + /* finally build the instance */ + if((pData->ctxee = ee_initCtx()) == NULL) { + errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize libee ctx, cannot " + "activate action"); + ABORT_FINALIZE(RS_RET_ERR_LIBEE_INIT); + } +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit + objRelease(errmsg, CORE_COMPONENT); +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; + RETiRet; +} + + +BEGINmodInit() + rsRetVal localRet; + rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts); + unsigned long opts; + int bMsgPassingSupported; +CODESTARTmodInit +INITLegCnfVars + *ipIFVersProvided = CURR_MOD_IF_VERSION; + /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + /* check if the rsyslog core supports parameter passing code */ + bMsgPassingSupported = 0; + localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", + &pomsrGetSupportedTplOpts); + if(localRet == RS_RET_OK) { + /* found entry point, so let's see if core supports msg passing */ + CHKiRet((*pomsrGetSupportedTplOpts)(&opts)); + if(opts & OMSR_TPL_AS_MSG) + bMsgPassingSupported = 1; + } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) { + ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */ + } + + if(!bMsgPassingSupported) { + DBGPRINTF("mmaudit: msg-passing is not supported by rsyslog core, " + "can not continue.\n"); + ABORT_FINALIZE(RS_RET_NO_MSG_PASSING); + } + + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit + +/* vi:set ai: + */ diff --git a/plugins/mmjsonparse/Makefile.am b/plugins/mmjsonparse/Makefile.am new file mode 100644 index 00000000..5175fe81 --- /dev/null +++ b/plugins/mmjsonparse/Makefile.am @@ -0,0 +1,8 @@ +pkglib_LTLIBRARIES = mmjsonparse.la + +mmjsonparse_la_SOURCES = mmjsonparse.c +mmjsonparse_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBLOGNORM_CFLAGS) $(LIBEE_CFLAGS) +mmjsonparse_la_LDFLAGS = -module -avoid-version $(LIBLOGNORM_LIBS) $(LIBEE_LIBS) +mmjsonparse_la_LIBADD = + +EXTRA_DIST = diff --git a/plugins/mmjsonparse/mmjsonparse.c b/plugins/mmjsonparse/mmjsonparse.c new file mode 100644 index 00000000..03147b59 --- /dev/null +++ b/plugins/mmjsonparse/mmjsonparse.c @@ -0,0 +1,250 @@ +/* mmjsonparse.c + * This is a message modification module. If give, it extracts JSON data + * and populates the EE event structure with it. + * + * NOTE: read comments in module-template.h for details on the calling interface! + * + * File begun on 2012-02-20 by RGerhards + * + * Copyright 2012 Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <ctype.h> +#include <libestr.h> +#include <libee/libee.h> +#include "conf.h" +#include "syslogd-types.h" +#include "template.h" +#include "module-template.h" +#include "errmsg.h" +#include "cfsysline.h" +#include "dirty.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("mmjsonparse") + +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + +/* static data */ +DEFobjCurrIf(errmsg); + +/* internal structures + */ +DEF_OMOD_STATIC_DATA + +typedef struct _instanceData { + ee_ctx ctxee; /**< context to be used for libee */ +} instanceData; + +typedef struct configSettings_s { + int dummy; /* remove when the first real parameter is needed */ +} configSettings_t; +static configSettings_t cs; + +BEGINinitConfVars /* (re)set config variables to default values */ +CODESTARTinitConfVars + resetConfigVariables(NULL, NULL); +ENDinitConfVars + + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature +ENDisCompatibleWithFeature + + +BEGINfreeInstance +CODESTARTfreeInstance + ee_exitCtx(pData->ctxee); +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + dbgprintf("mmjsonparse\n"); +ENDdbgPrintInstInfo + + +BEGINtryResume +CODESTARTtryResume +ENDtryResume + +#define COOKIE "@cee: " +#define LEN_COOKIE (sizeof(COOKIE)-1) +BEGINdoAction + msg_t *pMsg; + uchar *buf; + struct ee_event *event; + int bSuccess = 0; +CODESTARTdoAction + pMsg = (msg_t*) ppString[0]; + /* note that we can performance-optimize the interface, but this also + * requires changes to the libraries. For now, we accept message + * duplication. -- rgerhards, 2010-12-01 + */ + buf = getMSG(pMsg); + +dbgprintf("mmjsonparse: msg is '%s'\n", buf); + while(*buf && isspace(*buf)) { + ++buf; + } + + if(*buf == '\0' || strncmp((char*)buf, COOKIE, LEN_COOKIE)) { + DBGPRINTF("mmjsonparse: no JSON cookie: '%s'\n", buf); + FINALIZE; + } + buf += LEN_COOKIE; +dbgprintf("mmjsonparse: cookie found, rest of message: '%s'\n", buf); + event = ee_newEventFromJSON(pData->ctxee, (char*)buf); + if(event == NULL) { + DBGPRINTF("mmjsonparse: JSON parse error, assuming no " + "JSON-enhanced message: '%s'\n", buf); + FINALIZE; + } + /* TODO: in the long term, we need to think about merging & different + name spaces (probably best to add the newly-obtained event as a child to + the existing event...) + */ + if(pMsg->event != NULL) { + ee_deleteEvent(pMsg->event); + } + pMsg->event = event; + bSuccess = 1; + +#if 1 + /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01 + { + char *cstr; + es_str_t *str; + ee_fmtEventToJSON(pMsg->event, &str); + cstr = es_str2cstr(str, NULL); + dbgprintf("mmjsonparse generated: %s\n", cstr); + free(cstr); + es_deleteStr(str); + } + /***END DEBUG***/ +#endif +finalize_it: + MsgSetParseSuccess(pMsg, bSuccess); +ENDdoAction + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + /* first check if this config line is actually for us */ + if(strncmp((char*) p, ":mmjsonparse:", sizeof(":mmjsonparse:") - 1)) { + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + } + + /* ok, if we reach this point, we have something for us */ + p += sizeof(":mmjsonparse:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ + CHKiRet(createInstance(&pData)); + + /* check if a non-standard template is to be applied */ + if(*(p-1) == ';') + --p; + /* we call the function below because we need to call it via our interface definition. However, + * the format specified (if any) is always ignored. + */ + CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_MSG, (uchar*) "RSYSLOG_FileFormat")); + + /* finally build the instance */ + if((pData->ctxee = ee_initCtx()) == NULL) { + errmsg.LogError(0, RS_RET_NO_RULESET, "error: could not initialize libee ctx, cannot " + "activate action"); + ABORT_FINALIZE(RS_RET_ERR_LIBEE_INIT); + } +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit + objRelease(errmsg, CORE_COMPONENT); +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; + RETiRet; +} + + +BEGINmodInit() + rsRetVal localRet; + rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts); + unsigned long opts; + int bMsgPassingSupported; +CODESTARTmodInit +INITLegCnfVars + *ipIFVersProvided = CURR_MOD_IF_VERSION; + /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + /* check if the rsyslog core supports parameter passing code */ + bMsgPassingSupported = 0; + localRet = pHostQueryEtryPt((uchar*)"OMSRgetSupportedTplOpts", + &pomsrGetSupportedTplOpts); + if(localRet == RS_RET_OK) { + /* found entry point, so let's see if core supports msg passing */ + CHKiRet((*pomsrGetSupportedTplOpts)(&opts)); + if(opts & OMSR_TPL_AS_MSG) + bMsgPassingSupported = 1; + } else if(localRet != RS_RET_ENTRY_POINT_NOT_FOUND) { + ABORT_FINALIZE(localRet); /* Something else went wrong, not acceptable */ + } + + if(!bMsgPassingSupported) { + DBGPRINTF("mmjsonparse: msg-passing is not supported by rsyslog core, " + "can not continue.\n"); + ABORT_FINALIZE(RS_RET_NO_MSG_PASSING); + } + + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); +ENDmodInit + +/* vi:set ai: + */ diff --git a/plugins/mmnormalize/mmnormalize.c b/plugins/mmnormalize/mmnormalize.c index adba0efe..c5b290f4 100644 --- a/plugins/mmnormalize/mmnormalize.c +++ b/plugins/mmnormalize/mmnormalize.c @@ -48,6 +48,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("mmnormalize") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -68,8 +69,7 @@ typedef struct configSettings_s { uchar *rulebase; /**< name of normalization rulebase to use */ sbool bUseRawMsg; /**< use %rawmsg% instead of %msg% */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -126,6 +126,9 @@ CODESTARTdoAction r = ln_normalize(pData->ctxln, str, &pMsg->event); if(r != 0) { DBGPRINTF("error %d during ln_normalize\n", r); + MsgSetParseSuccess(pMsg, 0); + } else { + MsgSetParseSuccess(pMsg, 1); } es_deleteStr(str); /***DEBUG***/ // TODO: remove after initial testing - 2010-12-01 @@ -237,7 +240,7 @@ BEGINmodInit() unsigned long opts; int bMsgPassingSupported; CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr diff --git a/plugins/mmsnmptrapd/mmsnmptrapd.c b/plugins/mmsnmptrapd/mmsnmptrapd.c index bbe2619f..b1ac2f64 100644 --- a/plugins/mmsnmptrapd/mmsnmptrapd.c +++ b/plugins/mmsnmptrapd/mmsnmptrapd.c @@ -49,6 +49,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("mmsnmptrapd") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -76,8 +77,7 @@ typedef struct configSettings_s { uchar *pszTagName; /**< name of tag start value that indicates snmptrapd initiated message */ uchar *pszSeverityMapping; /**< severitystring to numerical code mapping for snmptrapd string */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -385,7 +385,7 @@ BEGINmodInit() unsigned long opts; int bMsgPassingSupported; CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr @@ -414,7 +414,7 @@ CODEmodInit_QueryRegCFSLineHdlr cs.pszTagName = NULL; cs.pszSeverityMapping = NULL; - CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrInt, + CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrGetWord, NULL, &cs.pszTagName, STD_LOADABLE_MODULE_ID)); CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdseveritymapping", 0, eCmdHdlrGetWord, NULL, &cs.pszSeverityMapping, STD_LOADABLE_MODULE_ID)); diff --git a/plugins/omdbalerting/Makefile.am b/plugins/omdbalerting/Makefile.am deleted file mode 100644 index becf29b0..00000000 --- a/plugins/omdbalerting/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -pkglib_LTLIBRARIES = omdbalerting.la - -omdbalerting_la_SOURCES = omdbalerting.c -omdbalerting_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -omdbalerting_la_LDFLAGS = -module -avoid-version -omdbalerting_la_LIBADD = - -EXTRA_DIST = diff --git a/plugins/omdbalerting/omdbalerting.c b/plugins/omdbalerting/omdbalerting.c deleted file mode 100644 index 35de5818..00000000 --- a/plugins/omdbalerting/omdbalerting.c +++ /dev/null @@ -1,145 +0,0 @@ -/* omdbalerting.c - * generate alerts based on database contents - so far a skeleton - * left for implementation by somebody else (skeleton created on request). - * - * NOTE: read comments in module-template.h for more specifics! - * - * File begun on 2009-11-17 by RGerhards - * - * Copyright 2009 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 <unistd.h> -#include "conf.h" -#include "syslogd-types.h" -#include "srUtils.h" -#include "template.h" -#include "module-template.h" -#include "errmsg.h" -#include "cfsysline.h" - -MODULE_TYPE_OUTPUT -MODULE_TYPE_NOKEEP - -/* internal structures - */ -DEF_OMOD_STATIC_DATA - -/* config variables */ - - -typedef struct _instanceData { -} instanceData; - -BEGINcreateInstance -CODESTARTcreateInstance -ENDcreateInstance - - -BEGINisCompatibleWithFeature -CODESTARTisCompatibleWithFeature - if(eFeat == sFEATURERepeatedMsgReduction) - iRet = RS_RET_OK; -ENDisCompatibleWithFeature - - -BEGINfreeInstance -CODESTARTfreeInstance -ENDfreeInstance - - -BEGINdbgPrintInstInfo -CODESTARTdbgPrintInstInfo -ENDdbgPrintInstInfo - - -BEGINtryResume -CODESTARTtryResume -ENDtryResume - -BEGINdoAction -CODESTARTdoAction -ENDdoAction - - -BEGINparseSelectorAct -CODESTARTparseSelectorAct -CODE_STD_STRING_REQUESTparseSelectorAct(1) - /* first check if this config line is actually for us */ - if(strncmp((char*) p, ":omdbalerting:", sizeof(":dbalerting:") - 1)) { - ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); - } - - /* ok, if we reach this point, we have something for us */ - p += sizeof(":omdbalerting:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ - CHKiRet(createInstance(&pData)); - - /* check if a non-standard template is to be applied */ - if(*(p-1) == ';') - --p; - /* we request the standard interface via template, others may be more useful - * here. - */ - CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, (uchar*) "RSYSLOG_FileFormat")); -CODE_STD_FINALIZERparseSelectorAct -ENDparseSelectorAct - - -BEGINmodExit -CODESTARTmodExit -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; - RETiRet; -} - - -BEGINmodInit() -CODESTARTmodInit - *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ -CODEmodInit_QueryRegCFSLineHdlr - // SAMPLE! CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomdbalertingensurelfending", 0, eCmdHdlrBinary, NULL, - // &bEnsureLFEnding, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, - resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); -ENDmodInit - -/* vi:set ai: - */ diff --git a/plugins/omelasticsearch/Makefile.am b/plugins/omelasticsearch/Makefile.am new file mode 100644 index 00000000..a574c72f --- /dev/null +++ b/plugins/omelasticsearch/Makefile.am @@ -0,0 +1,8 @@ +pkglib_LTLIBRARIES = omelasticsearch.la + +omelasticsearch_la_SOURCES = omelasticsearch.c +omelasticsearch_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) +omelasticsearch_la_LDFLAGS = -module -avoid-version +omelasticsearch_la_LIBADD = $(CURL_LIBS) + +EXTRA_DIST = diff --git a/plugins/omelasticsearch/omelasticsearch.c b/plugins/omelasticsearch/omelasticsearch.c new file mode 100644 index 00000000..f77caeca --- /dev/null +++ b/plugins/omelasticsearch/omelasticsearch.c @@ -0,0 +1,741 @@ +/* omelasticsearch.c + * This is the http://www.elasticsearch.org/ output module. + * + * NOTE: read comments in module-template.h for more specifics! + * + * Copyright 2011 Nathan Scott. + * Copyright 2009-2012 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" +#include "rsyslog.h" +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <curl/curl.h> +#include <curl/easy.h> +#include <assert.h> +#include <signal.h> +#include <errno.h> +#include <time.h> +#include "conf.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "template.h" +#include "module-template.h" +#include "errmsg.h" +#include "statsobj.h" +#include "cfsysline.h" +#include "unicode-helper.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omelasticsearch") + +/* internal structures */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(errmsg) +DEFobjCurrIf(statsobj) + +statsobj_t *indexStats; +STATSCOUNTER_DEF(indexConFail, mutIndexConFail) +STATSCOUNTER_DEF(indexSubmit, mutIndexSubmit) +STATSCOUNTER_DEF(indexFailed, mutIndexFailed) +STATSCOUNTER_DEF(indexSuccess, mutIndexSuccess) + +/* REST API for elasticsearch hits this URL: + * http://<hostName>:<restPort>/<searchIndex>/<searchType> + */ +typedef struct curl_slist HEADER; +typedef struct _instanceData { + uchar *server; + int port; + uchar *uid; + uchar *pwd; + uchar *searchIndex; + uchar *searchType; + uchar *parent; + uchar *tplName; + uchar *timeout; + sbool dynSrchIdx; + sbool dynSrchType; + sbool dynParent; + sbool bulkmode; + sbool asyncRepl; + struct { + es_str_t *data; + uchar *currTpl1; + uchar *currTpl2; + } batch; + CURL *curlHandle; /* libcurl session handle */ + HEADER *postHeader; /* json POST request info */ +} instanceData; + + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrGetWord, 0 }, + { "serverport", eCmdHdlrInt, 0 }, + { "uid", eCmdHdlrGetWord, 0 }, + { "pwd", eCmdHdlrGetWord, 0 }, + { "searchindex", eCmdHdlrGetWord, 0 }, + { "searchtype", eCmdHdlrGetWord, 0 }, + { "parent", eCmdHdlrGetWord, 0 }, + { "dynsearchindex", eCmdHdlrBinary, 0 }, + { "dynsearchtype", eCmdHdlrBinary, 0 }, + { "dynparent", eCmdHdlrBinary, 0 }, + { "bulkmode", eCmdHdlrBinary, 0 }, + { "asyncrepl", eCmdHdlrBinary, 0 }, + { "timeout", eCmdHdlrGetWord, 0 }, + { "template", eCmdHdlrGetWord, 1 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + +BEGINfreeInstance +CODESTARTfreeInstance + if (pData->postHeader) { + curl_slist_free_all(pData->postHeader); + pData->postHeader = NULL; + } + if (pData->curlHandle) { + curl_easy_cleanup(pData->curlHandle); + pData->curlHandle = NULL; + } + free(pData->server); + free(pData->uid); + free(pData->pwd); + free(pData->searchIndex); + free(pData->searchType); + free(pData->parent); + free(pData->tplName); +ENDfreeInstance + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + dbgprintf("omelasticsearch\n"); + dbgprintf("\ttemplate='%s'\n", pData->tplName); + dbgprintf("\tserver='%s'\n", pData->server); + dbgprintf("\tserverport=%d\n", pData->port); + dbgprintf("\tuid='%s'\n", pData->uid == NULL ? (uchar*)"(not configured)" : pData->uid); + dbgprintf("\tpwd=(%sconfigured)\n", pData->pwd == NULL ? "not " : ""); + dbgprintf("\tsearch index='%s'\n", pData->searchIndex); + dbgprintf("\tsearch index='%s'\n", pData->searchType); + dbgprintf("\tparent='%s'\n", pData->parent); + dbgprintf("\ttimeout='%s'\n", pData->timeout); + dbgprintf("\tdynamic search index=%d\n", pData->dynSrchIdx); + dbgprintf("\tdynamic search type=%d\n", pData->dynSrchType); + dbgprintf("\tdynamic parent=%d\n", pData->dynParent); + dbgprintf("\tasync replication=%d\n", pData->asyncRepl); + dbgprintf("\tbulkmode=%d\n", pData->bulkmode); +ENDdbgPrintInstInfo + + +/* Build basic URL part, which includes hostname and port as follows: + * http://hostname:port/ + * Newly creates an estr for this purpose. + */ +static rsRetVal +setBaseURL(instanceData *pData, es_str_t **url) +{ + char portBuf[64]; + int r; + DEFiRet; + + *url = es_newStr(128); + snprintf(portBuf, sizeof(portBuf), "%d", pData->port); + r = es_addBuf(url, "http://", sizeof("http://")-1); + if(r == 0) r = es_addBuf(url, (char*)pData->server, strlen((char*)pData->server)); + if(r == 0) r = es_addChar(url, ':'); + if(r == 0) r = es_addBuf(url, portBuf, strlen(portBuf)); + if(r == 0) r = es_addChar(url, '/'); + RETiRet; +} + + +static inline rsRetVal +checkConn(instanceData *pData) +{ + es_str_t *url; + CURL *curl = NULL; + CURLcode res; + char *cstr; + DEFiRet; + + setBaseURL(pData, &url); + curl = curl_easy_init(); + if(curl == NULL) { + DBGPRINTF("omelasticsearch: checkConn() curl_easy_init() failed\n"); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + cstr = es_str2cstr(url, NULL); + curl_easy_setopt(curl, CURLOPT_URL, cstr); + free(cstr); + + res = curl_easy_perform(curl); + if(res != CURLE_OK) { + DBGPRINTF("omelasticsearch: checkConn() curl_easy_perform() " + "failed: %s\n", curl_easy_strerror(res)); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + DBGPRINTF("omelasticsearch: checkConn() completed with success\n"); + +finalize_it: + if(curl != NULL) + curl_easy_cleanup(curl); + RETiRet; +} + + +BEGINtryResume +CODESTARTtryResume + DBGPRINTF("omelasticsearch: tryResume called\n"); + iRet = checkConn(pData); +ENDtryResume + + +/* get the current index and type for this message */ +static inline void +getIndexTypeAndParent(instanceData *pData, uchar **tpls, + uchar **srchIndex, uchar **srchType, uchar **parent) +{ + if(pData->dynSrchIdx) { + *srchIndex = tpls[1]; + if(pData->dynSrchType) { + *srchType = tpls[2]; + if(pData->dynParent) { + *parent = tpls[3]; + } else { + *parent = pData->parent; + } + } else { + *srchType = pData->searchType; + if(pData->dynParent) { + *parent = tpls[2]; + } else { + *parent = pData->parent; + } + } + } else { + *srchIndex = pData->searchIndex; + if(pData->dynSrchType) { + *srchType = tpls[1]; + if(pData->dynParent) { + *parent = tpls[2]; + } else { + *parent = pData->parent; + } + } else { + *srchType = pData->searchType; + if(pData->dynParent) { + *parent = tpls[1]; + } else { + *parent = pData->parent; + } + } + } +} + + +static rsRetVal +setCurlURL(instanceData *pData, uchar **tpls) +{ + char authBuf[1024]; + char *restURL; + uchar *searchIndex; + uchar *searchType; + uchar *parent; + es_str_t *url; + int rLocal; + int r; + DEFiRet; + + setBaseURL(pData, &url); + + if(pData->bulkmode) { + r = es_addBuf(&url, "_bulk", sizeof("_bulk")-1); + parent = NULL; + } else { + getIndexTypeAndParent(pData, tpls, &searchIndex, &searchType, &parent); + r = es_addBuf(&url, (char*)searchIndex, ustrlen(searchIndex)); + if(r == 0) r = es_addChar(&url, '/'); + if(r == 0) r = es_addBuf(&url, (char*)searchType, ustrlen(searchType)); + } + if(r == 0) r = es_addChar(&url, '?'); + if(pData->asyncRepl) { + if(r == 0) r = es_addBuf(&url, "replication=async&", + sizeof("replication=async&")-1); + } + if(pData->timeout != NULL) { + if(r == 0) r = es_addBuf(&url, "timeout=", sizeof("timeout=")-1); + if(r == 0) r = es_addBuf(&url, (char*)pData->timeout, ustrlen(pData->timeout)); + if(r == 0) r = es_addChar(&url, '&'); + } + if(parent != NULL) { + if(r == 0) r = es_addBuf(&url, "parent=", sizeof("parent=")-1); + if(r == 0) r = es_addBuf(&url, (char*)parent, ustrlen(parent)); + } + restURL = es_str2cstr(url, NULL); + curl_easy_setopt(pData->curlHandle, CURLOPT_URL, restURL); + es_deleteStr(url); + DBGPRINTF("omelasticsearch: using REST URL: '%s'\n", restURL); + free(restURL); + + if(pData->uid != NULL) { + rLocal = snprintf(authBuf, sizeof(authBuf), "%s:%s", pData->uid, + (pData->pwd == NULL) ? "" : (char*)pData->pwd); + if(rLocal != (int) es_strlen(url)) { + errmsg.LogError(0, RS_RET_ERR, "omelasticsearch: snprintf failed " + "when trying to build auth string (return %d)\n", + rLocal); + ABORT_FINALIZE(RS_RET_ERR); + } + curl_easy_setopt(pData->curlHandle, CURLOPT_USERPWD, authBuf); + curl_easy_setopt(pData->curlHandle, CURLOPT_PROXYAUTH, CURLAUTH_ANY); + } +finalize_it: + RETiRet; +} + + +/* this method does not directly submit but builds a batch instead. It + * may submit, if we have dynamic index/type and the current type or + * index changes. + */ +static rsRetVal +buildBatch(instanceData *pData, uchar *message, uchar **tpls) +{ + int length = strlen((char *)message); + int r; + uchar *searchIndex; + uchar *searchType; + uchar *parent; + DEFiRet; +# define META_STRT "{\"index\":{\"_index\": \"" +# define META_TYPE "\",\"_type\":\"" +# define META_PARENT "\",\"_parent\":\"" +# define META_END "\"}}\n" + + getIndexTypeAndParent(pData, tpls, &searchIndex, &searchType, &parent); +dbgprintf("AAA: searchIndex: '%s'\n", searchIndex); +dbgprintf("AAA: searchType: '%s'\n", searchType); +dbgprintf("AAA: parent: '%s'\n", parent); + r = es_addBuf(&pData->batch.data, META_STRT, sizeof(META_STRT)-1); + if(r == 0) r = es_addBuf(&pData->batch.data, (char*)searchIndex, + ustrlen(searchIndex)); + if(r == 0) r = es_addBuf(&pData->batch.data, META_TYPE, sizeof(META_TYPE)-1); + if(r == 0) r = es_addBuf(&pData->batch.data, (char*)searchType, + ustrlen(searchType)); + if(parent != NULL) { + if(r == 0) r = es_addBuf(&pData->batch.data, META_PARENT, sizeof(META_PARENT)-1); + if(r == 0) r = es_addBuf(&pData->batch.data, (char*)parent, ustrlen(parent)); + } + if(r == 0) r = es_addBuf(&pData->batch.data, META_END, sizeof(META_END)-1); + if(r == 0) r = es_addBuf(&pData->batch.data, (char*)message, length); + if(r == 0) r = es_addBuf(&pData->batch.data, "\n", sizeof("\n")-1); + if(r != 0) { + DBGPRINTF("omelasticsearch: growing batch failed with code %d\n", r); + ABORT_FINALIZE(RS_RET_ERR); + } + iRet = RS_RET_DEFER_COMMIT; + +finalize_it: + RETiRet; +} + +static rsRetVal +curlPost(instanceData *instance, uchar *message, int msglen, uchar **tpls) +{ + CURLcode code; + CURL *curl = instance->curlHandle; + DEFiRet; + + if(instance->dynSrchIdx || instance->dynSrchType || instance->dynParent) + CHKiRet(setCurlURL(instance, tpls)); + + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (char *)message); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (char *)message); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, msglen); +dbgprintf("omelasticsearch: do curl_easy_perform()\n"); + code = curl_easy_perform(curl); +DBGPRINTF("omelasticsearch: curl_easy_perform() returned %lld\n", (long long) code); + switch (code) { + case CURLE_COULDNT_RESOLVE_HOST: + case CURLE_COULDNT_RESOLVE_PROXY: + case CURLE_COULDNT_CONNECT: + case CURLE_WRITE_ERROR: + STATSCOUNTER_INC(indexConFail, mutIndexConFail); + DBGPRINTF("omelasticsearch: we are suspending ourselfs due " + "to failure %lld of curl_easy_perform()\n", + (long long) code); + return RS_RET_SUSPENDED; + default: + STATSCOUNTER_INC(indexSubmit, mutIndexSubmit); + return RS_RET_OK; + } +finalize_it: + RETiRet; +} + +BEGINbeginTransaction +CODESTARTbeginTransaction +dbgprintf("omelasticsearch: beginTransaction\n"); + if(!pData->bulkmode) { + FINALIZE; + } + + es_emptyStr(pData->batch.data); +finalize_it: +ENDbeginTransaction + + +BEGINdoAction +CODESTARTdoAction + if(pData->bulkmode) { + CHKiRet(buildBatch(pData, ppString[0], ppString)); + } else { +dbgprintf("omelasticsearch: doAction calling curlPost\n"); + CHKiRet(curlPost(pData, ppString[0], strlen((char*)ppString[0]), + ppString)); + } +finalize_it: +dbgprintf("omelasticsearch: result doAction: %d (bulkmode %d)\n", iRet, pData->bulkmode); +ENDdoAction + + +BEGINendTransaction + char *cstr; +CODESTARTendTransaction +dbgprintf("omelasticsearch: endTransaction init\n"); + cstr = es_str2cstr(pData->batch.data, NULL); + dbgprintf("omelasticsearch: endTransaction, batch: '%s'\n", cstr); + CHKiRet(curlPost(pData, (uchar*) cstr, strlen(cstr), NULL)); +finalize_it: + free(cstr); +dbgprintf("omelasticsearch: endTransaction done with %d\n", iRet); +ENDendTransaction + +/* elasticsearch POST result string ... useful for debugging */ +size_t +curlResult(void *ptr, size_t size, size_t nmemb, void *userdata) +{ + unsigned int i; + char *p = (char *)ptr; + char *jsonData = (char *)userdata; + static char ok[] = "{\"ok\":true,"; + + ASSERT(size == 1); +DBGPRINTF("omelasticsearch request: %s\n", jsonData); +DBGPRINTF("omelasticsearch result: "); +for (i = 0; i < nmemb; i++) + DBGPRINTF("%c", p[i]); +DBGPRINTF("\n"); + + if (size == 1 && + nmemb > sizeof(ok)-1 && + strncmp(p, ok, sizeof(ok)-1) == 0) { + STATSCOUNTER_INC(indexSuccess, mutIndexSuccess); +dbgprintf("omelasticsearch ok\n"); + } else { +dbgprintf("omelasticsearch fail\n"); + STATSCOUNTER_INC(indexFailed, mutIndexFailed); + if (Debug) { + DBGPRINTF("omelasticsearch (fail) request: %s\n", jsonData); + DBGPRINTF("omelasticsearch (fail) result: "); + for (i = 0; i < nmemb; i++) + DBGPRINTF("%c", p[i]); + DBGPRINTF("\n"); + } + } + return size * nmemb; +} + + +static rsRetVal +curlSetup(instanceData *pData) +{ + HEADER *header; + CURL *handle; + + handle = curl_easy_init(); + if (handle == NULL) { + return RS_RET_OBJ_CREATION_FAILED; + } + + header = curl_slist_append(NULL, "Content-Type: text/json; charset=utf-8"); + curl_easy_setopt(handle, CURLOPT_HTTPHEADER, header); + + curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curlResult); + curl_easy_setopt(handle, CURLOPT_POST, 1); + + pData->curlHandle = handle; + pData->postHeader = header; + + if( pData->bulkmode + || (pData->dynSrchIdx == 0 && pData->dynSrchType == 0 && pData->dynParent == 0)) { + /* in this case, we know no tpls are involved in the request-->NULL OK! */ + setCurlURL(pData, NULL); + } + + if(Debug) { + if(pData->dynSrchIdx == 0 && pData->dynSrchType == 0 && pData->dynParent == 0) + dbgprintf("omelasticsearch setup, using static REST URL\n"); + else + dbgprintf("omelasticsearch setup, we have a dynamic REST URL\n"); + } + return RS_RET_OK; +} + +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->server = NULL; + pData->port = 9200; + pData->uid = NULL; + pData->pwd = NULL; + pData->searchIndex = NULL; + pData->searchType = NULL; + pData->parent = NULL; + pData->timeout = NULL; + pData->dynSrchIdx = 0; + pData->dynSrchType = 0; + pData->dynParent = 0; + pData->asyncRepl = 0; + pData->bulkmode = 0; + pData->tplName = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; + int iNumTpls; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "server")) { + pData->server = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "serverport")) { + pData->port = (int) pvals[i].val.d.n, NULL; + } else if(!strcmp(actpblk.descr[i].name, "uid")) { + pData->uid = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "pwd")) { + pData->pwd = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "searchindex")) { + pData->searchIndex = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "searchtype")) { + pData->searchType = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "parent")) { + pData->parent = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "dynsearchindex")) { + pData->dynSrchIdx = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dynsearchtype")) { + pData->dynSrchType = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "dynparent")) { + pData->dynParent = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "bulkmode")) { + pData->bulkmode = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "timeout")) { + pData->timeout = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "asyncrepl")) { + pData->asyncRepl = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omelasticsearch: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->pwd != NULL && pData->uid == NULL) { + errmsg.LogError(0, RS_RET_UID_MISSING, + "omelasticsearch: password is provided, but no uid " + "- action definition invalid"); + ABORT_FINALIZE(RS_RET_UID_MISSING); + } + if(pData->dynSrchIdx && pData->searchIndex == NULL) { + errmsg.LogError(0, RS_RET_CONFIG_ERROR, + "omelasticsearch: requested dynamic search index, but no " + "name for index template given - action definition invalid"); + ABORT_FINALIZE(RS_RET_CONFIG_ERROR); + } + if(pData->dynSrchType && pData->searchType == NULL) { + errmsg.LogError(0, RS_RET_CONFIG_ERROR, + "omelasticsearch: requested dynamic search type, but no " + "name for type template given - action definition invalid"); + ABORT_FINALIZE(RS_RET_CONFIG_ERROR); + } + if(pData->dynParent && pData->parent == NULL) { + errmsg.LogError(0, RS_RET_CONFIG_ERROR, + "omelasticsearch: requested dynamic parent, but no " + "name for parent template given - action definition invalid"); + ABORT_FINALIZE(RS_RET_CONFIG_ERROR); + } + + if(pData->bulkmode) { + pData->batch.currTpl1 = NULL; + pData->batch.currTpl2 = NULL; + if((pData->batch.data = es_newStr(1024)) == NULL) { + DBGPRINTF("omelasticsearch: error creating batch string " + "turned off bulk mode\n"); + pData->bulkmode = 0; /* at least it works */ + } + } + + iNumTpls = 1; + if(pData->dynSrchIdx) ++iNumTpls; + if(pData->dynSrchType) ++iNumTpls; + if(pData->dynParent) ++iNumTpls; + DBGPRINTF("omelasticsearch: requesting %d templates\n", iNumTpls); + CODE_STD_STRING_REQUESTparseSelectorAct(iNumTpls) + + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*)strdup((pData->tplName == NULL) ? + " StdJSONFmt" : (char*)pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); + + + /* we need to request additional templates. If we have a dynamic search index, + * it will always be string 1. Type may be 1 or 2, depending on whether search + * index is dynamic as well. Rule needs to be followed throughout the module. + */ + if(pData->dynSrchIdx) { + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->searchIndex), + OMSR_NO_RQD_TPL_OPTS)); + if(pData->dynSrchType) { + CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->searchType), + OMSR_NO_RQD_TPL_OPTS)); + if(pData->dynParent) { + CHKiRet(OMSRsetEntry(*ppOMSR, 3, ustrdup(pData->parent), + OMSR_NO_RQD_TPL_OPTS)); + } + } else { + if(pData->dynParent) { + CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->parent), + OMSR_NO_RQD_TPL_OPTS)); + } + } + } else { + if(pData->dynSrchType) { + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->searchType), + OMSR_NO_RQD_TPL_OPTS)); + if(pData->dynParent) { + CHKiRet(OMSRsetEntry(*ppOMSR, 2, ustrdup(pData->parent), + OMSR_NO_RQD_TPL_OPTS)); + } + } else { + if(pData->dynParent) { + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(pData->parent), + OMSR_NO_RQD_TPL_OPTS)); + } + } + } + + if(pData->server == NULL) + pData->server = (uchar*) strdup("localhost"); + if(pData->searchIndex == NULL) + pData->searchIndex = (uchar*) strdup("system"); + if(pData->searchType == NULL) + pData->searchType = (uchar*) strdup("events"); + + CHKiRet(curlSetup(pData)); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct +CODE_STD_STRING_REQUESTparseSelectorAct(1) + if(!strncmp((char*) p, ":omelasticsearch:", sizeof(":omelasticsearch:") - 1)) { + errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED, + "omelasticsearch supports only v6 config format, use: " + "action(type=\"omelasticsearch\" server=...)"); + } + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit + curl_global_cleanup(); + statsobj.Destruct(&indexStats); + objRelease(errmsg, CORE_COMPONENT); + objRelease(statsobj, CORE_COMPONENT); +ENDmodExit + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */ +ENDqueryEtryPt + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); + + if (curl_global_init(CURL_GLOBAL_ALL) != 0) { + errmsg.LogError(0, RS_RET_OBJ_CREATION_FAILED, "CURL fail. -elasticsearch indexing disabled"); + ABORT_FINALIZE(RS_RET_OBJ_CREATION_FAILED); + } + + /* support statistics gathering */ + CHKiRet(statsobj.Construct(&indexStats)); + CHKiRet(statsobj.SetName(indexStats, (uchar *)"elasticsearch")); + CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"connfail", + ctrType_IntCtr, &indexConFail)); + CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"submits", + ctrType_IntCtr, &indexSubmit)); + CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"failed", + ctrType_IntCtr, &indexFailed)); + CHKiRet(statsobj.AddCounter(indexStats, (uchar *)"success", + ctrType_IntCtr, &indexSuccess)); + CHKiRet(statsobj.ConstructFinalize(indexStats)); +ENDmodInit + +/* vi:set ai: + */ diff --git a/plugins/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c index 25995248..818a7cfd 100644 --- a/plugins/omgssapi/omgssapi.c +++ b/plugins/omgssapi/omgssapi.c @@ -59,6 +59,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omgssapi") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -94,18 +95,12 @@ typedef enum gss_mode_e { GSSMODE_ENC } gss_mode_t; -typedef struct configSettings_s { +static struct configSettings_s { uchar *pszTplName; /* name of the default template to use */ char *gss_base_service_name; gss_mode_t gss_mode; -} configSettings_t; +} cs; -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ - -BEGINinitConfVars /* (re)set config variables to default values */ -CODESTARTinitConfVars - resetConfigVariables(NULL, NULL); -ENDinitConfVars /* get the syslog forward port from selector_t. The passed in * struct must be one that is setup for forwarding. @@ -698,7 +693,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); diff --git a/plugins/omhdfs/omhdfs.c b/plugins/omhdfs/omhdfs.c index 58b78a3d..cd14d03c 100644 --- a/plugins/omhdfs/omhdfs.c +++ b/plugins/omhdfs/omhdfs.c @@ -51,6 +51,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omhdfs") /* internal structures */ diff --git a/plugins/omhiredis/COPYING b/plugins/omhiredis/COPYING new file mode 100644 index 00000000..f44bd493 --- /dev/null +++ b/plugins/omhiredis/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 3 of the License, or + (at your option) any later version. + + This program 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, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>. diff --git a/plugins/omhiredis/COPYING_LESSER b/plugins/omhiredis/COPYING_LESSER new file mode 100644 index 00000000..ddae3e79 --- /dev/null +++ b/plugins/omhiredis/COPYING_LESSER @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/plugins/omhiredis/Makefile.am b/plugins/omhiredis/Makefile.am new file mode 100644 index 00000000..2332be4b --- /dev/null +++ b/plugins/omhiredis/Makefile.am @@ -0,0 +1,7 @@ +pkglib_LTLIBRARIES = omhiredis.la +omhiredis_la_SOURCES = omhiredis.c +omhiredis_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(HIREDIS_CFLAGS) +omhiredis_la_LDFLAGS = -module -avoid-version +omhiredis_la_LIBADD = $(HIREDIS_LIBS) + +EXTRA_DIST = diff --git a/plugins/omhiredis/README b/plugins/omhiredis/README new file mode 100644 index 00000000..5ca31373 --- /dev/null +++ b/plugins/omhiredis/README @@ -0,0 +1,29 @@ +Redis Outplug Plugin using hiredis library + +tested in Centos 6.2 + +BUILDING THIS PLUGIN +Requires the hiredis C client library: https://github.com/antirez/hiredis/ + +in your /etc/rsyslog.conf, together with other modules: + +TODO + +* Error handling for redis calls +* Integrating with impstats +* Clean up code +* Make it work with rsyslog batch mode +* Fix bugs + +Brian Knox <bknox@talksum.com> + +--------------------------------------------------------------------------------------------- +$ModLoad omhiredis.so # provides redis output + +$template TestRedis, "hincrby progcount %programname% 1" + +if $msg then { + action(type="omhiredis", template="TestRedis") +} +--------------------------------------------------------------------------------------------- + diff --git a/plugins/omhiredis/omhiredis.c b/plugins/omhiredis/omhiredis.c new file mode 100644 index 00000000..5435094b --- /dev/null +++ b/plugins/omhiredis/omhiredis.c @@ -0,0 +1,239 @@ +/* omhiredis.c + * Copyright 2012 Talksum, Inc +* +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public License +* as published by the Free Software Foundation, either version 3 of +* the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this program. If not, see +* <http://www.gnu.org/licenses/>. +* +* Author: Brian Knox +* <briank@talksum.com> +*/ + + + +#include "config.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <signal.h> +#include <time.h> +#include <hiredis/hiredis.h> + +#include "rsyslog.h" +#include "conf.h" +#include "syslogd-types.h" +#include "srUtils.h" +#include "template.h" +#include "module-template.h" +#include "errmsg.h" +#include "cfsysline.h" + +MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omhiredis") +/* internal structures + */ +DEF_OMOD_STATIC_DATA +DEFobjCurrIf(errmsg) + +typedef struct _instanceData { + redisContext *conn; + uchar *server; + int port; + uchar *tplName; +} instanceData; + + +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrGetWord, 0 }, + { "serverport", eCmdHdlrInt, 0 }, + { "template", eCmdHdlrGetWord, 1 } +}; +static struct cnfparamblk actpblk = { + CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr +}; + +BEGINcreateInstance +CODESTARTcreateInstance +ENDcreateInstance + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURERepeatedMsgReduction) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + +static void closeHiredis(instanceData *pData) +{ + if(pData->conn != NULL) { + redisFree(pData->conn); + pData->conn = NULL; + } +} + + +BEGINfreeInstance +CODESTARTfreeInstance + closeHiredis(pData); + free(pData->server); + free(pData->tplName); +ENDfreeInstance + + +BEGINdbgPrintInstInfo +CODESTARTdbgPrintInstInfo + /* nothing special here */ +ENDdbgPrintInstInfo + + +static rsRetVal initHiredis(instanceData *pData, int bSilent) +{ + char *server; + DEFiRet; + + server = (pData->server == NULL) ? "127.0.0.1" : (char*) pData->server; + DBGPRINTF("omhiredis: trying connect to '%s' at port %d\n", server, pData->port); + + struct timeval timeout = { 1, 500000 }; /* 1.5 seconds */ + pData->conn = redisConnectWithTimeout(server, pData->port, timeout); + if (pData->conn->err) { + if(!bSilent) + errmsg.LogError(0, RS_RET_SUSPENDED, + "can not initialize redis handle"); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + +finalize_it: + RETiRet; +} + +rsRetVal writeHiredis(uchar *message, instanceData *pData) +{ + redisReply *reply; + DEFiRet; + + if(pData->conn == NULL) + CHKiRet(initHiredis(pData, 0)); + + reply = redisCommand(pData->conn, (char*)message); + if (reply->type == REDIS_REPLY_ERROR) { + errmsg.LogError(0, NO_ERRCODE, "omhiredis: %s", reply->str); + dbgprintf("omhiredis: %s\n", reply->str); + freeReplyObject(reply); + ABORT_FINALIZE(RS_RET_ERR); + } else { + freeReplyObject(reply); + } + +finalize_it: + RETiRet; +} + +BEGINtryResume +CODESTARTtryResume + if(pData->conn == NULL) + iRet = initHiredis(pData, 0); +ENDtryResume + +BEGINdoAction +CODESTARTdoAction + iRet = writeHiredis(ppString[0], pData); +ENDdoAction + + +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->server = NULL; + pData->port = 6379; + pData->tplName = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + + if(!strcmp(actpblk.descr[i].name, "server")) { + pData->server = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "serverport")) { + pData->port = (int) pvals[i].val.d.n, NULL; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omhiredis: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + dbgprintf("omhiredis: action requires a template name"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + /* template string 0 is just a regular string */ + OMSRsetEntry(*ppOMSR, 0,(uchar*)pData->tplName, OMSR_NO_RQD_TPL_OPTS); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + +BEGINparseSelectorAct +CODESTARTparseSelectorAct + +/* tell the engine we only want one template string */ +CODE_STD_STRING_REQUESTparseSelectorAct(1) + if(!strncmp((char*) p, ":omhiredis:", sizeof(":omhiredis:") - 1)) + errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED, + "omhiredis supports only v6 config format, use: " + "action(type=\"omhiredis\" server=...)"); + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); +CODE_STD_FINALIZERparseSelectorAct +ENDparseSelectorAct + + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES +ENDqueryEtryPt + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* only supports rsyslog 6 configs */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(errmsg, CORE_COMPONENT)); + INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING); + DBGPRINTF("omhiredis: module compiled with rsyslog version %s.\n", VERSION); +ENDmodInit diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c index 2c67229b..8f5fa944 100644 --- a/plugins/omlibdbi/omlibdbi.c +++ b/plugins/omlibdbi/omlibdbi.c @@ -48,9 +48,11 @@ #include "module-template.h" #include "debug.h" #include "errmsg.h" +#include "conf.h" MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omlibdbi") /* internal structures */ @@ -59,6 +61,7 @@ DEFobjCurrIf(errmsg) static int bDbiInitialized = 0; /* dbi_initialize() can only be called one - this keeps track of it */ typedef struct _instanceData { + uchar *dbiDrvrDir; /* where do the dbi drivers reside? */ dbi_conn conn; /* handle to database */ uchar *drvrName; /* driver to use */ uchar *host; /* host to connect to */ @@ -66,6 +69,7 @@ typedef struct _instanceData { uchar *pwd; /* password for connect */ uchar *dbName; /* database to use */ unsigned uLastDBErrno; /* last errno returned by libdbi or 0 if all is well */ + uchar *tplName; /* format template to use */ } instanceData; typedef struct configSettings_s { @@ -76,8 +80,25 @@ typedef struct configSettings_s { uchar *pwd; /* password for connect */ uchar *dbName; /* database to use */ } configSettings_t; +static configSettings_t cs; + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrGetWord, 1 }, + { "db", eCmdHdlrGetWord, 1 }, + { "uid", eCmdHdlrGetWord, 1 }, + { "pwd", eCmdHdlrGetWord, 1 }, + { "driverdirectory", eCmdHdlrGetWord, 0 }, + { "driver", eCmdHdlrGetWord, 1 }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -123,6 +144,7 @@ static void closeConn(instanceData *pData) BEGINfreeInstance CODESTARTfreeInstance closeConn(pData); + free(pData->dbiDrvrDir); free(pData->drvrName); free(pData->host); free(pData->usrName); @@ -183,9 +205,9 @@ static rsRetVal initConn(instanceData *pData, int bSilent) if(bDbiInitialized == 0) { /* we need to init libdbi first */ # ifdef HAVE_DBI_R - iDrvrsLoaded = dbi_initialize_r((char*) cs.dbiDrvrDir, &dbiInst); + iDrvrsLoaded = dbi_initialize_r((char*) pData->dbiDrvrDir, &dbiInst); # else - iDrvrsLoaded = dbi_initialize((char*) cs.dbiDrvrDir); + iDrvrsLoaded = dbi_initialize((char*) pData->dbiDrvrDir); # endif if(iDrvrsLoaded == 0) { errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi or libdbi drivers not present on this system - suspending."); @@ -280,6 +302,62 @@ CODESTARTdoAction ENDdoAction +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->tplName = NULL; +} + + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "server")) { + pData->host = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "db")) { + pData->dbName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "uid")) { + pData->usrName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "pwd")) { + pData->pwd = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "driverdirectory")) { + pData->dbiDrvrDir = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "driver")) { + pData->drvrName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("ommysql: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*) strdup(" StdDBFmt"), + OMSR_RQD_TPL_OPT_SQL)); + } else { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_RQD_TPL_OPT_SQL)); + } +CODE_STD_FINALIZERnewActInst +dbgprintf("XXXX: added param, iRet %d\n", iRet); + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + BEGINparseSelectorAct CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) @@ -310,6 +388,8 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) if((pData->dbName = (uchar*) strdup((char*)cs.dbName)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); if(cs.pwd != NULL) if((pData->pwd = (uchar*) strdup((char*)cs.pwd)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + if(cs.dbiDrvrDir != NULL) + if((pData->dbiDrvrDir = (uchar*) strdup((char*)cs.dbiDrvrDir)) == NULL) ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdDBFmt")); @@ -333,6 +413,7 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt @@ -359,7 +440,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c index 05cc96d5..d70fa30a 100644 --- a/plugins/ommail/ommail.c +++ b/plugins/ommail/ommail.c @@ -54,6 +54,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("ommail") /* internal structures */ @@ -97,8 +98,7 @@ typedef struct configSettings_s { uchar *pszSubject; int bEnableBody; /* should a mail body be generated? (set to 0 eg for SMS gateways) */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -705,7 +705,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr /* tell which objects we need */ diff --git a/plugins/ommongodb/Makefile.am b/plugins/ommongodb/Makefile.am index 1b0e23a1..3a05c435 100644 --- a/plugins/ommongodb/Makefile.am +++ b/plugins/ommongodb/Makefile.am @@ -1,11 +1,7 @@ -mongodir = ./mongo-c-driver/src pkglib_LTLIBRARIES = ommongodb.la - ommongodb_la_SOURCES = ommongodb.c -ommongodb_la_SOURCES += $(mongodir)/bson.c $(mongodir)/mongo.c $(mongodir)/md5.c $(mongodir)/numbers.c - -ommongodb_la_CPPFLAGS = -DMONGO_HAVE_STDINT -Imongo-c-driver/src $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) +ommongodb_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(LIBMONGO_CLIENT_CFLAGS) ommongodb_la_LDFLAGS = -module -avoid-version -ommongodb_la_LIBADD = +ommongodb_la_LIBADD = $(LIBMONGO_CLIENT_LIBS) EXTRA_DIST = diff --git a/plugins/ommongodb/README b/plugins/ommongodb/README index cea3f3bc..7581131a 100644 --- a/plugins/ommongodb/README +++ b/plugins/ommongodb/README @@ -9,10 +9,10 @@ $ModLoad ommongodb # provides mongodb support then in your /etc/rsyslog.d (check your distribution way to organize the configuration..) you create a file 10-mongodb.conf with the following content: -#the format for the driver is :ommongodb:ip:db:collection;StdMongoDBFmt -#if you want to change what is logged in the db, the template, you must change the source code since the keys are hardcoded -$template StdMongoDBFmt,"%msg%%syslogfacility%%HOSTNAME%%syslogpriority%" -*.* :ommongodb:127.0.0.1,syslog,logs;StdMongoDBFmt +*.* action(type="ommongodb" db="..." collection="...") + +Note: currently templates are not supported. Ommongodb will pick a default +schema and use the message object content for that (templateless). TODO @@ -21,3 +21,5 @@ refactor my code :-) email Victor Pereira <victor.pereira@bigrails.com> twitter twitter.com/vpereira + +part of this doc by Rainer Gerhards <rgerhards@adiscon.com> diff --git a/plugins/ommongodb/ommongodb.c b/plugins/ommongodb/ommongodb.c index 8e19105f..39e2e4f9 100644 --- a/plugins/ommongodb/ommongodb.c +++ b/plugins/ommongodb/ommongodb.c @@ -1,3 +1,28 @@ +/* ommongodb.c + * Output module for mongodb. + * Note: this module uses the libmongo-client library. The original 10gen + * mongodb C interface is crap. Obtain the library here: + * https://github.com/algernon/libmongo-client + * + * Copyright 2007-2012 Rainer Gerhards and Adiscon GmbH. + * + * This file is part of rsyslog. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "config.h" #include <stdio.h> #include <string.h> #include <stdlib.h> @@ -6,54 +31,61 @@ #include <assert.h> #include <signal.h> #include <time.h> -#include "bson.h" -#include "mongo.h" -#include "config.h" +#include <mongo.h> + #include "rsyslog.h" #include "conf.h" #include "syslogd-types.h" #include "srUtils.h" #include "template.h" #include "module-template.h" +#include "datetime.h" #include "errmsg.h" #include "cfsysline.h" -#include "mongo-c-driver/src/mongo.h" - -#define countof(X) ( (size_t) ( sizeof(X)/sizeof*(X) ) ) - -#define DEFAULT_SERVER "127.0.0.1" -#define DEFAULT_DATABASE "syslog" -#define DEFAULT_COLLECTION "log" -#define DEFAULT_DB_COLLECTION "syslog.log" - -//i just defined some constants, i couldt not find the limit -#define MONGO_DB_NAME_SIZE 128 -#define MONGO_COLLECTION_NAME_SIZE 128 MODULE_TYPE_OUTPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("ommongodb") /* internal structures */ DEF_OMOD_STATIC_DATA DEFobjCurrIf(errmsg) +DEFobjCurrIf(datetime) typedef struct _instanceData { - mongo_connection conn[1]; /* ptr */ - mongo_connection_options opts[1]; - mongo_conn_return status; - char db[MONGO_DB_NAME_SIZE]; - char collection[MONGO_COLLECTION_NAME_SIZE]; - char dbcollection[MONGO_DB_NAME_SIZE + MONGO_COLLECTION_NAME_SIZE + 1]; - unsigned uLastMongoDBErrno; - //unsigned iSrvPort; /* sample: server port */ + mongo_sync_connection *conn; + uchar *server; + int port; + uchar *db; + uchar *collection; + uchar *uid; + uchar *pwd; + uchar *dbNcoll; + uchar *tplName; } instanceData; -char db[_DB_MAXDBLEN+2]; -static int iSrvPort = 27017; + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrGetWord, 0 }, + { "serverport", eCmdHdlrInt, 0 }, + { "db", eCmdHdlrGetWord, 0 }, + { "collection", eCmdHdlrGetWord, 0 }, + { "uid", eCmdHdlrGetWord, 0 }, + { "pwd", eCmdHdlrGetWord, 0 }, + { "template", eCmdHdlrGetWord, 1 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; + BEGINcreateInstance CODESTARTcreateInstance ENDcreateInstance - BEGINisCompatibleWithFeature CODESTARTisCompatibleWithFeature /* use this to specify if select features are supported by this @@ -66,149 +98,200 @@ ENDisCompatibleWithFeature static void closeMongoDB(instanceData *pData) { - ASSERT(pData != NULL); - if(pData->conn != NULL) { - mongo_destroy( pData->conn ); - memset(pData->conn,0x00,sizeof(mongo_connection)); + mongo_sync_disconnect(pData->conn); + pData->conn = NULL; } } + BEGINfreeInstance CODESTARTfreeInstance closeMongoDB(pData); + free(pData->server); + free(pData->db); + free(pData->collection); + free(pData->uid); + free(pData->pwd); + free(pData->tplName); ENDfreeInstance + BEGINdbgPrintInstInfo CODESTARTdbgPrintInstInfo /* nothing special here */ ENDdbgPrintInstInfo -/* log a database error with descriptive message. - * We check if we have a valid MongoDB handle. If not, we simply - * report an error + +/* report error that occured during *last* operation */ -static void reportDBError(instanceData *pData, int bSilent) +static void +reportMongoError(instanceData *pData) { - char errMsg[512]; - bson ErrObj; - - ASSERT(pData != NULL); - - /* output log message */ - errno = 0; - if(pData->conn == NULL) { - errmsg.LogError(0, NO_ERRCODE, "unknown DB error occured - could not obtain MongoDB handle"); - } else { /* we can ask mysql for the error description... */ - //we should handle the error. if bSilent is set then we should print as debug - mongo_cmd_get_last_error(pData->conn, pData->db, &ErrObj); - bson_destroy(&ErrObj); + char errStr[1024]; + errmsg.LogError(0, RS_RET_ERR, "ommongodb: error: %s", + rs_strerror_r(errno, errStr, sizeof(errStr))); +#if 0 + gchar *err; + if(mongo_sync_cmd_get_last_error(pData->conn, (gchar*)pData->db, &err) == TRUE) { + errmsg.LogError(0, RS_RET_ERR, "ommongodb: error: %s", err); + } else { + errmsg.LogError(0, RS_RET_ERR, "ommongodb: we had an error, but can " + "not obtain specifics"); } - - return; +#endif } + /* The following function is responsible for initializing a * MySQL connection. * Initially added 2004-10-28 mmeckelein */ static rsRetVal initMongoDB(instanceData *pData, int bSilent) { + char *server; DEFiRet; - ASSERT(pData != NULL); - ASSERT(pData->conn == NULL); - - //I'm trying to fallback to a default here - if(pData->opts->port == 0) - pData->opts->port = 27017; - - if(pData->opts->host == 0x00) - strcpy(pData->opts->host,DEFAULT_SERVER); - - if(pData->dbcollection == 0x00) - strcpy(pData->dbcollection,DEFAULT_DB_COLLECTION); + server = (pData->server == NULL) ? "127.0.0.1" : (char*) pData->server; + DBGPRINTF("ommongodb: trying connect to '%s' at port %d\n", server, pData->port); - pData->status = mongo_connect(pData->conn, pData->opts ); - - switch (pData->status) { - case mongo_conn_success: - fprintf(stderr, "connection succeeded\n" ); - iRet = RS_RET_OK; - break; - case mongo_conn_bad_arg: - errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle"); - fprintf(stderr, "bad arguments\n" ); - iRet = RS_RET_SUSPENDED; - break; - case mongo_conn_no_socket: - errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle"); - fprintf(stderr, "no socket\n" ); - iRet = RS_RET_SUSPENDED; - break; - case mongo_conn_fail: - errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle"); - fprintf(stderr, "connection failed\n" ); - iRet = RS_RET_SUSPENDED; - break; - case mongo_conn_not_master: - errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MongoDB handle"); - fprintf(stderr, "not master\n" ); - iRet = RS_RET_SUSPENDED; - break; - } + pData->conn = mongo_sync_connect(server, pData->port, TRUE); + if(pData->conn == NULL) { + if(!bSilent) { + reportMongoError(pData); + dbgprintf("ommongodb: can not initialize MongoDB handle"); + } + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + +finalize_it: RETiRet; } -//we must implement it -rsRetVal writeMongoDB(uchar *psz, instanceData *pData) -{ - char mydate[32]; - char **szParams; - bson b[1]; - bson_buffer buf[1]; - bson_buffer_init( buf ); - bson_append_new_oid(buf, "_id" ); - memset(mydate,0x00,32); - - - DEFiRet; - - ASSERT(psz != NULL); - ASSERT(pData != NULL); - - - /* see if we are ready to proceed */ - if(pData->conn == NULL) { - CHKiRet(initMongoDB(pData, 0)); - } -szParams = (char**)(void*) psz; -//We can make it beter -//if you change the fields in your template, we must update it here -//there is any C_metaprogramming_ninja there? :-) -if(countof(szParams) > 0) +/* map syslog severity to lumberjack level + * TODO: consider moving this to msg.c - make some dirty "friend" references... + * rgerhards, 2012-03-19 + */ +static inline char * +getLumberjackLevel(short severity) { - bson_append_string( buf, "msg", szParams[0]); - bson_append_string( buf, "facility",szParams[1]); - bson_append_string( buf, "hostname", szParams[2] ); - bson_append_string(buf, "priority",szParams[3]); - bson_append_int(buf,"count",countof(szParams)); - bson_from_buffer( b, buf ); - mongo_insert(pData->conn, pData->dbcollection, b ); + switch(severity) { + case 0: return "FATAL"; + case 1: + case 2: + case 3: return "ERROR"; + case 4: return "WARN"; + case 5: + case 6: return "INFO"; + case 7: return "DEBUG"; + default:DBGPRINTF("ommongodb: invalid syslog severity %u\n", severity); + return "INVLD"; + } } -if(b) - bson_destroy(b); +/* small helper: get integer power of 10 */ +static inline int +i10pow(int exp) +{ + int r = 1; + while(exp > 0) { + r *= 10; + exp--; + } + return r; +} +/* write to mongodb in MSG passing mode, that is without a template. + * In this mode, we use the standard document format, which is somewhat + * aligned to cee (as described in project lumberjack). Note that this is + * a moving target, so we may run out of sync (and stay so to retain + * backward compatibility, which we consider pretty important). + */ +rsRetVal writeMongoDB_msg(msg_t *pMsg, instanceData *pData) +{ + bson *doc = NULL; + uchar *procid; short unsigned procid_free; size_t procid_len; + uchar *tag; short unsigned tag_free; size_t tag_len; + uchar *pid; short unsigned pid_free; size_t pid_len; + uchar *sys; short unsigned sys_free; size_t sys_len; + uchar *msg; short unsigned msg_free; size_t msg_len; + int severity, facil; + gint64 ts_gen, ts_rcv; /* timestamps: generated, received */ + int secfrac; + DEFiRet; - finalize_it: - if(iRet == RS_RET_OK) { - pData->uLastMongoDBErrno = 0; /* reset error for error supression */ + /* see if we are ready to proceed */ + if(pData->conn == NULL) { + CHKiRet(initMongoDB(pData, 0)); } - - RETiRet; + procid = MsgGetProp(pMsg, NULL, PROP_PROGRAMNAME, NULL, &procid_len, &procid_free); + tag = MsgGetProp(pMsg, NULL, PROP_SYSLOGTAG, NULL, &tag_len, &tag_free); + pid = MsgGetProp(pMsg, NULL, PROP_PROCID, NULL, &pid_len, &pid_free); + sys = MsgGetProp(pMsg, NULL, PROP_HOSTNAME, NULL, &sys_len, &sys_free); + msg = MsgGetProp(pMsg, NULL, PROP_MSG, NULL, &msg_len, &msg_free); + + // TODO: move to datetime? Refactor in any case! rgerhards, 2012-03-30 + ts_gen = (gint64) datetime.syslogTime2time_t(&pMsg->tTIMESTAMP) * 1000; /* ms! */ +dbgprintf("ommongodb: ts_gen is %lld\n", (long long) ts_gen); +dbgprintf("ommongodb: secfrac is %d, precision %d\n", pMsg->tTIMESTAMP.secfrac, pMsg->tTIMESTAMP.secfracPrecision); + if(pMsg->tTIMESTAMP.secfracPrecision > 3) { + secfrac = pMsg->tTIMESTAMP.secfrac / i10pow(pMsg->tTIMESTAMP.secfracPrecision - 3); + } else if(pMsg->tTIMESTAMP.secfracPrecision < 3) { + secfrac = pMsg->tTIMESTAMP.secfrac * i10pow(3 - pMsg->tTIMESTAMP.secfracPrecision); + } else { + secfrac = pMsg->tTIMESTAMP.secfrac; + } + ts_gen += secfrac; + ts_rcv = (gint64) datetime.syslogTime2time_t(&pMsg->tRcvdAt) * 1000; /* ms! */ + if(pMsg->tRcvdAt.secfracPrecision > 3) { + secfrac = pMsg->tRcvdAt.secfrac / i10pow(pMsg->tRcvdAt.secfracPrecision - 3); + } else if(pMsg->tRcvdAt.secfracPrecision < 3) { + secfrac = pMsg->tRcvdAt.secfrac * i10pow(3 - pMsg->tRcvdAt.secfracPrecision); + } else { + secfrac = pMsg->tRcvdAt.secfrac; + } + ts_rcv += secfrac; + + /* the following need to be int, but are short, so we need to xlat */ + severity = pMsg->iSeverity; + facil = pMsg->iFacility; + + doc = bson_build(BSON_TYPE_STRING, "sys", sys, sys_len, + BSON_TYPE_UTC_DATETIME, "time", ts_gen, + BSON_TYPE_UTC_DATETIME, "time_rcvd", ts_rcv, + BSON_TYPE_STRING, "msg", msg, msg_len, + BSON_TYPE_INT32, "syslog_fac", facil, + BSON_TYPE_INT32, "syslog_sever", severity, + BSON_TYPE_STRING, "syslog_tag", tag, tag_len, + BSON_TYPE_STRING, "procid", procid, procid_len, + BSON_TYPE_STRING, "pid", pid, pid_len, + BSON_TYPE_STRING, "level", getLumberjackLevel(pMsg->iSeverity), -1, + BSON_TYPE_NONE); + + if(procid_free) free(procid); + if(tag_free) free(tag); + if(pid_free) free(pid); + if(sys_free) free(sys); + if(msg_free) free(msg); + + if(doc == NULL) { + reportMongoError(pData); + dbgprintf("ommongodb: error creating BSON doc\n"); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + bson_finish(doc); + if(!mongo_sync_cmd_insert(pData->conn, (char*)pData->dbNcoll, doc, NULL)) { + reportMongoError(pData); + dbgprintf("ommongodb: insert error\n"); + ABORT_FINALIZE(RS_RET_SUSPENDED); + } + +finalize_it: + if(doc != NULL) + bson_free(doc); + RETiRet; } BEGINtryResume @@ -220,53 +303,117 @@ ENDtryResume BEGINdoAction CODESTARTdoAction - iRet = writeMongoDB(ppString[0], pData); + if(pData->tplName == NULL) { + iRet = writeMongoDB_msg((msg_t*)ppString[0], pData); + } ENDdoAction + +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->server = NULL; + pData->port = 27017; + pData->db = NULL; + pData->collection= NULL; + pData->uid = NULL; + pData->pwd = NULL; + pData->tplName = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; + unsigned lendb, lencoll; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "server")) { + pData->server = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "serverport")) { + pData->port = (int) pvals[i].val.d.n, NULL; + } else if(!strcmp(actpblk.descr[i].name, "db")) { + pData->db = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "collection")) { + pData->collection = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "uid")) { + pData->uid = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "pwd")) { + pData->pwd = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("ommongodb: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG)); + } else { + errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED, + "ommongodb: templates are not supported in this version"); + ABORT_FINALIZE(RS_RET_ERR); + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_TPL_AS_ARRAY)); + } + + if(pData->db == NULL) + pData->db = (uchar*)strdup("syslog"); + if(pData->collection == NULL) + pData->collection = (uchar*)strdup("log"); + + /* we now create a db+collection string as we need to pass this + * into the API and we do not want to generate it each time ;) + * +2 ==> dot as delimiter and \0 + */ + lendb = strlen((char*)pData->db); + lencoll = strlen((char*)pData->collection); + CHKmalloc(pData->dbNcoll = malloc(lendb+lencoll+2)); + memcpy(pData->dbNcoll, pData->db, lendb); + pData->dbNcoll[lendb] = '.'; + /* lencoll+1 => copy \0! */ + memcpy(pData->dbNcoll+lendb+1, pData->collection, lencoll+1); + +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + BEGINparseSelectorAct - //int iMongoDBPropErr = 0; CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) - if(!strncmp((char*) p, ":ommongodb:", sizeof(":ommongodb:") - 1)) { - p += sizeof(":ommongodb:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ - } else { - ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); + errmsg.LogError(0, RS_RET_LEGA_ACT_NOT_SUPPORTED, + "ommongodb supports only v6 config format, use: " + "action(type=\"ommongodb\" server=...)"); } - - CHKiRet(createInstance(&pData)); - - if(getSubString(&p, pData->opts->host, MAXHOSTNAMELEN+1, ',')) - strcpy(pData->opts->host,DEFAULT_SERVER); - - //we must define the max db name - if(getSubString(&p,pData->db,255,',')) - strcpy(pData->db,DEFAULT_DATABASE); - if(getSubString(&p,pData->collection,255,';')) - strcpy(pData->collection,DEFAULT_COLLECTION); - if(*(p-1) == ';') - --p; - - - CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_TPL_AS_ARRAY, (uchar*) " StdMongoDBFmt")); - - - pData->opts->port = (unsigned) iSrvPort; /* set configured port */ - sprintf(pData->dbcollection,"%s.%s",pData->db,pData->collection); - CHKiRet(initMongoDB(pData, 0)); - + ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); CODE_STD_FINALIZERparseSelectorAct ENDparseSelectorAct BEGINmodExit CODESTARTmodExit + objRelease(errmsg, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt BEGINmodInit() @@ -274,7 +421,8 @@ 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)); INITChkCoreFeature(bCoreSupportsBatching, CORE_FEATURE_BATCHING); - DBGPRINTF("ompgsql: module compiled with rsyslog version %s.\n", VERSION); - DBGPRINTF("ompgsql: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not "); -ENDmodInit
\ No newline at end of file + DBGPRINTF("ommongodb: module compiled with rsyslog version %s.\n", VERSION); + //DBGPRINTF("ommongodb: %susing transactional output interface.\n", bCoreSupportsBatching ? "" : "not "); +ENDmodInit diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c index 79fd1b9e..253fc4c0 100644 --- a/plugins/ommysql/ommysql.c +++ b/plugins/ommysql/ommysql.c @@ -45,6 +45,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("ommysql") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -63,6 +64,7 @@ typedef struct _instanceData { unsigned uLastMySQLErrno; /* last errno returned by MySQL or 0 if all is well */ uchar * f_configfile; /* MySQL Client Configuration File */ uchar * f_configsection; /* MySQL Client Configuration Section */ + uchar *tplName; /* format template to use */ } instanceData; typedef struct configSettings_s { @@ -70,8 +72,26 @@ typedef struct configSettings_s { uchar *pszMySQLConfigFile; /* MySQL Client Configuration File */ uchar *pszMySQLConfigSection; /* MySQL Client Configuration Section */ } configSettings_t; +static configSettings_t cs; + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrGetWord, 1 }, + { "db", eCmdHdlrGetWord, 1 }, + { "uid", eCmdHdlrGetWord, 1 }, + { "pwd", eCmdHdlrGetWord, 1 }, + { "serverport", eCmdHdlrInt, 0 }, + { "mysqlconfig.file", eCmdHdlrGetWord, 0 }, + { "mysqlconfig.section", eCmdHdlrGetWord, 0 }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -115,6 +135,9 @@ static void closeMySQL(instanceData *pData) BEGINfreeInstance CODESTARTfreeInstance + free(pData->f_configfile); + free(pData->f_configsection); + free(pData->tplName); closeMySQL(pData); ENDfreeInstance @@ -256,6 +279,80 @@ CODESTARTdoAction ENDdoAction +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->f_dbsrvPort = 0; + pData->f_configfile = NULL; + pData->f_configsection = NULL; + pData->tplName = NULL; + pData->f_hmysql = NULL; /* initialize, but connect only on first message (important for queued mode!) */ +} + + +/* note: we use the fixed-size buffers inside the config object to avoid + * changing too much of the previous plumbing. rgerhards, 2012-02-02 + */ +BEGINnewActInst + struct cnfparamvals *pvals; + int i; + char *cstr; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "server")) { + cstr = es_str2cstr(pvals[i].val.d.estr, NULL); + strncpy(pData->f_dbsrv, cstr, sizeof(pData->f_dbsrv)); + free(cstr); + } else if(!strcmp(actpblk.descr[i].name, "serverport")) { + pData->f_dbsrvPort = (int) pvals[i].val.d.n, NULL; + } else if(!strcmp(actpblk.descr[i].name, "db")) { + cstr = es_str2cstr(pvals[i].val.d.estr, NULL); + strncpy(pData->f_dbname, cstr, sizeof(pData->f_dbname)); + free(cstr); + } else if(!strcmp(actpblk.descr[i].name, "uid")) { + cstr = es_str2cstr(pvals[i].val.d.estr, NULL); + strncpy(pData->f_dbuid, cstr, sizeof(pData->f_dbuid)); + free(cstr); + } else if(!strcmp(actpblk.descr[i].name, "pwd")) { + cstr = es_str2cstr(pvals[i].val.d.estr, NULL); + strncpy(pData->f_dbpwd, cstr, sizeof(pData->f_dbpwd)); + free(cstr); + } else if(!strcmp(actpblk.descr[i].name, "mysqlconfig.file")) { + pData->f_configfile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "mysqlconfig.section")) { + pData->f_configsection = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("ommysql: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*) strdup(" StdDBFmt"), + OMSR_RQD_TPL_OPT_SQL)); + } else { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_RQD_TPL_OPT_SQL)); + } +CODE_STD_FINALIZERnewActInst +dbgprintf("XXXX: added param, iRet %d\n", iRet); + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + BEGINparseSelectorAct int iMySQLPropErr = 0; CODESTARTparseSelectorAct @@ -339,6 +436,7 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt @@ -357,7 +455,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); diff --git a/plugins/omoracle/omoracle.c b/plugins/omoracle/omoracle.c index a37533ee..736629a6 100644 --- a/plugins/omoracle/omoracle.c +++ b/plugins/omoracle/omoracle.c @@ -83,6 +83,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omoracle") /** */ DEF_OMOD_STATIC_DATA diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c index df9cc3fe..11f346f6 100644 --- a/plugins/ompgsql/ompgsql.c +++ b/plugins/ompgsql/ompgsql.c @@ -50,6 +50,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("ompgsql") /* internal structures */ @@ -68,8 +69,7 @@ typedef struct _instanceData { typedef struct configSettings_s { EMPTY_STRUCT } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t __attribute__((unused)) cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -367,7 +367,7 @@ ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c index ae086a14..d71de320 100644 --- a/plugins/omprog/omprog.c +++ b/plugins/omprog/omprog.c @@ -45,6 +45,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omprog") /* internal structures */ @@ -53,6 +54,7 @@ DEFobjCurrIf(errmsg) typedef struct _instanceData { uchar *szBinary; /* name of binary to call */ + uchar *tplName; /* assigned output template */ pid_t pid; /* pid of currently running process */ int fdPipe; /* file descriptor to write to */ int bIsRunning; /* is binary currently running? 0-no, 1-yes */ @@ -61,8 +63,20 @@ typedef struct _instanceData { typedef struct configSettings_s { uchar *szBinary; /* name of binary to call */ } configSettings_t; +static configSettings_t cs; -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "binary", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -298,6 +312,51 @@ CODESTARTdoAction ENDdoAction +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->szBinary = NULL; + pData->fdPipe = -1; + pData->bIsRunning = 0; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "binary")) { + pData->szBinary = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("omprog: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*) "RSYSLOG_FileFormat", + OMSR_NO_RQD_TPL_OPTS)); + } else { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); + } +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + BEGINparseSelectorAct CODESTARTparseSelectorAct CODE_STD_STRING_REQUESTparseSelectorAct(1) @@ -308,6 +367,12 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) /* ok, if we reach this point, we have something for us */ p += sizeof(":omprog:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ + if(cs.szBinary == NULL) { + errmsg.LogError(0, RS_RET_CONF_RQRD_PARAM_MISSING, + "no binary to execute specified"); + ABORT_FINALIZE(RS_RET_CONF_RQRD_PARAM_MISSING); + } + CHKiRet(createInstance(&pData)); if(cs.szBinary == NULL) { @@ -337,6 +402,8 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt @@ -354,7 +421,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(errmsg, CORE_COMPONENT)); diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c index 3411c6d1..39ffe7fb 100644 --- a/plugins/omrelp/omrelp.c +++ b/plugins/omrelp/omrelp.c @@ -46,6 +46,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omrelp") /* internal structures */ @@ -67,8 +68,7 @@ typedef struct _instanceData { typedef struct configSettings_s { EMPTY_STRUCT } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t __attribute__((unused)) cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -346,7 +346,7 @@ ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr /* create our relp engine */ diff --git a/plugins/omruleset/omruleset.c b/plugins/omruleset/omruleset.c index a31a778a..67aee97e 100644 --- a/plugins/omruleset/omruleset.c +++ b/plugins/omruleset/omruleset.c @@ -50,6 +50,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omruleset") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -73,8 +74,7 @@ typedef struct configSettings_s { ruleset_t *pRuleset; /* ruleset to enqueue message to (NULL = Default, not recommended) */ uchar *pszRulesetName; } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -131,7 +131,7 @@ setRuleset(void __attribute__((unused)) *pVal, uchar *pszName) rsRetVal localRet; DEFiRet; - localRet = ruleset.GetRuleset(&cs.pRuleset, pszName); + localRet = ruleset.GetRuleset(ourConf, &cs.pRuleset, pszName); if(localRet == RS_RET_NOT_FOUND) { errmsg.LogError(0, RS_RET_RULESET_NOT_FOUND, "error: ruleset '%s' not found - ignored", pszName); } @@ -214,7 +214,7 @@ BEGINmodInit() unsigned long opts; int bMsgPassingSupported; /* does core support template passing as an array? */ CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr /* check if the rsyslog core supports parameter passing code */ diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c index 0a4279bc..e279c852 100644 --- a/plugins/omsnmp/omsnmp.c +++ b/plugins/omsnmp/omsnmp.c @@ -44,6 +44,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omsnmp") /* internal structures */ @@ -60,13 +61,12 @@ static oid objid_sysuptime[] = { 1, 3, 6, 1, 2, 1, 1, 3, 0 }; 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: + uchar *szTransport; /* Transport - Can be udp, tcp, udp6, tcp6 and other types supported by NET-SNMP */ + uchar *szTarget; /* IP/hostname of Snmp Target*/ + uchar *szCommunity; /* Snmp Community */ + uchar *szEnterpriseOID;/* Snmp Enterprise OID - default is (1.3.6.1.4.1.3.1.1 = enterprises.cmu.1.1) */ + uchar *szSnmpTrapOID; /* Snmp Trap OID - default is (1.3.6.1.4.1.19406.1.2.1 = ADISCON-MONITORWARE-MIB::syslogtrap) */ + uchar *szSyslogMessageOID; /* 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. @@ -80,6 +80,7 @@ typedef struct _instanceData { int iSpecificType; /* Snmp Specific Type */ netsnmp_session *snmpsession; /* Holds to SNMP Session, NULL if not initialized */ + uchar *tplName; /* format template to use */ } instanceData; typedef struct configSettings_s { @@ -105,8 +106,28 @@ typedef struct configSettings_s { SNMP_TRAP_ENTERPRISESPECIFIC (6) */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; + +/* tables for interfacing with the v6 config system */ +/* action (instance) parameters */ +static struct cnfparamdescr actpdescr[] = { + { "server", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "port", eCmdHdlrInt, CNFPARAM_REQUIRED }, + { "transport", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "version", eCmdHdlrInt, CNFPARAM_REQUIRED }, + { "community", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "enterpriseoid", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "trapoid", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "messageoid", eCmdHdlrString, CNFPARAM_REQUIRED }, + { "traptype", eCmdHdlrInt, CNFPARAM_REQUIRED }, + { "specifictype", eCmdHdlrInt, CNFPARAM_REQUIRED }, + { "template", eCmdHdlrGetWord, 0 } +}; +static struct cnfparamblk actpblk = + { CNFPARAMBLK_VERSION, + sizeof(actpdescr)/sizeof(struct cnfparamdescr), + actpdescr + }; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -132,7 +153,6 @@ 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); @@ -169,13 +189,18 @@ static rsRetVal omsnmp_exitSession(instanceData *pData) */ static rsRetVal omsnmp_initSession(instanceData *pData) { - DEFiRet; netsnmp_session session; + char szTargetAndPort[MAXHOSTNAMELEN+128]; /* work buffer for specifying a full target and port string */ + DEFiRet; /* should not happen, but if session is not cleared yet - we do it now! */ if (pData->snmpsession != NULL) omsnmp_exitSession(pData); + snprintf((char*)szTargetAndPort, sizeof(szTargetAndPort), "%s:%s:%d", + (pData->szTransport == NULL) ? "udp" : (char*)pData->szTransport, + pData->szTarget, pData->iPort == 0 ? 162 : pData->iPort); + dbgprintf( "omsnmp_initSession: ENTER - Target = '%s' on Port = '%d'\n", pData->szTarget, pData->iPort); putenv(strdup("POSIXLY_CORRECT=1")); @@ -184,13 +209,12 @@ static rsRetVal omsnmp_initSession(instanceData *pData) session.version = pData->iSNMPVersion; session.callback = NULL; /* NOT NEEDED */ session.callback_magic = NULL; - session.peername = (char*) pData->szTargetAndPort; + session.peername = (char*) 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); + if (session.version == SNMP_VERSION_1 || session.version == SNMP_VERSION_2c) { + session.community = (unsigned char *) pData->szCommunity == NULL ? (uchar*)"public" : pData->szCommunity; + session.community_len = strlen((char*) session.community); } pData->snmpsession = snmp_open(&session); @@ -226,16 +250,15 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) dbgprintf( "omsnmp_sendsnmp: ENTER - Syslogmessage = '%s'\n", (char*)psz); /* If SNMP Version1 is configured !*/ - if ( pData->snmpsession->version == SNMP_VERSION_1) - { + 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 )) - { + if(!snmp_parse_oid(pData->szEnterpriseOID == NULL ? "1.3.6.1.4.1.3.1.1" : (char*)pData->szEnterpriseOID, + enterpriseoid, &enterpriseoidlen )) { strErr = snmp_api_errstring(snmp_errno); - errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Parsing EnterpriseOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); - + errmsg.LogError(0, RS_RET_DISABLE_ACTION, "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)); @@ -267,8 +290,9 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) 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) - { + if ( snmp_add_var(pdu, objid_snmptrap, sizeof(objid_snmptrap) / sizeof(oid), 'o', + pData->szSnmpTrapOID == NULL ? "1.3.6.1.4.1.19406.1.2.1" : (char*) pData->szSnmpTrapOID + ) != 0) { strErr = snmp_api_errstring(snmp_errno); errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Adding trap OID failed '%s' with error '%s' \n", pData->szSnmpTrapOID, strErr); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); @@ -279,18 +303,16 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz) /* dbgprintf( "omsnmp_sendsnmp: SyslogMessage '%s'\n", psz );*/ /* First create new OID object */ - if (snmp_parse_oid( (char*) pData->szSyslogMessageOID, oidSyslogMessage, &oLen)) - { + if (snmp_parse_oid(pData->szSyslogMessageOID == NULL ? + "1.3.6.1.4.1.19406.1.1.2.1" : (char*)pData->szSyslogMessageOID, + oidSyslogMessage, &oLen)) { int iErrCode = snmp_add_var(pdu, oidSyslogMessage, oLen, 's', (char*) psz); - if (iErrCode) - { + if (iErrCode) { const char *str = snmp_api_errstring(iErrCode); errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Invalid SyslogMessage OID, error code '%d' - '%s'\n", iErrCode, str ); ABORT_FINALIZE(RS_RET_DISABLE_ACTION); } - } - else - { + } else { strErr = snmp_api_errstring(snmp_errno); errmsg.LogError(0, RS_RET_DISABLE_ACTION, "omsnmp_sendsnmp: Parsing SyslogMessageOID failed '%s' with error '%s' \n", pData->szSyslogMessageOID, strErr); @@ -345,16 +367,82 @@ CODESTARTfreeInstance /* free snmp Session here */ omsnmp_exitSession(pData); - if(pData->szTarget != NULL) - free(pData->szTarget); - if(pData->szTargetAndPort != NULL) - free(pData->szTargetAndPort); - + free(pData->tplName); + free(pData->szTarget); ENDfreeInstance +static inline void +setInstParamDefaults(instanceData *pData) +{ + pData->tplName = NULL; + pData->szCommunity = NULL; + pData->szEnterpriseOID = NULL; + pData->szSnmpTrapOID = NULL; + pData->szSyslogMessageOID = NULL; +} + +BEGINnewActInst + struct cnfparamvals *pvals; + int i; +CODESTARTnewActInst + if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) { + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + CHKiRet(createInstance(&pData)); + setInstParamDefaults(pData); + + CODE_STD_STRING_REQUESTparseSelectorAct(1) + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(actpblk.descr[i].name, "server")) { + pData->szTarget = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "port")) { + pData->iPort = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "transport")) { + pData->szTransport = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "version")) { + pData->iSNMPVersion = pvals[i].val.d.n; + if(pData->iSNMPVersion < 0 || cs.iSNMPVersion > 1) + pData->iSNMPVersion = 1; + } else if(!strcmp(actpblk.descr[i].name, "community")) { + pData->szCommunity = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "enterpriseoid")) { + pData->szEnterpriseOID = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "trapoid")) { + pData->szSnmpTrapOID = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "messageoid")) { + pData->szSyslogMessageOID = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(actpblk.descr[i].name, "traptype")) { + pData->iTrapType = pvals[i].val.d.n; + if(cs.iTrapType < 0 && cs.iTrapType >= 6) + pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC; + } else if(!strcmp(actpblk.descr[i].name, "specifictype")) { + pData->iSpecificType = pvals[i].val.d.n; + } else if(!strcmp(actpblk.descr[i].name, "template")) { + pData->tplName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("ompipe: program error, non-handled " + "param '%s'\n", actpblk.descr[i].name); + } + } + + if(pData->tplName == NULL) { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, (uchar*) "RSYSLOG_FileFormat", + OMSR_NO_RQD_TPL_OPTS)); + } else { + CHKiRet(OMSRsetEntry(*ppOMSR, 0, + (uchar*) strdup((char*) pData->tplName), + OMSR_NO_RQD_TPL_OPTS)); + } +CODE_STD_FINALIZERnewActInst + cnfparamvalsDestruct(pvals, &actpblk); +ENDnewActInst + + 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)) { @@ -367,55 +455,21 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) if((iRet = createInstance(&pData)) != RS_RET_OK) FINALIZE; - /* Check Transport */ - if (cs.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*) cs.pszTransport, strlen((char*) cs.pszTransport) ); - } - /* Check Target */ - if (cs.pszTarget == NULL) { + if(cs.pszTarget == NULL) { ABORT_FINALIZE( RS_RET_PARAM_ERROR ); } else { - /* Copy Target */ CHKmalloc(pData->szTarget = (uchar*) strdup((char*)cs.pszTarget)); } - /* Copy Community */ - if (cs.pszCommunity == NULL) /* Failsave */ - strncpy( (char*) pData->szCommunity, "public", sizeof("public") ); - else /* Copy Target */ - strncpy( (char*) pData->szCommunity, (char*) cs.pszCommunity, strlen((char*) cs.pszCommunity) ); - - /* Copy Enterprise OID */ - if (cs.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*) cs.pszEnterpriseOID, strlen((char*) cs.pszEnterpriseOID) ); - - /* Copy SnmpTrap OID */ - if (cs.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*) cs.pszSnmpTrapOID, strlen((char*) cs.pszSnmpTrapOID) ); - - - /* Copy SyslogMessage OID */ - if (cs.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*) cs.pszSyslogMessageOID, strlen((char*) cs.pszSyslogMessageOID) ); - - /* Copy Port */ - if ( cs.iPort == 0) /* If no Port is set we use the default Port 162 */ - pData->iPort = 162; - else - pData->iPort = cs.iPort; + /* copy config params */ + pData->szTransport = (uchar*) ((cs.pszTransport == NULL) ? NULL : strdup((char*)cs.pszTransport)); + pData->szCommunity = (uchar*) ((cs.pszCommunity == NULL) ? NULL : strdup((char*)cs.pszCommunity)); + pData->szEnterpriseOID = (uchar*) ((cs.pszEnterpriseOID == NULL) ? NULL : strdup((char*)cs.pszEnterpriseOID)); + pData->szSnmpTrapOID = (uchar*) ((cs.pszSnmpTrapOID == NULL) ? NULL : strdup((char*)cs.pszSnmpTrapOID)); + pData->szSyslogMessageOID = (uchar*) ((cs.pszSyslogMessageOID == NULL) ? NULL : strdup((char*)cs.pszSyslogMessageOID)); + pData->iPort = cs.iPort; + pData->iSpecificType = cs.iSpecificType; /* Set SNMPVersion */ if ( cs.iSNMPVersion < 0 || cs.iSNMPVersion > 1) /* Set default to 1 if out of range */ @@ -423,27 +477,16 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1) else pData->iSNMPVersion = cs.iSNMPVersion; - /* Copy SpecificType */ - if ( cs.iSpecificType == 0) /* If no iSpecificType is set, we use the default 0 */ - pData->iSpecificType = 0; - else - pData->iSpecificType = cs.iSpecificType; - /* Copy TrapType */ if ( cs.iTrapType < 0 && cs.iTrapType >= 6) /* Only allow values from 0 to 6 !*/ pData->iTrapType = SNMP_TRAP_ENTERPRISESPECIFIC; else pData->iTrapType = cs.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); @@ -506,27 +549,29 @@ ENDmodExit BEGINqueryEtryPt CODESTARTqueryEtryPt CODEqueryEtryPt_STD_OMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_CNFNAME_QUERIES +CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr + initConfVars(); CHKiRet(objUse(errmsg, CORE_COMPONENT)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &cs.pszTransport, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &cs.pszTarget, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &cs.iPort, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &cs.iSNMPVersion, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &cs.pszCommunity, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &cs.pszEnterpriseOID, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSnmpTrapOID, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSyslogMessageOID, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &cs.iSpecificType, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &cs.iTrapType, STD_LOADABLE_MODULE_ID)); - CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptransport", 0, eCmdHdlrGetWord, NULL, &cs.pszTransport, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptarget", 0, eCmdHdlrGetWord, NULL, &cs.pszTarget, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptargetport", 0, eCmdHdlrInt, NULL, &cs.iPort, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpversion", 0, eCmdHdlrInt, NULL, &cs.iSNMPVersion, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpcommunity", 0, eCmdHdlrGetWord, NULL, &cs.pszCommunity, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpenterpriseoid", 0, eCmdHdlrGetWord, NULL, &cs.pszEnterpriseOID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptrapoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSnmpTrapOID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpsyslogmessageoid", 0, eCmdHdlrGetWord, NULL, &cs.pszSyslogMessageOID, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmpspecifictype", 0, eCmdHdlrInt, NULL, &cs.iSpecificType, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionsnmptraptype", 0, eCmdHdlrInt, NULL, &cs.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/omstdout/omstdout.c b/plugins/omstdout/omstdout.c index 54dfdb04..fb95e951 100644 --- a/plugins/omstdout/omstdout.c +++ b/plugins/omstdout/omstdout.c @@ -44,6 +44,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omstdout") static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); @@ -63,8 +64,7 @@ typedef struct configSettings_s { int bUseArrayInterface; /* shall action use array instead of string template interface? */ int bEnsureLFEnding; /* shall action use array instead of string template interface? */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -196,7 +196,7 @@ BEGINmodInit() unsigned long opts; int bArrayPassingSupported; /* does core support template passing as an array? */ CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr /* check if the rsyslog core supports parameter passing code */ diff --git a/plugins/omtemplate/Makefile.am b/plugins/omtemplate/Makefile.am deleted file mode 100644 index e816c7c6..00000000 --- a/plugins/omtemplate/Makefile.am +++ /dev/null @@ -1,8 +0,0 @@ -pkglib_LTLIBRARIES = omtemplate.la - -omtemplate_la_SOURCES = omtemplate.c -omtemplate_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -omtemplate_la_LDFLAGS = -module -avoid-version -omtemplate_la_LIBADD = - -EXTRA_DIST = diff --git a/plugins/omtemplate/omtemplate.c b/plugins/omtemplate/omtemplate.c deleted file mode 100644 index 2ed0518a..00000000 --- a/plugins/omtemplate/omtemplate.c +++ /dev/null @@ -1,233 +0,0 @@ -/* omtemplate.c - * This is a template for an output module. It implements a very - * simple single-threaded output, just as thought of by the output - * plugin interface. - * - * NOTE: read comments in module-template.h for more specifics! - * - * File begun on 2009-03-16 by RGerhards - * - * Copyright 2009 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 "conf.h" -#include "syslogd-types.h" -#include "srUtils.h" -#include "template.h" -#include "module-template.h" -#include "errmsg.h" -#include "cfsysline.h" - -MODULE_TYPE_OUTPUT -MODULE_TYPE_NOKEEP - -static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); - -/* internal structures - */ -DEF_OMOD_STATIC_DATA -DEFobjCurrIf(errmsg) - -typedef struct _instanceData { - /* here you need to define all action-specific data. A record of type - * instanceData will be handed over to each instance of the action. Keep - * in mind that there may be several invocations of the same type of action - * inside rsyslog.conf, and this is what keeps them apart. Do NOT use - * static data for this! - */ - unsigned iSrvPort; /* sample: server port */ -} instanceData; - -/* config variables - * For the configuration interface, we need to keep track of some settings. This - * is done in global variables, inside a struct. It works as follows: when configuration statements - * are entered, the config file handler (or custom function) sets the global - * variable here. When the action then actually is instantiated, this handler - * copies over to instanceData whatever configuration settings (from the global - * variables) apply. The global variables are NEVER used inside an action - * instance (at least this is how it is supposed to work ;) - */ -typedef struct configSettings_s { - int iSrvPort; /* sample: server port */ -} configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ - -BEGINinitConfVars /* (re)set config variables to default values */ -CODESTARTinitConfVars - resetConfigVariables(NULL, NULL); -ENDinitConfVars - - -BEGINcreateInstance -CODESTARTcreateInstance -ENDcreateInstance - - -BEGINisCompatibleWithFeature -CODESTARTisCompatibleWithFeature - /* use this to specify if select features are supported by this - * plugin. If not, the framework will handle that. Currently, only - * RepeatedMsgReduction ("last message repeated n times") is optional. - */ - if(eFeat == sFEATURERepeatedMsgReduction) - iRet = RS_RET_OK; -ENDisCompatibleWithFeature - - -BEGINfreeInstance -CODESTARTfreeInstance - /* this is a cleanup callback. All dynamically-allocated resources - * in instance data must be cleaned up here. Prime examples are - * malloc()ed memory, file & database handles and the like. - */ -ENDfreeInstance - - -BEGINdbgPrintInstInfo -CODESTARTdbgPrintInstInfo - /* permits to spit out some debug info */ -ENDdbgPrintInstInfo - - -BEGINtryResume -CODESTARTtryResume - /* this is called when an action has been suspended and the - * rsyslog core tries to resume it. The action must then - * retry (if possible) and report RS_RET_OK if it succeeded - * or RS_RET_SUSPENDED otherwise. - * Note that no data can be written in this callback, as it is - * not present. Prime examples of what can be retried are - * reconnects to remote hosts, reconnects to database, - * opening of files and the like. - * If there is no retry-type of operation, the action may - * return RS_RET_OK, so that it will get called on its doAction - * entry point (where it receives data), retries there, and - * immediately returns RS_RET_SUSPENDED if that does not work - * out. This disables some optimizations in the core's retry logic, - * but is a valid and expected behaviour. Note that it is also OK - * for the retry entry point to return OK but the immediately following - * doAction call to fail. In real life, for example, a buggy com line - * may cause such behaviour. - * Note that there is no guarantee that the core will very quickly - * call doAction after the retry succeeded. Today, it does, but that may - * not always be the case. - */ -ENDtryResume - -BEGINdoAction -CODESTARTdoAction - /* this is where you receive the message and need to carry out the - * action. Data is provided in ppString[i] where 0 <= i <= num of strings - * requested. - * Return RS_RET_OK if all goes well, RS_RET_SUSPENDED if the action can - * currently not complete, or an error code or RS_RET_DISABLED. The later - * two should only be returned if there is no hope that the action can be - * restored unless an rsyslog restart (prime example is an invalid config). - * Error code or RS_RET_DISABLED permanently disables the action, up to - * the next restart. - */ -ENDdoAction - - -BEGINparseSelectorAct -CODESTARTparseSelectorAct -CODE_STD_STRING_REQUESTparseSelectorAct(1) - /* first check if this config line is actually for us - * This is a clumpsy interface. We receive the action-part of the selector line - * and need to look at the first characters. If they match our signature - * ":omtemplate:", then we need to instantiate an action. It is recommended that - * newer actions just watch for the template and all other parameters are passed in - * via $-config-lines, this will hopefully be compatbile with future config syntaxes. - * If we do not detect our signature, we must return with RS_RET_CONFLINE_UNPROCESSED - * and NOT do anything else. - */ - if(strncmp((char*) p, ":omtemplate:", sizeof(":omtemplate:") - 1)) { - ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED); - } - - /* ok, if we reach this point, we have something for us */ - p += sizeof(":omtemplate:") - 1; /* eat indicator sequence (-1 because of '\0'!) */ - CHKiRet(createInstance(&pData)); - - /* check if a non-standard template is to be applied */ - if(*(p-1) == ';') - --p; - /* if we have, call rsyslog runtime to get us template. Note that StdFmt below is - * the standard name. Currently, we may need to patch tools/syslogd.c if we need - * to add a new standard template. - */ - CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_RQD_TPL_OPT_SQL, (uchar*) " StdFmt")); - - /* if we reach this point, all went well, and we can copy over to instanceData - * those configuration elements that we need. - */ - pData->iSrvPort = (unsigned) cs.iSrvPort; /* set configured port */ - -CODE_STD_FINALIZERparseSelectorAct -ENDparseSelectorAct - - -BEGINmodExit -CODESTARTmodExit -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; - cs.iSrvPort = 0; /* zero is the default port */ - RETiRet; -} - - -BEGINmodInit() -CODESTARTmodInit -SCOPINGmodInit - *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ -CODEmodInit_QueryRegCFSLineHdlr - CHKiRet(objUse(errmsg, CORE_COMPONENT)); - /* register our config handlers */ - /* confguration parameters MUST always be specified in lower case! */ - CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtemplteserverport", 0, eCmdHdlrInt, NULL, &cs.iSrvPort, STD_LOADABLE_MODULE_ID)); - /* "resetconfigvariables" should be provided. Notat that it is a chained directive */ - CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); -ENDmodInit - -/* vi:set ai: - */ diff --git a/plugins/omtesting/omtesting.c b/plugins/omtesting/omtesting.c index 139ac8f9..ff290c94 100644 --- a/plugins/omtesting/omtesting.c +++ b/plugins/omtesting/omtesting.c @@ -57,6 +57,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omtesting") /* internal structures */ @@ -78,8 +79,7 @@ typedef struct _instanceData { typedef struct configSettings_s { int bEchoStdout; /* echo non-failed messages to stdout */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -318,7 +318,7 @@ ENDqueryEtryPt BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtestingechostdout", 0, eCmdHdlrBinary, NULL, diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c index 252f3647..43b36551 100644 --- a/plugins/omudpspoof/omudpspoof.c +++ b/plugins/omudpspoof/omudpspoof.c @@ -83,6 +83,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omudpspoof") /* internal structures */ @@ -114,8 +115,7 @@ typedef struct configSettings_s { int iSourcePortStart; int iSourcePortEnd; } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -420,7 +420,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(2) pData->port = NULL; else CHKmalloc(pData->port = ustrdup(cs.pszTargetPort)); - CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(cs.pszSourceNameTemplate), OMSR_NO_RQD_TPL_OPTS)); + CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(sourceTpl), OMSR_NO_RQD_TPL_OPTS)); pData->compressionLevel = cs.iCompressionLevel; pData->sourcePort = pData->sourcePortStart = cs.iSourcePortStart; pData->sourcePortEnd = cs.iSourcePortEnd; @@ -483,7 +483,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit -SCOPINGmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(glbl, CORE_COMPONENT)); diff --git a/plugins/omuxsock/omuxsock.c b/plugins/omuxsock/omuxsock.c index 05b0532d..cf27c93c 100644 --- a/plugins/omuxsock/omuxsock.c +++ b/plugins/omuxsock/omuxsock.c @@ -46,6 +46,7 @@ MODULE_TYPE_OUTPUT MODULE_TYPE_NOKEEP +MODULE_CNFNAME("omuxsock") /* internal structures */ @@ -68,8 +69,7 @@ typedef struct configSettings_s { uchar *tplName; /* name of the default template to use */ uchar *sockName; /* name of the default template to use */ } configSettings_t; - -SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */ +static configSettings_t cs; BEGINinitConfVars /* (re)set config variables to default values */ CODESTARTinitConfVars @@ -306,6 +306,7 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a BEGINmodInit() CODESTARTmodInit +INITLegCnfVars *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ CODEmodInit_QueryRegCFSLineHdlr CHKiRet(objUse(glbl, CORE_COMPONENT)); diff --git a/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c index fe3e85fa..76198e9c 100644 --- a/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c +++ b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c @@ -42,6 +42,7 @@ MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP +MODULE_CNFNAME("pmaixforwardedfrom") PARSER_NAME("rsyslog.aixforwardedfrom") /* internal structures diff --git a/plugins/pmcisconames/pmcisconames.c b/plugins/pmcisconames/pmcisconames.c index 61688cbf..d8235752 100644 --- a/plugins/pmcisconames/pmcisconames.c +++ b/plugins/pmcisconames/pmcisconames.c @@ -42,6 +42,7 @@ MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP +MODULE_CNFNAME("pmcisconames") PARSER_NAME("rsyslog.cisconames") /* internal structures diff --git a/plugins/pmlastmsg/pmlastmsg.c b/plugins/pmlastmsg/pmlastmsg.c index 259c5d41..a290c446 100644 --- a/plugins/pmlastmsg/pmlastmsg.c +++ b/plugins/pmlastmsg/pmlastmsg.c @@ -48,6 +48,7 @@ MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP +MODULE_CNFNAME("pmlastmsg") PARSER_NAME("rsyslog.lastline") /* internal structures diff --git a/plugins/pmrfc3164sd/pmrfc3164sd.c b/plugins/pmrfc3164sd/pmrfc3164sd.c index 53204ece..de5805bc 100644 --- a/plugins/pmrfc3164sd/pmrfc3164sd.c +++ b/plugins/pmrfc3164sd/pmrfc3164sd.c @@ -46,6 +46,7 @@ MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP +MODULE_CNFNAME("pmrfc3164sd") PARSER_NAME("contrib.rfc3164sd") /* internal structures diff --git a/plugins/pmsnare/pmsnare.c b/plugins/pmsnare/pmsnare.c index f3658d11..aca0271f 100644 --- a/plugins/pmsnare/pmsnare.c +++ b/plugins/pmsnare/pmsnare.c @@ -59,6 +59,7 @@ MODULE_TYPE_PARSER MODULE_TYPE_NOKEEP +MODULE_CNFNAME("pmsnare") PARSER_NAME("rsyslog.snare") /* internal structures diff --git a/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c index baad667e..acf1bfad 100644 --- a/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c +++ b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c @@ -52,6 +52,7 @@ MODULE_TYPE_STRGEN MODULE_TYPE_NOKEEP +MODULE_CNFNAME("sm_cust_bindcdr") STRGEN_NAME("Custom_BindCDR,sql") /* internal structures |