summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/im3195/im3195.c1
-rw-r--r--plugins/imdiag/imdiag.c46
-rw-r--r--plugins/imfile/imfile.c144
-rw-r--r--plugins/imgssapi/imgssapi.c34
-rw-r--r--plugins/imklog/bsd.c3
-rw-r--r--plugins/imklog/imklog.c2
-rw-r--r--plugins/imklog/ksym.c3
-rw-r--r--plugins/imklog/ksym_mod.c3
-rw-r--r--plugins/imklog/solaris.c184
-rw-r--r--plugins/imklog/solaris_cddl.c293
-rw-r--r--plugins/imklog/solaris_cddl.h2
-rw-r--r--plugins/immark/immark.c29
-rw-r--r--plugins/impstats/Makefile.am6
-rw-r--r--plugins/impstats/impstats.c231
-rw-r--r--plugins/imptcp/Makefile.am6
-rw-r--r--plugins/imptcp/imptcp.c1196
-rw-r--r--plugins/imrelp/imrelp.c1
-rw-r--r--plugins/imsolaris/Makefile.am6
-rw-r--r--plugins/imsolaris/imsolaris.c400
-rw-r--r--plugins/imsolaris/imsolaris.h2
-rw-r--r--plugins/imsolaris/sun_cddl.c419
-rw-r--r--plugins/imsolaris/sun_cddl.h7
-rw-r--r--plugins/imtcp/imtcp.c18
-rw-r--r--plugins/imtemplate/imtemplate.c42
-rw-r--r--plugins/imudp/Makefile.am2
-rw-r--r--plugins/imudp/imudp.c395
-rw-r--r--plugins/imuxsock/Makefile.am4
-rw-r--r--plugins/imuxsock/imuxsock.c779
-rw-r--r--plugins/mmsnmptrapd/Makefile.am8
-rw-r--r--plugins/mmsnmptrapd/mmsnmptrapd.c430
-rw-r--r--plugins/omdbalerting/Makefile.am8
-rw-r--r--plugins/omdbalerting/omdbalerting.c145
-rw-r--r--plugins/omgssapi/omgssapi.c9
-rw-r--r--plugins/omhdfs/Makefile.am6
-rw-r--r--plugins/omhdfs/omhdfs.c476
-rw-r--r--plugins/omlibdbi/omlibdbi.c11
-rw-r--r--plugins/ommail/ommail.c7
-rw-r--r--plugins/ommysql/ommysql.c40
-rw-r--r--plugins/omoracle/omoracle.c84
-rw-r--r--plugins/ompgsql/ompgsql.c47
-rw-r--r--plugins/omprog/omprog.c4
-rw-r--r--plugins/omrelp/omrelp.c4
-rw-r--r--plugins/omruleset/Makefile.am8
-rw-r--r--plugins/omruleset/omruleset.c232
-rw-r--r--plugins/omsnmp/omsnmp.c3
-rw-r--r--plugins/omstdout/omstdout.c3
-rw-r--r--plugins/omtemplate/omtemplate.c1
-rw-r--r--plugins/omtesting/omtesting.c160
-rw-r--r--plugins/omudpspoof/Makefile.am8
-rw-r--r--plugins/omudpspoof/omudpspoof.c503
-rw-r--r--plugins/omuxsock/Makefile.am8
-rw-r--r--plugins/omuxsock/omuxsock.c316
-rw-r--r--plugins/pmaixforwardedfrom/Makefile.am8
-rw-r--r--plugins/pmaixforwardedfrom/pmaixforwardedfrom.c168
-rw-r--r--plugins/pmcisconames/Makefile.am8
-rw-r--r--plugins/pmcisconames/pmcisconames.c178
-rw-r--r--plugins/pmlastmsg/Makefile.am8
-rw-r--r--plugins/pmlastmsg/pmlastmsg.c176
-rw-r--r--plugins/pmrfc3164sd/Makefile.am8
-rw-r--r--plugins/pmrfc3164sd/pmrfc3164sd.c344
-rw-r--r--plugins/pmsnare/Makefile.am8
-rw-r--r--plugins/pmsnare/pmsnare.c239
-rw-r--r--plugins/sm_cust_bindcdr/Makefile.am6
-rw-r--r--plugins/sm_cust_bindcdr/sm_cust_bindcdr.c390
64 files changed, 7996 insertions, 324 deletions
diff --git a/plugins/im3195/im3195.c b/plugins/im3195/im3195.c
index 106da2c8..156524c9 100644
--- a/plugins/im3195/im3195.c
+++ b/plugins/im3195/im3195.c
@@ -51,6 +51,7 @@
#include "errmsg.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* Module static data */
DEF_IMOD_STATIC_DATA
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index bf972191..912c7bbf 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -57,6 +57,7 @@
#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* static data */
DEF_IMOD_STATIC_DATA
@@ -204,7 +205,7 @@ doInjectMsg(int iNum)
DEFiRet;
snprintf((char*)szMsg, sizeof(szMsg)/sizeof(uchar),
- "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:\n", iNum);
+ "<167>Mar 1 01:00:00 172.20.245.8 tag msgnum:%8.8d:", iNum);
datetime.getCurrTime(&stTime, &ttGenTime);
/* we now create our own message object and submit it to the queue */
@@ -213,7 +214,6 @@ doInjectMsg(int iNum)
MsgSetInputName(pMsg, pInputName);
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
- pMsg->bParseHOSTNAME = 1;
MsgSetRcvFrom(pMsg, pRcvDummy);
CHKiRet(MsgSetRcvFromIP(pMsg, pRcvIPDummy));
CHKiRet(submitMsg(pMsg));
@@ -246,7 +246,8 @@ injectMsg(uchar *pszCmd, tcps_sess_t *pSess)
doInjectMsg(i + iFrom);
}
- CHKiRet(sendResponse(pSess, "messages injected\n"));
+ CHKiRet(sendResponse(pSess, "%d messages injected\n", nMsgs));
+ DBGPRINTF("imdiag: %d messages injected\n", nMsgs);
finalize_it:
RETiRet;
@@ -254,20 +255,37 @@ finalize_it:
/* This function waits until the main queue is drained (size = 0)
+ * To make sure it really is drained, we check three times. Otherwise we
+ * may just see races.
*/
static rsRetVal
waitMainQEmpty(tcps_sess_t *pSess)
{
int iMsgQueueSize;
+ int iPrint = 0;
DEFiRet;
CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
- while(iMsgQueueSize > 0) {
- srSleep(0,2); /* wait a little bit */
+ while(1) {
+ if(iMsgQueueSize == 0) {
+ /* verify that queue is still empty (else it could just be a race!) */
+ srSleep(0,250000);/* wait a little bit */
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ if(iMsgQueueSize == 0) {
+ srSleep(0,500000);/* wait a little bit */
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ }
+ }
+ if(iMsgQueueSize == 0)
+ break;
+ if(iPrint++ % 500 == 0)
+ dbgprintf("imdiag sleeping, wait mainq drain, curr size %d\n", iMsgQueueSize);
+ srSleep(0,200000);/* wait a little bit */
CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
}
CHKiRet(sendResponse(pSess, "mainqueue empty\n"));
+ DBGPRINTF("imdiag: mainqueue empty\n");
finalize_it:
RETiRet;
@@ -281,6 +299,7 @@ OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg)
{
int iMsgQueueSize;
uchar *pszMsg;
+ uchar *pToFree = NULL;
uchar cmdBuf[1024];
DEFiRet;
@@ -291,15 +310,18 @@ OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg)
* WITHOUT a termination \0 char. So we need to convert it to one
* before proceeding.
*/
- CHKmalloc(pszMsg = malloc(sizeof(uchar) * (iLenMsg + 1)));
+ CHKmalloc(pszMsg = MALLOC(sizeof(uchar) * (iLenMsg + 1)));
+ pToFree = pszMsg;
memcpy(pszMsg, pRcv, iLenMsg);
pszMsg[iLenMsg] = '\0';
getFirstWord(&pszMsg, cmdBuf, sizeof(cmdBuf)/sizeof(uchar), TO_LOWERCASE);
+ dbgprintf("imdiag received command '%s'\n", cmdBuf);
if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("getmainmsgqueuesize"))) {
CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
CHKiRet(sendResponse(pSess, "%d\n", iMsgQueueSize));
+ DBGPRINTF("imdiag: %d messages in main queue\n", iMsgQueueSize);
} else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("waitmainqueueempty"))) {
CHKiRet(waitMainQEmpty(pSess));
} else if(!ustrcmp(cmdBuf, UCHAR_CONSTANT("injectmsg"))) {
@@ -310,6 +332,8 @@ OnMsgReceived(tcps_sess_t *pSess, uchar *pRcv, int iLenMsg)
}
finalize_it:
+ if(pToFree != NULL)
+ free(pToFree);
RETiRet;
}
@@ -417,6 +441,9 @@ CODESTARTmodExit
net.DestructPermittedPeers(&pPermPeersRoot);
}
+ /* free some globals to keep valgrind happy */
+ free(pszInputName);
+
/* release objects we used */
objRelease(net, LM_NET_FILENAME);
objRelease(netstrm, LM_NETSTRMS_FILENAME);
@@ -443,10 +470,17 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
}
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c
index 3981f9f7..cac3a55d 100644
--- a/plugins/imfile/imfile.c
+++ b/plugins/imfile/imfile.c
@@ -5,7 +5,7 @@
*
* Work originally begun on 2008-02-01 by Rainer Gerhards
*
- * Copyright 2008 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2008,2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -47,8 +47,11 @@
#include "datetime.h"
#include "unicode-helper.h"
#include "prop.h"
+#include "stringbuf.h"
+#include "ruleset.h"
MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
+MODULE_TYPE_NOKEEP
/* defines */
@@ -59,6 +62,7 @@ DEFobjCurrIf(glbl)
DEFobjCurrIf(datetime)
DEFobjCurrIf(strm)
DEFobjCurrIf(prop)
+DEFobjCurrIf(ruleset)
typedef struct fileInfo_s {
uchar *pszFileName;
@@ -67,17 +71,27 @@ typedef struct fileInfo_s {
uchar *pszStateFile; /* file in which state between runs is to be stored */
int iFacility;
int iSeverity;
+ 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) */
} fileInfo_t;
+/* forward definitions */
+static rsRetVal persistStrmState(fileInfo_t *pInfo);
+
/* config variables */
static uchar *pszFileName = NULL;
static uchar *pszFileTag = NULL;
static uchar *pszStateFile = NULL;
static int iPollInterval = 10; /* number of seconds to sleep when there was no file activity */
+static int iPersistStateInterval = 0; /* how often if state file to be persisted? (default 0->never) */
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 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 */
#define MAX_INPUT_FILES 100
@@ -107,7 +121,7 @@ static rsRetVal enqLine(fileInfo_t *pInfo, cstr_t *cstrLine)
MsgSetTAG(pMsg, pInfo->pszTag, pInfo->lenTag);
pMsg->iFacility = LOG_FAC(pInfo->iFacility);
pMsg->iSeverity = LOG_PRI(pInfo->iSeverity);
- pMsg->bParseHOSTNAME = 0;
+ MsgSetRuleset(pMsg, pInfo->pRuleset);
CHKiRet(submitMsg(pMsg));
finalize_it:
RETiRet;
@@ -154,10 +168,9 @@ openFile(fileInfo_t *pThis)
CHKiRet(strm.SeekCurrOffs(pThis->pStrm));
- /* OK, we could successfully read the file, so we now can request that it be deleted.
- * If we need it again, it will be written on the next shutdown.
+ /* note: we do not delete the state file, so that the last position remains
+ * known even in the case that rsyslogd aborts for some reason (like powerfail)
*/
- psSF->bDeleteOnClose = 1;
finalize_it:
if(psSF != NULL)
@@ -206,15 +219,19 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
}
/* loop below will be exited when strmReadLine() returns EOF */
- while(1) {
- CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr));
+ while(glbl.GetGlobalInputTermState() == 0) {
+ CHKiRet(strm.ReadLine(pThis->pStrm, &pCStr, pThis->readMode));
*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!) */
+ if(pThis->iPersistStateInterval > 0 && pThis->nRecords++ >= pThis->iPersistStateInterval) {
+ persistStrmState(pThis);
+ pThis->nRecords = 0;
+ }
}
finalize_it:
- /*EMPTY - just to keep the compiler happy, do NOT remove*/;
+ ; /*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
* "finalize_it: }", that is a label without a statement. The C standard does
@@ -244,27 +261,12 @@ finalize_it:
* IMPORTANT: the calling interface of this function can NOT be modified. It actually is
* called by pthreads. The provided argument is currently not being used.
*/
-/* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
static void
inputModuleCleanup(void __attribute__((unused)) *arg)
{
BEGINfunc
-/* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
-
-
- /* so far not needed */
-
-
-
-/* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
ENDfunc
}
-/* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
/* This function is called by the framework to gather the input. The module stays
@@ -292,30 +294,25 @@ BEGINrunInput
int i;
int bHadFileData; /* were there at least one file with data during this run? */
CODESTARTrunInput
- /* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
pthread_cleanup_push(inputModuleCleanup, NULL);
- while(1) { /* endless loop - do NOT break; out of it! */
- /* END no-touch zone *
- * ------------------------------------------------------------------------------------------ */
-
- do {
- bHadFileData = 0;
- for(i = 0 ; i < iFilPtr ; ++i) {
- pollFile(&files[i], &bHadFileData);
- }
- } while(iFilPtr > 1 && bHadFileData == 1); /* waring: do...while()! */
-
- /* Note: the additional 10ns wait is vitally important. It guards rsyslog against totally
- * hogging the CPU if the users selects a polling interval of 0 seconds. It doesn't hurt any
- * other valid scenario. So do not remove. -- rgerhards, 2008-02-14
- */
- srSleep(iPollInterval, 10);
-
- /* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
+ while(glbl.GetGlobalInputTermState() == 0) {
+ do {
+ bHadFileData = 0;
+ for(i = 0 ; i < iFilPtr ; ++i) {
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
+ pollFile(&files[i], &bHadFileData);
+ }
+ } while(iFilPtr > 1 && bHadFileData == 1 && glbl.GetGlobalInputTermState() == 0); /* warning: do...while()! */
+
+ /* Note: the additional 10ns wait is vitally important. It guards rsyslog against totally
+ * hogging the CPU if the users selects a polling interval of 0 seconds. It doesn't hurt any
+ * other valid scenario. So do not remove. -- rgerhards, 2008-02-14
+ */
+ if(glbl.GetGlobalInputTermState() == 0)
+ srSleep(iPollInterval, 10);
}
- /*NOTREACHED*/
+ DBGPRINTF("imfile: terminating upon request of rsyslog core\n");
pthread_cleanup_pop(0); /* just for completeness, but never called... */
RETiRet; /* use it to make sure the housekeeping is done! */
@@ -334,6 +331,11 @@ ENDrunInput
*/
BEGINwillRun
CODESTARTwillRun
+ /* free config variables we do no longer needed */
+ free(pszFileName);
+ free(pszFileTag);
+ free(pszStateFile);
+
if(iFilPtr == 0) {
errmsg.LogError(0, RS_RET_NO_RUN, "No files configured to be monitored");
ABORT_FINALIZE(RS_RET_NO_RUN);
@@ -365,7 +367,7 @@ persistStrmState(fileInfo_t *pInfo)
/* TODO: create a function persistObj in obj.c? */
CHKiRet(strm.Construct(&psSF));
- lenDir = strlen((char*)glbl.GetWorkDir());
+ lenDir = ustrlen(glbl.GetWorkDir());
if(lenDir > 0)
CHKiRet(strm.SetDir(psSF, glbl.GetWorkDir(), lenDir));
CHKiRet(strm.SettOperationsMode(psSF, STREAMMODE_WRITE_TRUNC));
@@ -401,6 +403,9 @@ CODESTARTafterRun
persistStrmState(&files[i]);
strm.Destruct(&(files[i].pStrm));
}
+ free(files[i].pszFileName);
+ free(files[i].pszTag);
+ free(files[i].pszStateFile);
}
if(pInputName != NULL)
@@ -408,6 +413,13 @@ CODESTARTafterRun
ENDafterRun
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
/* 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.
@@ -420,12 +432,14 @@ CODESTARTmodExit
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -459,6 +473,8 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
iPollInterval = 10;
iFacility = 128; /* local0 */
iSeverity = 5; /* notice, as of rfc 3164 */
+ readMode = 0;
+ pBindRuleset = NULL;
RETiRet;
}
@@ -499,6 +515,11 @@ static rsRetVal addMonitor(void __attribute__((unused)) *pVal, uchar *pNewVal)
pThis->iSeverity = iSeverity;
pThis->iFacility = iFacility;
+ pThis->iPersistStateInterval = iPersistStateInterval;
+ pThis->nRecords = 0;
+ pThis->readMode = readMode;
+ pThis->pRuleset = pBindRuleset;
+ iPersistStateInterval = 0;
} else {
errmsg.LogError(0, RS_RET_OUT_OF_DESRIPTORS, "Too many file monitors configured - ignoring this one");
ABORT_FINALIZE(RS_RET_OUT_OF_DESRIPTORS);
@@ -513,6 +534,29 @@ finalize_it:
RETiRet;
}
+
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal
+setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ 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("imfile current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ 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
@@ -530,8 +574,10 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(strm, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
+ DBGPRINTF("imfile: version %s initializing\n", VERSION);
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilename", 0, eCmdHdlrGetWord,
NULL, &pszFileName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfiletag", 0, eCmdHdlrGetWord,
@@ -544,6 +590,12 @@ CODEmodInit_QueryRegCFSLineHdlr
NULL, &iFacility, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilepollinterval", 0, eCmdHdlrInt,
NULL, &iPollInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilereadmode", 0, eCmdHdlrInt,
+ NULL, &readMode, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilepersiststateinterval", 0, eCmdHdlrInt,
+ NULL, &iPersistStateInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputfilebindruleset", 0, eCmdHdlrGetWord,
+ setRuleset, NULL, STD_LOADABLE_MODULE_ID));
/* that command ads a new file! */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputrunfilemonitor", 0, eCmdHdlrGetWord,
addMonitor, NULL, STD_LOADABLE_MODULE_ID));
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index d8791880..446795d6 100644
--- a/plugins/imgssapi/imgssapi.c
+++ b/plugins/imgssapi/imgssapi.c
@@ -48,6 +48,7 @@
#include "dirty.h"
#include "cfsysline.h"
#include "module-template.h"
+#include "unicode-helper.h"
#include "net.h"
#include "srUtils.h"
#include "gss-misc.h"
@@ -56,9 +57,12 @@
#include "errmsg.h"
#include "netstrm.h"
#include "glbl.h"
+#include "debug.h"
+#include "unlimited_select.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* defines */
#define ALLOWEDMETHOD_GSS 2
@@ -176,10 +180,10 @@ isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void *pUsrSrv, void*p
pGSess = (gss_sess_t*) pUsrSess;
if((pGSrv->allowedMethods & ALLOWEDMETHOD_TCP) &&
- net.isAllowedSender((uchar*)"TCP", addr, (char*)fromHostFQDN))
+ net.isAllowedSender2((uchar*)"TCP", addr, (char*)fromHostFQDN, 1))
allowedMethods |= ALLOWEDMETHOD_TCP;
if((pGSrv->allowedMethods & ALLOWEDMETHOD_GSS) &&
- net.isAllowedSender((uchar*)"GSS", addr, (char*)fromHostFQDN))
+ net.isAllowedSender2((uchar*)"GSS", addr, (char*)fromHostFQDN, 1))
allowedMethods |= ALLOWEDMETHOD_GSS;
if(allowedMethods && pGSess != NULL)
pGSess->allowedMethods = allowedMethods;
@@ -330,6 +334,7 @@ addGSSListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
CHKiRet(tcpsrv.SetCBOnSessAccept(pOurTcpsrv, onSessAccept));
CHKiRet(tcpsrv.SetCBOnRegularClose(pOurTcpsrv, onRegularClose));
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
+ CHKiRet(tcpsrv.SetInputName(pOurTcpsrv, UCHAR_CONSTANT("imgssapi")));
tcpsrv.configureTCPListen(pOurTcpsrv, pNewVal);
CHKiRet(tcpsrv.ConstructFinalize(pOurTcpsrv));
}
@@ -407,22 +412,27 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess)
*/
char *buf;
int ret = 0;
- CHKmalloc(buf = (char*) malloc(sizeof(char) * (glbl.GetMaxLine() + 1)));
+ CHKmalloc(buf = (char*) MALLOC(sizeof(char) * (glbl.GetMaxLine() + 1)));
dbgprintf("GSS-API Trying to accept TCP session %p\n", pSess);
CHKiRet(netstrm.GetSock(pSess->pStrm, &fdSess)); // TODO: method access!
if (allowedMethods & ALLOWEDMETHOD_TCP) {
int len;
- fd_set fds;
struct timeval tv;
+#ifdef USE_UNLIMITED_SELECT
+ fd_set *pFds = malloc(glbl.GetFdSetSize());
+#else
+ fd_set fds;
+ fd_set *pFds = &fds;
+#endif
do {
- FD_ZERO(&fds);
- FD_SET(fdSess, &fds);
+ FD_ZERO(pFds);
+ FD_SET(fdSess, pFds);
tv.tv_sec = 1;
tv.tv_usec = 0;
- ret = select(fdSess + 1, &fds, NULL, NULL, &tv);
+ ret = select(fdSess + 1, pFds, NULL, NULL, &tv);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
errmsg.LogError(0, RS_RET_ERR, "TCP session %p will be closed, error ignored\n", pSess);
@@ -475,6 +485,8 @@ OnSessAcceptGSS(tcpsrv_t *pThis, tcps_sess_t *pSess)
pGSess->allowedMethods = ALLOWEDMETHOD_TCP;
ABORT_FINALIZE(RS_RET_OK); // TODO: define good error codes
}
+
+ freeFdSet(pFds);
}
context = &pGSess->gss_context;
@@ -674,9 +686,17 @@ CODESTARTafterRun
ENDafterRun
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
diff --git a/plugins/imklog/bsd.c b/plugins/imklog/bsd.c
index cecf21c4..0a4c7cd4 100644
--- a/plugins/imklog/bsd.c
+++ b/plugins/imklog/bsd.c
@@ -75,6 +75,7 @@
#include "rsyslog.h"
#include "imklog.h"
+#include "debug.h"
/* globals */
static int fklog = -1; /* /dev/klog */
@@ -132,7 +133,7 @@ readklog(void)
if((size_t) iMaxLine < sizeof(bufRcv) - 1) {
pRcv = bufRcv;
} else {
- if((pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1))) == NULL)
+ if((pRcv = (uchar*) MALLOC(sizeof(uchar) * (iMaxLine + 1))) == NULL)
iMaxLine = sizeof(bufRcv) - 1; /* better this than noting */
}
diff --git a/plugins/imklog/imklog.c b/plugins/imklog/imklog.c
index 7994c5eb..69c8cd1a 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -58,6 +58,7 @@
#include "unicode-helper.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* Module static data */
DEF_IMOD_STATIC_DATA
@@ -111,7 +112,6 @@ enqMsg(uchar *msg, uchar* pszTag, int iFacility, int iSeverity)
MsgSetTAG(pMsg, pszTag, ustrlen(pszTag));
pMsg->iFacility = LOG_FAC(iFacility);
pMsg->iSeverity = LOG_PRI(iSeverity);
- pMsg->bParseHOSTNAME = 0;
CHKiRet(submitMsg(pMsg));
finalize_it:
diff --git a/plugins/imklog/ksym.c b/plugins/imklog/ksym.c
index ca708ba6..ebaec011 100644
--- a/plugins/imklog/ksym.c
+++ b/plugins/imklog/ksym.c
@@ -122,6 +122,7 @@
#include "imklog.h"
#include "ksyms.h"
#include "module.h"
+#include "debug.h"
int num_syms = 0;
@@ -523,7 +524,7 @@ static int AddSymbol(unsigned long address, char *symbol)
return(0);
/* Then the space for the symbol. */
- sym_array[num_syms].name = (char *) malloc(strlen(symbol)*sizeof(char) + 1);
+ sym_array[num_syms].name = (char *) MALLOC(strlen(symbol)*sizeof(char) + 1);
if ( sym_array[num_syms].name == NULL )
return(0);
diff --git a/plugins/imklog/ksym_mod.c b/plugins/imklog/ksym_mod.c
index be5fdee9..82978892 100644
--- a/plugins/imklog/ksym_mod.c
+++ b/plugins/imklog/ksym_mod.c
@@ -106,6 +106,7 @@
#include "rsyslog.h"
#include "imklog.h"
#include "ksyms.h"
+#include "debug.h"
#define KSYMS "/proc/kallsyms"
@@ -289,7 +290,7 @@ struct Module *AddModule(module)
struct Module *mp;
if ( num_modules == 0 ) {
- sym_array_modules = (struct Module *)malloc(sizeof(struct Module));
+ sym_array_modules = (struct Module *)MALLOC(sizeof(struct Module));
if ( sym_array_modules == NULL )
{
diff --git a/plugins/imklog/solaris.c b/plugins/imklog/solaris.c
new file mode 100644
index 00000000..8a6d5af1
--- /dev/null
+++ b/plugins/imklog/solaris.c
@@ -0,0 +1,184 @@
+/* klog driver for solaris
+ *
+ * This contains OS-specific functionality to read the
+ * kernel log. For a general overview, see head comment in
+ * imklog.c.
+ *
+ * This file relies on Sun code in solaris_cddl.c. We have split
+ * it from Sun's code to keep the copyright issue as simple as possible.
+ *
+ * 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.
+ *
+ * If that may be required, an exception is granted to permit linking
+ * this code to the code in solaris_cddl.c that is under the cddl license.
+ *
+ * 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/socket.h>
+
+
+
+#include "rsyslog.h"
+#include "imklog.h"
+#include "srUtils.h"
+#include "unicode-helper.h"
+#include "solaris_cddl.h"
+
+/* globals */
+static int fklog; // TODO: remove
+#ifndef _PATH_KLOG
+# define _PATH_KLOG "/dev/log"
+#endif
+
+
+static uchar *GetPath(void)
+{
+ return pszPath ? pszPath : UCHAR_CONSTANT(_PATH_KLOG);
+}
+
+/* open the kernel log - will be called inside the willRun() imklog
+ * entry point. -- rgerhards, 2008-04-09
+ */
+rsRetVal
+klogWillRun(void)
+{
+ DEFiRet;
+
+ fklog = sun_openklog((char*) GetPath(), O_RDONLY);
+ if (fklog < 0) {
+ char errStr[1024];
+ int err = errno;
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ DBGPRINTF("error %s opening log socket: %s\n",
+ errStr, GetPath());
+ iRet = RS_RET_ERR; // TODO: better error code
+ }
+
+ RETiRet;
+}
+
+
+#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
+ */
+rsRetVal klogAfterRun(void)
+{
+ DEFiRet;
+ if(fklog != -1)
+ close(fklog);
+ RETiRet;
+}
+
+
+
+/* to be called in the module's WillRun entry point, this is the main
+ * "message pull" mechanism.
+ * rgerhards, 2008-04-09
+ */
+rsRetVal klogLogKMsg(void)
+{
+ DEFiRet;
+ sun_sys_poll();
+ RETiRet;
+}
+
+
+/* provide the (system-specific) default facility for internal messages
+ * rgerhards, 2008-04-14
+ */
+int
+klogFacilIntMsg(void)
+{
+ return LOG_SYSLOG;
+}
+
diff --git a/plugins/imklog/solaris_cddl.c b/plugins/imklog/solaris_cddl.c
new file mode 100644
index 00000000..7e86c68c
--- /dev/null
+++ b/plugins/imklog/solaris_cddl.c
@@ -0,0 +1,293 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/* Portions Copyright 2010 by Rainer Gerhards and Adiscon
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+#include "config.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pthread.h>
+#include <sys/poll.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <assert.h>
+#include <sys/strlog.h>
+
+#include "rsyslog.h"
+#include "imklog.h"
+
+/* TODO: this define should be changed over time to the more generic
+ * system-provided (configurable) upper limit. However, it is quite
+ * unexpected that Solaris-emitted messages are so long, so it seems
+ * acceptable to set a fixed (relatively high) limit for the time
+ * being -- and gain some experience with it. -- rgerhars, 2010-04-12
+ */
+#define MAXLINE 4096
+
+static struct pollfd Pfd; /* Pollfd for local the log device */
+
+
+/* findnl_bkwd:
+ * Scans each character in buf until it finds the last newline in buf,
+ * or the scanned character becomes the last COMPLETE character in buf.
+ * Returns the number of scanned bytes.
+ *
+ * buf - pointer to a buffer containing the message string
+ * len - the length of the buffer
+ */
+size_t
+findnl_bkwd(const char *buf, const size_t len)
+{
+ const char *p;
+ size_t mb_cur_max;
+
+ if (len == 0) {
+ return (0);
+ }
+
+ mb_cur_max = MB_CUR_MAX;
+
+ if (mb_cur_max == 1) {
+ /* single-byte locale */
+ for (p = buf + len - 1; p != buf; p--) {
+ if (*p == '\n') {
+ return ((size_t)(p - buf));
+ }
+ }
+ return ((size_t)len);
+ } else {
+ /* multi-byte locale */
+ int mlen;
+ const char *nl;
+ size_t rem;
+
+ p = buf;
+ nl = NULL;
+ for (rem = len; rem >= mb_cur_max; ) {
+ mlen = mblen(p, mb_cur_max);
+ if (mlen == -1) {
+ /*
+ * Invalid character found.
+ */
+ dbgprintf("klog:findnl_bkwd: Invalid MB sequence\n");
+ /*
+ * handle as a single byte character.
+ */
+ p++;
+ rem--;
+ } else {
+ /*
+ * It's guaranteed that *p points to
+ * the 1st byte of a multibyte character.
+ */
+ if (*p == '\n') {
+ nl = p;
+ }
+ p += mlen;
+ rem -= mlen;
+ }
+ }
+ if (nl) {
+ return ((size_t)(nl - buf));
+ }
+ /*
+ * no newline nor null byte found.
+ * Also it's guaranteed that *p points to
+ * the 1st byte of a (multibyte) character
+ * at this point.
+ */
+ return (len - rem);
+ }
+}
+//___ end
+
+
+/* Attempts to open the local log device
+ * and return a file descriptor.
+ */
+int
+sun_openklog(char *name, int mode)
+{
+ int fd;
+ struct strioctl str;
+
+ if ((fd = open(name, mode)) < 0) {
+ dbgprintf("klog:openklog: cannot open %s (%d)\n",
+ name, errno);
+ return (-1);
+ }
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if (ioctl(fd, I_STR, &str) < 0) {
+ dbgprintf("klog:openklog: cannot register to log "
+ "console messages (%d)\n", errno);
+ return (-1);
+ }
+ Pfd.fd = fd;
+ Pfd.events = POLLIN;
+ return (fd);
+}
+
+
+/*
+ * Pull up one message from log driver.
+ */
+void
+sun_getkmsg()
+{
+ int flags = 0, i;
+ char *lastline;
+ struct strbuf ctl, dat;
+ struct log_ctl hdr;
+ char buf[MAXLINE+1];
+ size_t buflen;
+ size_t len;
+ char tmpbuf[MAXLINE+1];
+
+ dat.maxlen = MAXLINE;
+ dat.buf = buf;
+ ctl.maxlen = sizeof (struct log_ctl);
+ ctl.buf = (caddr_t)&hdr;
+
+ while ((i = getmsg(Pfd.fd, &ctl, &dat, &flags)) == MOREDATA) {
+ lastline = &dat.buf[dat.len];
+ *lastline = '\0';
+
+ dbgprintf("klog:sys_poll: getmsg: dat.len = %d\n", dat.len);
+ buflen = strlen(buf);
+ len = findnl_bkwd(buf, buflen);
+
+ (void) memcpy(tmpbuf, buf, len);
+ tmpbuf[len] = '\0';
+
+ Syslog(LOG_INFO, (uchar*) buf);
+
+ if (len != buflen) {
+ /* If anything remains in buf */
+ size_t remlen;
+
+ if (buf[len] == '\n') {
+ /* skip newline */
+ len++;
+ }
+
+ /* Move the remaining bytes to
+ * the beginnning of buf.
+ */
+
+ remlen = buflen - len;
+ (void) memcpy(buf, &buf[len], remlen);
+ dat.maxlen = MAXLINE - remlen;
+ dat.buf = &buf[remlen];
+ } else {
+ dat.maxlen = MAXLINE;
+ dat.buf = buf;
+ }
+ }
+
+ if (i == 0 && dat.len > 0) {
+ dat.buf[dat.len] = '\0';
+ /* Format sys will enqueue the log message.
+ * Set the sync flag if timeout != 0, which
+ * means that we're done handling all the
+ * initial messages ready during startup.
+ */
+ dbgprintf("klog:getkmsg: getmsg: dat.maxlen = %d\n", dat.maxlen);
+ dbgprintf("klog:getkmsg: getmsg: dat.len = %d\n", dat.len);
+ dbgprintf("klog:getkmsg: getmsg: strlen(dat.buf) = %d\n", strlen(dat.buf));
+ dbgprintf("klog:getkmsg: getmsg: dat.buf = \"%s\"\n", dat.buf);
+ dbgprintf("klog:getkmsg: buf len = %d\n", strlen(buf));
+ Syslog(LOG_INFO, (uchar*) buf);
+ } else if (i < 0 && errno != EINTR) {
+ if(1){ /* V5-TODO: rsyslog-like termination! (!shutting_down) { */
+ dbgprintf("klog:kernel log driver read error");
+ }
+ // TODO trigger retry logic
+ //(void) close(Pfd.fd);
+ //Pfd.fd = -1;
+ }
+}
+
+
+/* this thread listens to the local stream log driver for log messages
+ * generated by this host, formats them, and queues them to the logger
+ * thread.
+ */
+/*ARGSUSED*/
+void *
+sun_sys_poll()
+{
+ int nfds;
+
+ dbgprintf("klog:sys_poll: sys_thread started\n");
+
+ for (;;) {
+ errno = 0;
+
+ nfds = poll(&Pfd, 1, INFTIM);
+
+ if (nfds == 0)
+ continue;
+
+ if (nfds < 0) {
+ if (errno != EINTR)
+ dbgprintf("klog:poll error");
+ continue;
+ }
+ if (Pfd.revents & POLLIN) {
+ sun_getkmsg();
+ } else {
+ /* TODO: shutdown, the rsyslog way (in v5!) -- check shutdown flag */
+ if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
+ // TODO: trigger retry logic
+/* logerror("kernel log driver poll error");
+ (void) close(Pfd.fd);
+ Pfd.fd = -1;
+ */
+ }
+ }
+
+ }
+ /*NOTREACHED*/
+ return (NULL);
+}
diff --git a/plugins/imklog/solaris_cddl.h b/plugins/imklog/solaris_cddl.h
new file mode 100644
index 00000000..d48ef628
--- /dev/null
+++ b/plugins/imklog/solaris_cddl.h
@@ -0,0 +1,2 @@
+void *sun_sys_poll();
+int sun_openklog(char *name, int mode);
diff --git a/plugins/immark/immark.c b/plugins/immark/immark.c
index 8504f872..6410003d 100644
--- a/plugins/immark/immark.c
+++ b/plugins/immark/immark.c
@@ -42,16 +42,27 @@
#include "module-template.h"
#include "errmsg.h"
#include "msg.h"
+#include "srUtils.h"
+#include "glbl.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* defines */
#define DEFAULT_MARK_PERIOD (20 * 60)
/* Module static data */
DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
static int iMarkMessagePeriod = DEFAULT_MARK_PERIOD;
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
/* 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)
@@ -71,16 +82,13 @@ CODESTARTrunInput
* right into the sleep below.
*/
while(1) {
- /* we do not need to handle the RS_RET_TERMINATE_NOW case any
- * special because we just need to terminate. This may be different
- * if a cleanup is needed. But for now, we can just use CHKiRet().
- * rgerhards, 2007-12-17
- */
- CHKiRet(thrdSleep(pThrd, iMarkMessagePeriod, 0)); /* seconds, micro seconds */
+ srSleep(iMarkMessagePeriod, 0); /* seconds, micro seconds */
+
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
+
logmsgInternal(NO_ERRCODE, LOG_INFO, (uchar*)"-- MARK --", MARK);
}
-finalize_it:
- return iRet;
ENDrunInput
@@ -106,6 +114,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
@@ -119,9 +128,9 @@ BEGINmodInit()
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));
ENDmodInit
-/*
- * vi:set ai:
+/* vi:set ai:
*/
diff --git a/plugins/impstats/Makefile.am b/plugins/impstats/Makefile.am
new file mode 100644
index 00000000..187bbca4
--- /dev/null
+++ b/plugins/impstats/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = impstats.la
+
+impstats_la_SOURCES = impstats.c
+impstats_la_CPPFLAGS = $(RSRT_CFLAGS) -I$(top_srcdir) $(PTHREADS_CFLAGS)
+impstats_la_LDFLAGS = -module -avoid-version
+impstats_la_LIBADD =
diff --git a/plugins/impstats/impstats.c b/plugins/impstats/impstats.c
new file mode 100644
index 00000000..cfbbad76
--- /dev/null
+++ b/plugins/impstats/impstats.c
@@ -0,0 +1,231 @@
+/* impstats.c
+ * A module to periodically output statistics gathered by rsyslog.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <pthread.h>
+#include "dirty.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "msg.h"
+#include "srUtils.h"
+#include "unicode-helper.h"
+#include "glbl.h"
+#include "statsobj.h"
+#include "prop.h"
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+
+/* defines */
+#define DEFAULT_STATS_PERIOD (5 * 60)
+#define DEFAULT_FACILITY 5 /* syslog */
+#define DEFAULT_SEVERITY 6 /* info */
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(statsobj)
+DEFobjCurrIf(errmsg)
+
+typedef struct configSettings_s {
+ int iStatsInterval;
+ int iFacility;
+ int iSeverity;
+} configSettings_t;
+
+static configSettings_t cs;
+
+static prop_t *pInputName = NULL;
+static prop_t *pLocalHostIP = NULL;
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+static inline void
+initConfigSettings(void)
+{
+ cs.iStatsInterval = DEFAULT_STATS_PERIOD;
+ cs.iFacility = DEFAULT_FACILITY;
+ cs.iSeverity = DEFAULT_SEVERITY;
+}
+
+
+/* actually submit a message to the rsyslog core
+ */
+static inline rsRetVal
+doSubmitMsg(uchar *line)
+{
+ msg_t *pMsg;
+ DEFiRet;
+
+ CHKiRet(msgConstruct(&pMsg));
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsgWOSize(pMsg, (char*)line);
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ MsgSetRcvFrom(pMsg, glbl.GetLocalHostNameProp());
+ MsgSetRcvFromIP(pMsg, pLocalHostIP);
+ MsgSetMSGoffs(pMsg, 0);
+ MsgSetTAG(pMsg, UCHAR_CONSTANT("rsyslogd-pstats:"), sizeof("rsyslogd-pstats:") - 1);
+ pMsg->iFacility = cs.iFacility;
+ pMsg->iSeverity = cs.iSeverity;
+ pMsg->msgFlags = 0;
+
+ submitMsg(pMsg);
+
+finalize_it:
+ RETiRet;
+
+}
+
+
+/* callback for statsobj
+ * Note: usrptr exists only to satisfy requirements of statsobj callback interface!
+ */
+static rsRetVal
+doStatsLine(void __attribute__((unused)) *usrptr, cstr_t *cstr)
+{
+ DEFiRet;
+ doSubmitMsg(rsCStrGetSzStrNoNULL(cstr));
+ RETiRet;
+}
+
+
+/* the function to generate the actual statistics messages
+ * rgerhards, 2010-09-09
+ */
+static inline void
+generateStatsMsgs(void)
+{
+ statsobj.GetAllStatsLines(doStatsLine, NULL);
+}
+
+
+BEGINrunInput
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+ while(1) {
+ srSleep(cs.iStatsInterval, 0); /* seconds, micro seconds */
+
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
+
+ generateStatsMsgs();
+ }
+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
+
+
+BEGINafterRun
+CODESTARTafterRun
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ prop.Destruct(&pInputName);
+ prop.Destruct(&pLocalHostIP);
+ /* release objects we used */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
+ENDmodExit
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ initConfigSettings();
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ DBGPRINTF("impstats version %s loading\n", VERSION);
+ initConfigSettings();
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+ /* the pstatsinverval is an alias to support a previous screwed-up syntax... */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatsinterval", 0, eCmdHdlrInt, NULL, &cs.iStatsInterval, STD_LOADABLE_MODULE_ID));
+ 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 *)"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/Makefile.am b/plugins/imptcp/Makefile.am
new file mode 100644
index 00000000..bfacc884
--- /dev/null
+++ b/plugins/imptcp/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imptcp.la
+
+imptcp_la_SOURCES = imptcp.c
+imptcp_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+imptcp_la_LDFLAGS = -module -avoid-version
+imptcp_la_LIBADD =
diff --git a/plugins/imptcp/imptcp.c b/plugins/imptcp/imptcp.c
new file mode 100644
index 00000000..e8cffbb0
--- /dev/null
+++ b/plugins/imptcp/imptcp.c
@@ -0,0 +1,1196 @@
+/* imptcp.c
+ * This is a native implementation of plain tcp. It is intentionally
+ * duplicate work (imtcp). The intent is to gain very fast and simple
+ * native ptcp support, utilizing the best interfaces Linux (no cross-
+ * platform intended!) has to offer.
+ *
+ * Note that in this module we try out some new naming conventions,
+ * so it may look a bit "different" from the other modules. We are
+ * investigating if removing prefixes can help make code more readable.
+ *
+ * File begun on 2010-08-10 by RGerhards
+ *
+ * Copyright 2007-2010 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"
+#if !defined(HAVE_EPOLL_CREATE)
+# error imptcp requires OS support for epoll - can not build
+ /* imptcp gains speed by using modern Linux capabilities. As such,
+ * it can only be build on platforms supporting the epoll API.
+ */
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/epoll.h>
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include "rsyslog.h"
+#include "cfsysline.h"
+#include "prop.h"
+#include "dirty.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+#include "glbl.h"
+#include "prop.h"
+#include "errmsg.h"
+#include "srUtils.h"
+#include "datetime.h"
+#include "ruleset.h"
+#include "msg.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 ... */
+#define TCPSRV_NO_ADDTL_DELIMITER -1 /* specifies that no additional delimiter is to be used in TCP framing */
+
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+
+/* static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+DEFobjCurrIf(prop)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(ruleset)
+
+
+
+/* config settings */
+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 */
+ 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) */
+} configSettings_t;
+
+static configSettings_t cs;
+
+/* data elements describing our running config */
+typedef struct ptcpsrv_s ptcpsrv_t;
+typedef struct ptcplstn_s ptcplstn_t;
+typedef struct ptcpsess_s ptcpsess_t;
+typedef struct epolld_s epolld_t;
+
+/* the ptcp server (listener) object
+ * Note that the object contains support for forming a linked list
+ * of them. It does not make sense to do this seperately.
+ */
+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;
+ 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 */
+};
+
+/* the ptcp session object. Describes a single active session.
+ * includes support for doubly-linked list.
+ */
+struct ptcpsess_s {
+ ptcpsrv_t *pSrv; /* our server */
+ 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? */
+ enum {
+ eAtStrtFram,
+ eInOctetCnt,
+ eInMsg
+ } inputState; /* our current state */
+ int iOctetsRemain; /* Number of Octets remaining in message */
+ TCPFRAMINGMODE eFraming;
+ uchar *pMsg; /* message (fragment) received */
+ prop_t *peerName; /* host name we received messages from */
+ prop_t *peerIP;
+//--- END from tcps_sess.h
+};
+
+
+/* the ptcp listener object. Describes a single active listener.
+ */
+struct ptcplstn_s {
+ ptcpsrv_t *pSrv; /* our server */
+ ptcplstn_t *prev, *next;
+ int sock;
+ epolld_t *epd;
+};
+
+
+/* type of object stored in epoll descriptor */
+typedef enum {
+ epolld_lstn,
+ epolld_sess
+} epolld_type_t;
+
+/* an epoll descriptor. contains all information necessary to process
+ * the result of epoll.
+ */
+struct epolld_s {
+ epolld_type_t typ;
+ void *ptr;
+ struct epoll_event ev;
+};
+
+
+/* global data */
+//static permittedPeers_t *pPermPeersRoot = NULL;
+static ptcpsrv_t *pSrvRoot = NULL;
+static int epollfd = -1; /* (sole) descriptor for epoll */
+static int iMaxLine; /* maximum size of a single message */
+/* we use a single static receive buffer, as this module is not multi-threaded. Keeping
+ * the buffer in the data segment is probably a little bit more efficient than on the stack
+ * (but at least I can't believe it will ever be less efficient ;) -- rgerhards, 2010-08-10
+ * Note that we do NOT (yet?) provide a config setting to set the buffer size. For usual
+ * syslog traffic, it should be large enough. Also keep in mind that we run under a virtual
+ * memory system, so if we do not use large parts of the buffer, that's no issue at
+ * all -- it'll just use up address space. On the other hand, it would be silly to page in
+ * or page out some data just to get space for the IO buffer.
+ */
+static char rcvBuf[128*1024];
+
+/* forward definitions */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+static rsRetVal addLstn(ptcpsrv_t *pSrv, int sock);
+
+
+/* some simple constructors/destructors */
+static void
+destructSess(ptcpsess_t *pSess)
+{
+ free(pSess->pMsg);
+ free(pSess->epd);
+ prop.Destruct(&pSess->peerName);
+ prop.Destruct(&pSess->peerIP);
+ /* TODO: make these inits compile-time switch depending: */
+ pSess->pMsg = NULL;
+ pSess->epd = NULL;
+ free(pSess);
+}
+
+static void
+destructSrv(ptcpsrv_t *pSrv)
+{
+ prop.Destruct(&pSrv->pInputName);
+ free(pSrv->port);
+ free(pSrv);
+}
+
+/****************************************** TCP SUPPORT FUNCTIONS ***********************************/
+/* We may later think about moving this into a helper library again. But the whole point
+ * so far was to keep everything related close togehter. -- rgerhards, 2010-08-10
+ */
+
+
+/* Start up a server. That means all of its listeners are created.
+ * Does NOT yet accept/process any incoming data (but binds ports). Hint: this
+ * code is to be executed before dropping privileges.
+ */
+static rsRetVal
+startupSrv(ptcpsrv_t *pSrv)
+{
+ DEFiRet;
+ int error, maxs, on = 1;
+ int sock = -1;
+ int numSocks;
+ int sockflags;
+ struct addrinfo hints, *res = NULL, *r;
+ uchar *lstnIP;
+
+ lstnIP = pSrv->lstnIP == NULL ? UCHAR_CONSTANT("") : pSrv->lstnIP;
+
+ DBGPRINTF("imptcp creating listen socket on server '%s', port %s\n", lstnIP, pSrv->port);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_STREAM;
+
+ error = getaddrinfo((char*)pSrv->lstnIP, (char*) pSrv->port, &hints, &res);
+ if(error) {
+ DBGPRINTF("error %d querying server '%s', port '%s'\n", error, pSrv->lstnIP, pSrv->port);
+ ABORT_FINALIZE(RS_RET_INVALID_PORT);
+ }
+
+ /* Count max number of sockets we may open */
+ for(maxs = 0, r = res; r != NULL ; r = r->ai_next, maxs++)
+ /* EMPTY */;
+
+ numSocks = 0; /* num of sockets counter at start of array */
+ for(r = res; r != NULL ; r = r->ai_next) {
+ sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if(sock < 0) {
+ if(!(r->ai_family == PF_INET6 && errno == EAFNOSUPPORT))
+ DBGPRINTF("error %d creating tcp listen socket", errno);
+ /* it is debatable if PF_INET with EAFNOSUPPORT should
+ * also be ignored...
+ */
+ continue;
+ }
+
+#ifdef IPV6_V6ONLY
+ if(r->ai_family == AF_INET6) {
+ int iOn = 1;
+ if(setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&iOn, sizeof (iOn)) < 0) {
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ }
+#endif
+ if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on)) < 0 ) {
+ DBGPRINTF("error %d setting tcp socket option\n", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ /* We use non-blocking IO! */
+ if((sockflags = fcntl(sock, F_GETFL)) != -1) {
+ sockflags |= O_NONBLOCK;
+ /* SETFL could fail too, so get it caught by the subsequent
+ * error check.
+ */
+ sockflags = fcntl(sock, F_SETFL, sockflags);
+ }
+ if(sockflags == -1) {
+ DBGPRINTF("error %d setting fcntl(O_NONBLOCK) on tcp socket", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+
+
+ /* We need to enable BSD compatibility. Otherwise an attacker
+ * could flood our log files by sending us tons of ICMP errors.
+ */
+#ifndef BSD
+ if(net.should_use_so_bsdcompat()) {
+ if (setsockopt(sock, SOL_SOCKET, SO_BSDCOMPAT,
+ (char *) &on, sizeof(on)) < 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "TCP setsockopt(BSDCOMPAT)");
+ close(sock);
+ sock = -1;
+ continue;
+ }
+ }
+#endif
+
+ if( (bind(sock, r->ai_addr, r->ai_addrlen) < 0)
+#ifndef IPV6_V6ONLY
+ && (errno != EADDRINUSE)
+#endif
+ ) {
+ /* TODO: check if *we* bound the socket - else we *have* an error! */
+ DBGPRINTF("error %d while binding tcp socket", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ if(listen(sock, 511) < 0) {
+ DBGPRINTF("tcp listen error %d, suspending\n", errno);
+ close(sock);
+ sock = -1;
+ continue;
+ }
+
+ /* 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));
+ ++numSocks;
+ }
+
+ if(numSocks != maxs)
+ DBGPRINTF("We could initialize %d TCP listen sockets out of %d we received "
+ "- this may or may not be an error indication.\n", numSocks, maxs);
+
+ if(numSocks == 0) {
+ DBGPRINTF("No TCP listen sockets could successfully be initialized");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_BIND);
+ }
+
+finalize_it:
+ if(res != NULL)
+ freeaddrinfo(res);
+
+ if(iRet != RS_RET_OK) {
+ if(sock != -1)
+ close(sock);
+ }
+
+ RETiRet;
+}
+
+
+/* Set pRemHost based on the address provided. This is to be called upon accept()ing
+ * a connection request. It must be provided by the socket we received the
+ * message on as well as a NI_MAXHOST size large character buffer for the FQDN.
+ * Please see http://www.hmug.org/man/3/getnameinfo.php (under Caveats)
+ * for some explanation of the code found below. If we detect a malicious
+ * hostname, we return RS_RET_MALICIOUS_HNAME and let the caller decide
+ * on how to deal with that.
+ * rgerhards, 2008-03-31
+ */
+static rsRetVal
+getPeerNames(prop_t **peerName, prop_t **peerIP, struct sockaddr *pAddr)
+{
+ int error;
+ uchar szIP[NI_MAXHOST] = "";
+ uchar szHname[NI_MAXHOST] = "";
+ struct addrinfo hints, *res;
+
+ DEFiRet;
+
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szIP, sizeof(szIP), NULL, 0, NI_NUMERICHOST);
+
+ if(error) {
+ DBGPRINTF("Malformed from address %s\n", gai_strerror(error));
+ strcpy((char*)szHname, "???");
+ strcpy((char*)szIP, "???");
+ ABORT_FINALIZE(RS_RET_INVALID_HNAME);
+ }
+
+ if(!glbl.GetDisableDNS()) {
+ error = getnameinfo(pAddr, SALEN(pAddr), (char*)szHname, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
+ if(error == 0) {
+ memset (&hints, 0, sizeof (struct addrinfo));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_socktype = SOCK_STREAM;
+ /* we now do a lookup once again. This one should fail,
+ * because we should not have obtained a non-numeric address. If
+ * we got a numeric one, someone messed with DNS!
+ */
+ if(getaddrinfo((char*)szHname, NULL, &hints, &res) == 0) {
+ freeaddrinfo (res);
+ /* OK, we know we have evil, so let's indicate this to our caller */
+ snprintf((char*)szHname, NI_MAXHOST, "[MALICIOUS:IP=%s]", szIP);
+ DBGPRINTF("Malicious PTR record, IP = \"%s\" HOST = \"%s\"", szIP, szHname);
+ iRet = RS_RET_MALICIOUS_HNAME;
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+ } else {
+ strcpy((char*)szHname, (char*)szIP);
+ }
+
+ /* We now have the names, so now let's allocate memory and store them permanently. */
+ CHKiRet(prop.Construct(peerName));
+ CHKiRet(prop.SetString(*peerName, szHname, ustrlen(szHname)));
+ CHKiRet(prop.ConstructFinalize(*peerName));
+ CHKiRet(prop.Construct(peerIP));
+ CHKiRet(prop.SetString(*peerIP, szIP, ustrlen(szIP)));
+ CHKiRet(prop.ConstructFinalize(*peerIP));
+
+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)
+{
+ int sockflags;
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ int iNewSock = -1;
+
+ DEFiRet;
+
+ iNewSock = accept(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);
+ }
+
+ CHKiRet(getPeerNames(peerName, peerIP, (struct sockaddr*) &addr));
+
+ /* set the new socket to non-blocking IO */
+ if((sockflags = fcntl(iNewSock, F_GETFL)) != -1) {
+ sockflags |= O_NONBLOCK;
+ /* SETFL could fail too, so get it caught by the subsequent
+ * error check.
+ */
+ sockflags = fcntl(iNewSock, F_SETFL, sockflags);
+ }
+ if(sockflags == -1) {
+ DBGPRINTF("error %d setting fcntl(O_NONBLOCK) on tcp socket %d", errno, iNewSock);
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+ *newSock = iNewSock;
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ /* the close may be redundant, but that doesn't hurt... */
+ if(iNewSock != -1)
+ close(iNewSock);
+ }
+
+ RETiRet;
+}
+
+
+/* This is a helper for submitting the message to the rsyslog core.
+ * It does some common processing, including resetting the various
+ * state variables to a "processed" state.
+ * Note that this function is also called if we had a buffer overflow
+ * due to a too-long message. So far, there is no indication this
+ * happened and it may be worth thinking about different handling
+ * of this case (what obviously would require a change to this
+ * function or some related code).
+ * rgerhards, 2009-04-23
+ * EXTRACT from tcps_sess.c
+ */
+static rsRetVal
+doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ msg_t *pMsg;
+ DEFiRet;
+
+ if(pThis->iMsg == 0) {
+ DBGPRINTF("discarding zero-sized message\n");
+ FINALIZE;
+ }
+
+ /* 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);
+ 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);
+
+ if(pMultiSub == NULL) {
+ CHKiRet(submitMsg(pMsg));
+ } else {
+ pMultiSub->ppMsgs[pMultiSub->nElem++] = pMsg;
+ if(pMultiSub->nElem == pMultiSub->maxElem)
+ CHKiRet(multiSubmitMsg(pMultiSub));
+ }
+
+
+finalize_it:
+ /* reset status variables */
+ pThis->bAtStrtOfFram = 1;
+ pThis->iMsg = 0;
+
+ RETiRet;
+}
+
+
+/* process the data received. As TCP is stream based, we need to process the
+ * data inside a state machine. The actual data received is passed in byte-by-byte
+ * from DataRcvd, and this function here compiles messages from them and submits
+ * the end result to the queue. Introducing this function fixes a long-term bug ;)
+ * rgerhards, 2008-03-14
+ * EXTRACT from tcps_sess.c
+ */
+static inline rsRetVal
+processDataRcvd(ptcpsess_t *pThis, char c, struct syslogTime *stTime, time_t ttGenTime, multi_submit_t *pMultiSub)
+{
+ DEFiRet;
+
+ if(pThis->inputState == eAtStrtFram) {
+ if(isdigit((int) c)) {
+ pThis->inputState = eInOctetCnt;
+ pThis->iOctetsRemain = 0;
+ pThis->eFraming = TCP_FRAMING_OCTET_COUNTING;
+ } else {
+ pThis->inputState = eInMsg;
+ pThis->eFraming = TCP_FRAMING_OCTET_STUFFING;
+ }
+ }
+
+ if(pThis->inputState == eInOctetCnt) {
+ if(isdigit(c)) {
+ pThis->iOctetsRemain = pThis->iOctetsRemain * 10 + c - '0';
+ } else { /* done with the octet count, so this must be the SP terminator */
+ DBGPRINTF("TCP Message with octet-counter, size %d.\n", pThis->iOctetsRemain);
+ if(c != ' ') {
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "delimiter is not SP but has ASCII value %d.\n", c);
+ }
+ if(pThis->iOctetsRemain < 1) {
+ /* TODO: handle the case where the octet count is 0! */
+ DBGPRINTF("Framing Error: invalid octet count\n");
+ errmsg.LogError(0, NO_ERRCODE, "Framing Error in received TCP message: "
+ "invalid octet count %d.\n", pThis->iOctetsRemain);
+ } else if(pThis->iOctetsRemain > iMaxLine) {
+ /* while we can not do anything against it, we can at least log an indication
+ * that something went wrong) -- rgerhards, 2008-03-14
+ */
+ DBGPRINTF("truncating message with %d octets - max msg size is %d\n",
+ pThis->iOctetsRemain, iMaxLine);
+ errmsg.LogError(0, NO_ERRCODE, "received oversize message: size is %d bytes, "
+ "max msg size is %d, truncating...\n", pThis->iOctetsRemain, iMaxLine);
+ }
+ pThis->inputState = eInMsg;
+ }
+ } else {
+ assert(pThis->inputState == eInMsg);
+ if(pThis->iMsg >= iMaxLine) {
+ /* emergency, we now need to flush, no matter if we are at end of message or not... */
+ DBGPRINTF("error: message received is larger than max msg size, we split it\n");
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ /* we might think if it is better to ignore the rest of the
+ * message than to treat it as a new one. Maybe this is a good
+ * candidate for a configuration parameter...
+ * rgerhards, 2006-12-04
+ */
+ }
+
+ if(( (c == '\n')
+ || ((pThis->pSrv->iAddtlFrameDelim != TCPSRV_NO_ADDTL_DELIMITER) && (c == pThis->pSrv->iAddtlFrameDelim))
+ ) && pThis->eFraming == TCP_FRAMING_OCTET_STUFFING) { /* record delimiter? */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ } else {
+ /* IMPORTANT: here we copy the actual frame content to the message - for BOTH framing modes!
+ * If we have a message that is larger than the max msg size, we truncate it. This is the best
+ * we can do in light of what the engine supports. -- rgerhards, 2008-03-14
+ */
+ if(pThis->iMsg < iMaxLine) {
+ *(pThis->pMsg + pThis->iMsg++) = c;
+ }
+ }
+
+ if(pThis->eFraming == TCP_FRAMING_OCTET_COUNTING) {
+ /* do we need to find end-of-frame via octet counting? */
+ pThis->iOctetsRemain--;
+ if(pThis->iOctetsRemain < 1) {
+ /* we have end of frame! */
+ doSubmitMsg(pThis, stTime, ttGenTime, pMultiSub);
+ pThis->inputState = eAtStrtFram;
+ }
+ }
+ }
+
+ RETiRet;
+}
+
+
+/* Processes the data received via a TCP session. If there
+ * is no other way to handle it, data is discarded.
+ * Input parameter data is the data received, iLen is its
+ * len as returned from recv(). iLen must be 1 or more (that
+ * is errors must be handled by caller!). iTCPSess must be
+ * the index of the TCP session that received the data.
+ * rgerhards 2005-07-04
+ * And another change while generalizing. We now return either
+ * RS_RET_OK, which means the session should be kept open
+ * or anything else, which means it must be closed.
+ * rgerhards, 2008-03-01
+ * As a performance optimization, we pick up the timestamp here. Acutally,
+ * this *is* the *correct* reception step for all the data we received, because
+ * we have just received a bunch of data! -- rgerhards, 2009-06-16
+ * EXTRACT from tcps_sess.c
+ */
+#define NUM_MULTISUB 1024
+static rsRetVal
+DataRcvd(ptcpsess_t *pThis, char *pData, size_t iLen)
+{
+ multi_submit_t multiSub;
+ msg_t *pMsgs[NUM_MULTISUB];
+ struct syslogTime stTime;
+ time_t ttGenTime;
+ char *pEnd;
+ DEFiRet;
+
+ assert(pData != NULL);
+ assert(iLen > 0);
+
+ datetime.getCurrTime(&stTime, &ttGenTime);
+ multiSub.ppMsgs = pMsgs;
+ multiSub.maxElem = NUM_MULTISUB;
+ multiSub.nElem = 0;
+
+ /* We now copy the message to the session buffer. */
+ pEnd = pData + iLen; /* this is one off, which is intensional */
+
+ while(pData < pEnd) {
+ CHKiRet(processDataRcvd(pThis, *pData++, &stTime, ttGenTime, &multiSub));
+ }
+
+ if(multiSub.nElem > 0) {
+ /* submit anything that was not yet submitted */
+ CHKiRet(multiSubmitMsg(&multiSub));
+ }
+
+finalize_it:
+ RETiRet;
+}
+#undef NUM_MULTISUB
+
+
+/****************************************** --END-- TCP SUPPORT FUNCTIONS ***********************************/
+
+
+static inline void
+initConfigSettings(void)
+{
+ cs.bEmitMsgOnClose = 0;
+ cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ cs.pszInputName = NULL;
+ cs.pRuleset = NULL;
+ cs.lstnIP = NULL;
+}
+
+
+/* add socket to the epoll set
+ */
+static inline rsRetVal
+addEPollSock(epolld_type_t typ, void *ptr, int sock, epolld_t **pEpd)
+{
+ DEFiRet;
+ epolld_t *epd = NULL;
+
+ CHKmalloc(epd = malloc(sizeof(epolld_t)));
+ epd->typ = typ;
+ epd->ptr = ptr;
+ *pEpd = epd;
+ epd->ev.events = EPOLLIN|EPOLLET;
+ epd->ev.data.ptr = (void*) epd;
+
+ if(epoll_ctl(epollfd, EPOLL_CTL_ADD, sock, &(epd->ev)) != 0) {
+ char errStr[1024];
+ int eno = errno;
+ errmsg.LogError(0, RS_RET_EPOLL_CTL_FAILED, "os error (%d) during epoll ADD: %s",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ ABORT_FINALIZE(RS_RET_EPOLL_CTL_FAILED);
+ }
+
+ DBGPRINTF("imptcp: added socket %d to epoll[%d] set\n", sock, epollfd);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ free(epd);
+ }
+ RETiRet;
+}
+
+
+/* remove a socket from the epoll set. Note that the epd parameter
+ * is not really required -- it is used to satisfy older kernels where
+ * epoll_ctl() required a non-NULL pointer even though the ptr is never used.
+ * For simplicity, we supply the same pointer we had when we created the
+ * event (it's simple because we have it at hand).
+ */
+static inline rsRetVal
+removeEPollSock(int sock, epolld_t *epd)
+{
+ DEFiRet;
+
+ DBGPRINTF("imptcp: removing socket %d from epoll[%d] set\n", sock, epollfd);
+
+ if(epoll_ctl(epollfd, EPOLL_CTL_DEL, sock, &(epd->ev)) != 0) {
+ char errStr[1024];
+ int eno = errno;
+ errmsg.LogError(0, RS_RET_EPOLL_CTL_FAILED, "os error (%d) during epoll DEL: %s",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ ABORT_FINALIZE(RS_RET_EPOLL_CTL_FAILED);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* add a listener to the server
+ */
+static rsRetVal
+addLstn(ptcpsrv_t *pSrv, int sock)
+{
+ DEFiRet;
+ ptcplstn_t *pLstn;
+
+ CHKmalloc(pLstn = malloc(sizeof(ptcplstn_t)));
+ pLstn->pSrv = pSrv;
+ pLstn->sock = sock;
+
+ /* add to start of server's listener list */
+ pLstn->prev = NULL;
+ pLstn->next = pSrv->pLstn;
+ if(pSrv->pLstn != NULL)
+ pSrv->pLstn->prev = pLstn;
+ pSrv->pLstn = pLstn;
+
+ iRet = addEPollSock(epolld_lstn, pLstn, sock, &pLstn->epd);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* add a session to the server
+ */
+static rsRetVal
+addSess(ptcpsrv_t *pSrv, int sock, prop_t *peerName, prop_t *peerIP)
+{
+ DEFiRet;
+ ptcpsess_t *pSess = NULL;
+
+ CHKmalloc(pSess = malloc(sizeof(ptcpsess_t)));
+ CHKmalloc(pSess->pMsg = malloc(iMaxLine * sizeof(uchar)));
+ pSess->pSrv = pSrv;
+ pSess->sock = sock;
+ pSess->inputState = eAtStrtFram;
+ pSess->iMsg = 0;
+ pSess->bAtStrtOfFram = 1;
+ pSess->peerName = peerName;
+ pSess->peerIP = peerIP;
+
+ /* add to start of server's listener list */
+ pSess->prev = NULL;
+ pSess->next = pSrv->pSess;
+ if(pSrv->pSess != NULL)
+ pSrv->pSess->prev = pSess;
+ pSrv->pSess = pSess;
+
+ iRet = addEPollSock(epolld_sess, pSess, sock, &pSess->epd);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* close/remove a session
+ * NOTE: we must first remove the fd from the epoll set and then close it -- else we
+ * get an error "bad file descriptor" from epoll.
+ */
+static rsRetVal
+closeSess(ptcpsess_t *pSess)
+{
+ int sock;
+ DEFiRet;
+
+ sock = pSess->sock;
+ CHKiRet(removeEPollSock(sock, pSess->epd));
+ close(sock);
+
+ /* finally unlink session from structures */
+//fprintf(stderr, "closing session %d next %p, prev %p\n", pSess->sock, pSess->next, pSess->prev);
+//DBGPRINTF("imptcp: pSess->next %p\n", pSess->next);
+//DBGPRINTF("imptcp: pSess->prev %p\n", pSess->prev);
+ if(pSess->next != NULL)
+ pSess->next->prev = pSess->prev;
+ if(pSess->prev == NULL) {
+ /* need to update root! */
+ pSess->pSrv->pSess = pSess->next;
+ } else {
+ pSess->prev->next = pSess->next;
+ }
+
+ /* unlinked, now remove structure */
+ destructSess(pSess);
+
+finalize_it:
+ DBGPRINTF("imtcp: session on socket %d closed with iRet %d.\n", sock, iRet);
+ RETiRet;
+}
+
+
+#if 0
+/* set permitted peer -- rgerhards, 2008-05-19
+ */
+static rsRetVal
+setPermittedPeer(void __attribute__((unused)) *pVal, uchar *pszID)
+{
+ DEFiRet;
+ CHKiRet(net.AddPermittedPeer(&pPermPeersRoot, pszID));
+ free(pszID); /* no longer needed, but we need to free as of interface def */
+finalize_it:
+ RETiRet;
+}
+#endif
+
+
+/* accept a new ruleset to bind. Checks if it exists and complains, if not */
+static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ 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);
+ cs.pRuleset = pRuleset;
+ DBGPRINTF("imptcp current bind ruleset %p: '%s'\n", pRuleset, pszName);
+
+finalize_it:
+ free(pszName); /* no longer needed */
+ RETiRet;
+}
+
+
+static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ DEFiRet;
+ ptcpsrv_t *pSrv;
+
+ CHKmalloc(pSrv = malloc(sizeof(ptcpsrv_t)));
+ pSrv->pSess = NULL;
+ pSrv->pLstn = NULL;
+ pSrv->bEmitMsgOnClose = cs.bEmitMsgOnClose;
+ pSrv->port = pNewVal;
+ pSrv->iAddtlFrameDelim = cs.iAddtlFrameDelim;
+ cs.pszInputName = NULL; /* moved over to pSrv, we do not own */
+ pSrv->lstnIP = cs.lstnIP;
+ cs.lstnIP = NULL; /* moved over to pSrv, we do not own */
+ pSrv->pRuleset = cs.pRuleset;
+ pSrv->pszInputName = (cs.pszInputName == NULL) ? UCHAR_CONSTANT("imptcp") : cs.pszInputName;
+ CHKiRet(prop.Construct(&pSrv->pInputName));
+ CHKiRet(prop.SetString(pSrv->pInputName, pSrv->pszInputName, ustrlen(pSrv->pszInputName)));
+ CHKiRet(prop.ConstructFinalize(pSrv->pInputName));
+
+ /* add to linked list */
+ pSrv->pNext = pSrvRoot;
+ pSrvRoot = pSrv;
+
+ /* all config vars are auto-reset -- this also is very useful with the
+ * new config format effort (v6).
+ */
+ resetConfigVariables(NULL, NULL);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, NO_ERRCODE, "error %d trying to add listener", iRet);
+ }
+ RETiRet;
+}
+
+
+/* start up all listeners
+ * This is a one-time stop once the module is set to start.
+ */
+static inline rsRetVal
+startupServers()
+{
+ DEFiRet;
+ ptcpsrv_t *pSrv;
+
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ DBGPRINTF("Starting up ptcp server for port %s, name '%s'\n", pSrv->port, pSrv->pszInputName);
+ startupSrv(pSrv);
+ pSrv = pSrv->pNext;
+ }
+
+ RETiRet;
+}
+
+
+/* process new activity on listener. This means we need to accept a new
+ * connection.
+ */
+static inline rsRetVal
+lstnActivity(ptcplstn_t *pLstn)
+{
+ int newSock;
+ prop_t *peerName;
+ prop_t *peerIP;
+ rsRetVal localRet;
+ DEFiRet;
+
+ DBGPRINTF("imptcp: new connection on listen socket %d\n", pLstn->sock);
+ while(1) {
+ localRet = AcceptConnReq(pLstn->sock, &newSock, &peerName, &peerIP);
+ if(localRet == RS_RET_NO_MORE_DATA)
+ break;
+ CHKiRet(localRet);
+ CHKiRet(addSess(pLstn->pSrv, newSock, peerName, peerIP));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* process new activity on session. This means we need to accept data
+ * or close the session.
+ */
+static inline rsRetVal
+sessActivity(ptcpsess_t *pSess)
+{
+ int lenRcv;
+ int lenBuf;
+ DEFiRet;
+
+ DBGPRINTF("imptcp: new activity on session socket %d\n", pSess->sock);
+
+ while(1) {
+ lenBuf = sizeof(rcvBuf);
+ lenRcv = recv(pSess->sock, rcvBuf, lenBuf, 0);
+
+ if(lenRcv > 0) {
+ /* have data, process it */
+ DBGPRINTF("imtcp: data(%d) on socket %d: %s\n", lenBuf, pSess->sock, rcvBuf);
+ CHKiRet(DataRcvd(pSess, rcvBuf, lenRcv));
+ } else if (lenRcv == 0) {
+ /* session was closed, do clean-up */
+ if(pSess->pSrv->bEmitMsgOnClose) {
+ uchar *peerName;
+ int lenPeer;
+ prop.GetString(pSess->peerName, &peerName, &lenPeer);
+ errmsg.LogError(0, RS_RET_PEER_CLOSED_CONN, "imptcp session %d closed by remote peer %s.\n",
+ pSess->sock, peerName);
+ }
+ CHKiRet(closeSess(pSess));
+ break;
+ } else {
+ if(errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+ DBGPRINTF("imtcp: error on session socket %d - closed.\n", pSess->sock);
+ closeSess(pSess); /* try clean-up by dropping session */
+ break;
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* This function is called to gather input.
+ */
+BEGINrunInput
+ int i;
+ int nfds;
+ struct epoll_event events[1];
+ epolld_t *epd;
+CODESTARTrunInput
+ DBGPRINTF("imptcp now beginning to process input data\n");
+ /* v5 TODO: consentual termination mode */
+ while(1) {
+ DBGPRINTF("imptcp going on epoll_wait\n");
+ nfds = epoll_wait(epollfd, events, sizeof(events)/sizeof(struct epoll_event), -1);
+ for(i = 0 ; i < nfds ; ++i) { /* support for larger batches (later, TODO) */
+ epd = (epolld_t*) events[i].data.ptr;
+ switch(epd->typ) {
+ case epolld_lstn:
+ lstnActivity((ptcplstn_t *) epd->ptr);
+ break;
+ case epolld_sess:
+ sessActivity((ptcpsess_t *) epd->ptr);
+ break;
+ default:
+ errmsg.LogError(0, RS_RET_INTERNAL_ERROR,
+ "error: invalid epolld_type_t %d after epoll", epd->typ);
+ break;
+ }
+ }
+ }
+ENDrunInput
+
+
+/* initialize and return if will run or not */
+BEGINwillRun
+CODESTARTwillRun
+ /* first apply some config settings */
+ //net.PrintAllowedSenders(2); /* TCP */
+ iMaxLine = glbl.GetMaxLine(); /* get maximum size we currently support */
+
+ if(pSrvRoot == NULL) {
+ errmsg.LogError(0, RS_RET_NO_LSTN_DEFINED, "error: no ptcp server defined, module can not run.");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+#if defined(EPOLL_CLOEXEC) && defined(HAVE_EPOLL_CREATE1)
+ DBGPRINTF("imptcp uses epoll_create1()\n");
+ epollfd = epoll_create1(EPOLL_CLOEXEC);
+ if(epollfd < 0 && errno == ENOSYS)
+#endif
+ {
+ DBGPRINTF("imptcp uses epoll_create()\n");
+ /* reading the docs, the number of epoll events passed to
+ * epoll_create() seems not to be used at all in kernels. So
+ * we just provide "a" number, happens to be 10.
+ */
+ epollfd = epoll_create(10);
+ }
+
+ if(epollfd < 0) {
+ errmsg.LogError(0, RS_RET_EPOLL_CR_FAILED, "error: epoll_create() failed");
+ ABORT_FINALIZE(RS_RET_NO_RUN);
+ }
+
+ /* start up servers, but do not yet read input data */
+ CHKiRet(startupServers());
+ DBGPRINTF("imptcp started up, but not yet receiving data\n");
+finalize_it:
+ENDwillRun
+
+
+/* completely shut down a server, that means closing all of its
+ * listeners and sessions.
+ */
+static inline void
+shutdownSrv(ptcpsrv_t *pSrv)
+{
+ ptcplstn_t *pLstn, *lstnDel;
+ ptcpsess_t *pSess, *sessDel;
+
+ /* listeners */
+ pLstn = pSrv->pLstn;
+ while(pLstn != NULL) {
+ close(pLstn->sock);
+ lstnDel = pLstn;
+ pLstn = pLstn->next;
+ DBGPRINTF("imptcp shutdown listen socket %d\n", lstnDel->sock);
+ free(lstnDel->epd);
+ free(lstnDel);
+ }
+
+ /* sessions */
+ pSess = pSrv->pSess;
+ while(pSess != NULL) {
+ close(pSess->sock);
+ sessDel = pSess;
+ pSess = pSess->next;
+ DBGPRINTF("imptcp shutdown session socket %d\n", sessDel->sock);
+ destructSess(sessDel);
+ }
+}
+
+
+BEGINafterRun
+ ptcpsrv_t *pSrv, *srvDel;
+CODESTARTafterRun
+ /* do cleanup here */
+ //net.clearAllowedSenders(UCHAR_CONSTANT("TCP"));
+ /* we need to close everything that is still open */
+ pSrv = pSrvRoot;
+ while(pSrv != NULL) {
+ srvDel = pSrv;
+ pSrv = pSrv->pNext;
+ shutdownSrv(srvDel);
+ destructSrv(srvDel);
+ }
+
+ close(epollfd);
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+#if 0
+ if(pPermPeersRoot != NULL) {
+ net.DestructPermittedPeers(&pPermPeersRoot);
+ }
+#endif
+
+ /* release objects we used */
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ objRelease(datetime, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
+ENDmodExit
+
+
+static rsRetVal
+resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ cs.bEmitMsgOnClose = 0;
+ cs.iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ free(cs.pszInputName);
+ cs.pszInputName = NULL;
+ free(cs.lstnIP);
+ cs.lstnIP = NULL;
+ return RS_RET_OK;
+}
+
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ initConfigSettings();
+ /* request objects we use */
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(net, LM_NET_FILENAME));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverrun"), 0, eCmdHdlrGetWord,
+ addTCPListener, NULL, 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,
+ NULL, &cs.iAddtlFrameDelim, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputptcpserverinputname"), 0,
+ eCmdHdlrGetWord, NULL, &cs.pszInputName, STD_LOADABLE_MODULE_ID));
+ 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));
+ CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("resetconfigvariables"), 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+
+/* vim:set ai:
+ */
diff --git a/plugins/imrelp/imrelp.c b/plugins/imrelp/imrelp.c
index 4d14b47a..20c4f450 100644
--- a/plugins/imrelp/imrelp.c
+++ b/plugins/imrelp/imrelp.c
@@ -47,6 +47,7 @@
#include "prop.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* static data */
DEF_IMOD_STATIC_DATA
diff --git a/plugins/imsolaris/Makefile.am b/plugins/imsolaris/Makefile.am
new file mode 100644
index 00000000..b4ee1c29
--- /dev/null
+++ b/plugins/imsolaris/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = imsolaris.la
+
+imsolaris_la_SOURCES = imsolaris.c sun_cddl.c sun_cddl.h imsolaris.h
+imsolaris_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+imsolaris_la_LDFLAGS = -module -avoid-version
+imsolaris_la_LIBADD = -ldoor -lpthread
diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c
new file mode 100644
index 00000000..ee9ec5c6
--- /dev/null
+++ b/plugins/imsolaris/imsolaris.c
@@ -0,0 +1,400 @@
+/* imsolaris.c
+ * This input module is used to gather local log data under Solaris. This
+ * includes messages from local applications AS WELL AS the kernel log.
+ * I first considered to make all of this available via imklog, but that
+ * did not lock appropriately on second thought. So I created this module
+ * that does anything for local message recption.
+ *
+ * This module is not meant to be used on plaforms other than Solaris. As
+ * such, trying to compile it elswhere will probably fail with all sorts
+ * of errors.
+ *
+ * Some notes on the Solaris syslog mechanism:
+ * Both system (kernel) and application log messages are provided via
+ * a single message stream.
+ *
+ * Solaris checks if the syslogd is running. If so, syslog() emits messages
+ * to the log socket, only. Otherwise, it emits messages to the console.
+ * It is possible to gather these console messages as well. However, then
+ * we clutter the console.
+ * Solaris does this "syslogd alive check" in a somewhat unexpected way
+ * (at least unexpected for me): it uses the so-called "door" mechanism, a
+ * fast RPC facility. I first thought that the door API was used to submit
+ * the actual syslog messages. But this is not the case. Instead, a door
+ * call is done, and the server process inside rsyslog simply does NOTHING
+ * but return. All that Solaris sylsogd() is interested in is if the door
+ * server (we) responds and thus can be considered alive. The actual message
+ * is then submitted via the usual stream. I have to admit I do not
+ * understand why the message itself is not passed via this high-performance
+ * API. But anyhow, that's nothing I can change, so the most important thing
+ * is to note how Solaris does this thing ;)
+ * The syslog() library call checks syslogd state for *each* call (what a
+ * waste of time...) and decides each time if the message should go to the
+ * console or not. According to OpenSolaris sources, it looks like there is
+ * message loss potential when the door file is created before all data has
+ * been pulled from the stream. While I have to admit that I do not fully
+ * understand that problem, I will follow the original code advise and do
+ * one complete pull cycle on the log socket (until it has no further data
+ * available) and only thereafter create the door file and start the "regular"
+ * pull cycle. As of my understanding, there is a minimal race between the
+ * point where the intial pull cycle has ended and the door file is created,
+ * but that race is also present in OpenSolaris syslogd code, so it should
+ * not matter that much (plus, I do not know how to avoid it...)
+ *
+ * File begun on 2010-04-15 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/strlog.h>
+#include <errno.h>
+#include "dirty.h"
+#include "cfsysline.h"
+#include "unicode-helper.h"
+#include "module-template.h"
+#include "srUtils.h"
+#include "errmsg.h"
+#include "net.h"
+#include "glbl.h"
+#include "msg.h"
+#include "prop.h"
+#include "sun_cddl.h"
+
+MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
+
+/* defines */
+#define PATH_LOG "/dev/log"
+
+
+/* Module static data */
+DEF_IMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(prop)
+
+
+/* config settings */
+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! */
+
+
+/* a function to replace the sun logerror() function.
+ * It generates an error message from the supplied string. The main
+ * reason for not calling logError directly is that sun_cddl.c does not
+ * know or has acces to rsyslog objects (namely errmsg) -- and we do not
+ * want to do this effort. -- rgerhards, 2010-04-19
+ */
+void
+imsolaris_logerror(int err, char *errStr)
+{
+ errmsg.LogError(err, RS_RET_ERR_DOOR, "%s", errStr);
+}
+
+
+/* we try to recover a failed file by closing and re-opening
+ * it. We loop until the re-open works, but wait between each
+ * failure. If the open succeeds, we assume all is well. If it is
+ * not, we will run into the retry process with the next
+ * iteration.
+ * rgerhards, 2010-04-19
+ */
+static void
+tryRecover(void)
+{
+ int tryNum = 1;
+ int waitsecs;
+ int waitusecs;
+ rsRetVal iRet;
+
+ close(sun_Pfd.fd);
+ sun_Pfd.fd = -1;
+
+ while(1) { /* loop broken inside */
+ iRet = sun_openklog((LogName == NULL) ? PATH_LOG : LogName);
+ if(iRet == RS_RET_OK) {
+ if(tryNum > 1) {
+ errmsg.LogError(0, iRet, "failure on system log socket recovered.");
+ }
+ break;
+ }
+ /* failure, so sleep a bit. We wait try*10 ms, with a max of 15 seconds */
+ if(tryNum == 1) {
+ errmsg.LogError(0, iRet, "failure on system log socket, trying to recover...");
+ }
+ waitusecs = tryNum * 10000;
+ waitsecs = waitusecs / 1000000;
+ DBGPRINTF("imsolaris: try %d to recover system log socket in %d.%d seconds\n",
+ tryNum, waitsecs, waitusecs);
+ if(waitsecs > 15) {
+ waitsecs = 15;
+ waitusecs = 0;
+ } else {
+ waitusecs = waitusecs % 1000000;
+ }
+ srSleep(waitsecs, waitusecs);
+ ++tryNum;
+ }
+}
+
+
+/* This function receives data from a socket indicated to be ready
+ * to receive and submits the message received for processing.
+ * rgerhards, 2007-12-20
+ * Interface changed so that this function is passed the array index
+ * of the socket which is to be processed. This eases access to the
+ * growing number of properties. -- rgerhards, 2008-08-01
+ */
+static rsRetVal
+readLog(int fd, uchar *pRcv, int iMaxLine)
+{
+ DEFiRet;
+ struct strbuf data;
+ struct strbuf ctl;
+ struct log_ctl hdr;
+ int flags;
+ msg_t *pMsg;
+ int ret;
+ char errStr[1024];
+
+ data.buf = (char*)pRcv;
+ data.maxlen = iMaxLine;
+ ctl.maxlen = sizeof (struct log_ctl);
+ ctl.buf = (caddr_t)&hdr;
+ flags = 0;
+ ret = getmsg(fd, &ctl, &data, &flags);
+ if(ret < 0) {
+ if(errno == EINTR) {
+ FINALIZE;
+ } else {
+ int en = errno;
+ rs_strerror_r(errno, errStr, sizeof(errStr));
+ DBGPRINTF("imsolaris: stream input error on fd %d: %s.\n", fd, errStr);
+ errmsg.LogError(en, NO_ERRCODE, "imsolaris: stream input error: %s", errStr);
+ tryRecover();
+ }
+ } else {
+ DBGPRINTF("imsolaris: message from log stream %d: %s\n", fd, pRcv);
+ pRcv[data.len] = '\0'; /* make sure it is a valid C-String */
+ CHKiRet(msgConstruct(&pMsg));
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetRawMsg(pMsg, (char*)pRcv, strlen((char*)pRcv));
+ MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
+ pMsg->iFacility = LOG_FAC(hdr.pri);
+ pMsg->iSeverity = LOG_PRI(hdr.pri);
+ pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW;
+ CHKiRet(submitMsg(pMsg));
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* once the system is fully initialized, we wait for new messages.
+ * We may think about replacing this with a read-loop, thus saving
+ * us the overhead of the poll.
+ * The timeout variable is the timeout to use for poll. During startup,
+ * it should be set to 0 (non-blocking) and later to -1 (infinit, blocking).
+ * This mimics the (strange) behaviour of the original syslogd.
+ * rgerhards, 2010-04-19
+ */
+static inline rsRetVal
+getMsgs(thrdInfo_t *pThrd, int timeout)
+{
+ DEFiRet;
+ int nfds;
+ int iMaxLine;
+ uchar *pRcv = NULL; /* receive buffer */
+ uchar bufRcv[4096+1];
+ char errStr[1024];
+
+ iMaxLine = glbl.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 {
+ CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1)));
+ }
+
+ while(pThrd->bShallStop != TRUE) {
+ DBGPRINTF("imsolaris: waiting for next message (timeout %d)...\n", timeout);
+ if(timeout == 0) {
+ nfds = poll(&sun_Pfd, 1, timeout); /* wait without timeout */
+
+ if(pThrd->bShallStop == TRUE) {
+ break;
+ }
+
+ if(nfds == 0) {
+ if(timeout == 0) {
+ DBGPRINTF("imsolaris: no more messages, getMsgs() terminates\n");
+ FINALIZE;
+ } else {
+ continue;
+ }
+ }
+
+ if(nfds < 0) {
+ if(errno != EINTR) {
+ int en = errno;
+ rs_strerror_r(en, errStr, sizeof(errStr));
+ DBGPRINTF("imsolaris: poll error: %d = %s.\n", errno, errStr);
+ errmsg.LogError(en, NO_ERRCODE, "imsolaris: poll error: %s",
+ errStr);
+ }
+ continue;
+ }
+ if(sun_Pfd.revents & POLLIN) {
+ readLog(sun_Pfd.fd, pRcv, iMaxLine);
+ } else if(sun_Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
+ tryRecover();
+ }
+ } else {
+ /* if we have an infinite wait, we do not use poll at all
+ * I'd consider this a waste of time. However, I do not totally
+ * remove the code, as it may be useful if we decide at some
+ * point to provide a capability to support multiple input streams
+ * at once (this may be useful for a jail). In that case, the poll()
+ * loop would be needed, and so it doesn't make much sense to change
+ * the code to not support it. -- rgerhards, 2010-04-20
+ */
+ readLog(sun_Pfd.fd, pRcv, iMaxLine);
+ }
+
+ }
+
+finalize_it:
+ if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1)
+ free(pRcv);
+
+ RETiRet;
+}
+
+
+/* This function is called to gather input. */
+BEGINrunInput
+CODESTARTrunInput
+ /* this is an endless loop - it is terminated when the thread is
+ * signalled to do so. This, however, is handled by the framework,
+ * right into the sleep below.
+ */
+
+ DBGPRINTF("imsolaris: doing startup poll before openeing door()\n");
+ CHKiRet(getMsgs(pThrd, 0));
+
+ /* note: sun's syslogd code claims that the door should only
+ * be opened when the log stream has been polled. So file header
+ * comment of this file for more details.
+ */
+ sun_open_door();
+ DBGPRINTF("imsolaris: starting regular poll loop\n");
+ iRet = getMsgs(pThrd, -1); /* this is the primary poll loop, infinite timeout */
+
+ DBGPRINTF("imsolaris: terminating (bShallStop=%d)\n", pThrd->bShallStop);
+finalize_it:
+ RETiRet;
+ENDrunInput
+
+
+BEGINwillRun
+CODESTARTwillRun
+ /* we need to create the inputName property (only once during our lifetime) */
+ CHKiRet(prop.Construct(&pInputName));
+ CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imsolaris"), sizeof("imsolaris") - 1));
+ CHKiRet(prop.ConstructFinalize(pInputName));
+
+ iRet = sun_openklog((LogName == NULL) ? PATH_LOG : LogName);
+ if(iRet != RS_RET_OK) {
+ errmsg.LogError(0, iRet, "error opening system log socket");
+ }
+finalize_it:
+ENDwillRun
+
+
+BEGINafterRun
+CODESTARTafterRun
+ /* do cleanup here */
+ if(pInputName != NULL)
+ prop.Destruct(&pInputName);
+ free(LogName);
+ENDafterRun
+
+
+BEGINmodExit
+CODESTARTmodExit
+ sun_delete_doorfiles();
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(prop, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp,
+ void __attribute__((unused)) *pVal)
+{
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(prop, CORE_COMPONENT));
+
+ DBGPRINTF("imsolaris version %s initializing\n", PACKAGE_VERSION);
+
+ /* register config file handlers */
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imsolarislogsocketname", 0, eCmdHdlrGetWord,
+ NULL, &LogName, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+/* vim:set ai:
+ */
diff --git a/plugins/imsolaris/imsolaris.h b/plugins/imsolaris/imsolaris.h
new file mode 100644
index 00000000..e73380fa
--- /dev/null
+++ b/plugins/imsolaris/imsolaris.h
@@ -0,0 +1,2 @@
+rsRetVal solaris_readLog(int fd);
+void imsolaris_logerror(int err, char *errStr);
diff --git a/plugins/imsolaris/sun_cddl.c b/plugins/imsolaris/sun_cddl.c
new file mode 100644
index 00000000..6d49c8bc
--- /dev/null
+++ b/plugins/imsolaris/sun_cddl.c
@@ -0,0 +1,419 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/* Portions Copyright 2010 by Rainer Gerhards and Adiscon
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
+ * All Rights Reserved
+ */
+
+/*
+ * University Copyright- Copyright (c) 1982, 1986, 1988
+ * The Regents of the University of California
+ * All Rights Reserved
+ *
+ * University Acknowledgment- Portions of this document are derived from
+ * software developed by the University of California, Berkeley, and its
+ * contributors.
+ */
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <pthread.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <assert.h>
+
+#include <sys/param.h>
+#include <sys/strlog.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <door.h>
+#include <sys/door.h>
+
+#include "rsyslog.h"
+#include "srUtils.h"
+#include "debug.h"
+#include "imsolaris.h"
+
+#define DOORFILE "/var/run/syslog_door"
+#define RELATIVE_DOORFILE "../var/run/syslog_door"
+#define OLD_DOORFILE "/etc/.syslog_door"
+
+/* Buffer to allocate for error messages: */
+#define ERRMSG_LEN 1024
+
+/* Max number of door server threads for syslogd. Since door is used
+ * to check the health of syslogd, we don't need large number of
+ * server threads.
+ */
+#define MAX_DOOR_SERVER_THR 3
+
+
+struct pollfd sun_Pfd; /* Pollfd for local log device */
+
+static int DoorFd = -1;
+static int DoorCreated = 0;
+static char *DoorFileName = DOORFILE;
+
+/* for managing door server threads */
+static pthread_mutex_t door_server_cnt_lock = PTHREAD_MUTEX_INITIALIZER;
+static uint_t door_server_cnt = 0;
+static pthread_attr_t door_thr_attr;
+
+/* the 'server' function that we export via the door. It does
+ * nothing but return.
+ */
+/*ARGSUSED*/
+static void
+server( void __attribute__((unused)) *cookie,
+ char __attribute__((unused)) *argp,
+ size_t __attribute__((unused)) arg_size,
+ door_desc_t __attribute__((unused)) *dp,
+ __attribute__((unused)) uint_t n )
+{
+ (void) door_return(NULL, 0, NULL, 0);
+ /* NOTREACHED */
+}
+
+/*ARGSUSED*/
+static void *
+create_door_thr(void __attribute__((unused)) *arg)
+{
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ (void) door_return(NULL, 0, NULL, 0);
+
+ /* If there is an error in door_return(), it will return here and
+ * the thread will exit. Hence we need to decrement door_server_cnt.
+ */
+ (void) pthread_mutex_lock(&door_server_cnt_lock);
+ door_server_cnt--;
+ (void) pthread_mutex_unlock(&door_server_cnt_lock);
+ return (NULL);
+}
+
+/*
+ * Manage door server thread pool.
+ */
+/*ARGSUSED*/
+static void
+door_server_pool(door_info_t __attribute__((unused)) *dip)
+{
+ (void) pthread_mutex_lock(&door_server_cnt_lock);
+ if (door_server_cnt <= MAX_DOOR_SERVER_THR &&
+ pthread_create(NULL, &door_thr_attr, create_door_thr, NULL) == 0) {
+ door_server_cnt++;
+ (void) pthread_mutex_unlock(&door_server_cnt_lock);
+ return;
+ }
+
+ (void) pthread_mutex_unlock(&door_server_cnt_lock);
+}
+
+void
+sun_delete_doorfiles(void)
+{
+ struct stat sb;
+ int err;
+ char line[ERRMSG_LEN+1];
+
+ if (lstat(DoorFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
+ if (unlink(DoorFileName) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line),
+ "unlink() of %s failed - fatal", DoorFileName);
+ imsolaris_logerror(err, line);
+ DBGPRINTF("delete_doorfiles: error: %s, "
+ "errno=%d\n", line, err);
+ exit(1);
+ }
+
+ DBGPRINTF("delete_doorfiles: deleted %s\n", DoorFileName);
+ }
+
+ if (strcmp(DoorFileName, DOORFILE) == 0) {
+ if (lstat(OLD_DOORFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
+ if (unlink(OLD_DOORFILE) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line),
+ "unlink() of %s failed", OLD_DOORFILE);
+ DBGPRINTF("delete_doorfiles: %s\n", line);
+
+ if (err != EROFS) {
+ errno = err;
+ (void) strlcat(line, " - fatal",
+ sizeof (line));
+ imsolaris_logerror(err, line);
+ DBGPRINTF("delete_doorfiles: "
+ "error: %s, errno=%d\n",
+ line, err);
+ exit(1);
+ }
+
+ DBGPRINTF("delete_doorfiles: unlink() "
+ "failure OK on RO file system\n");
+ }
+
+ DBGPRINTF("delete_doorfiles: deleted %s\n",
+ OLD_DOORFILE);
+ }
+ }
+
+ if (DoorFd != -1) {
+ (void) door_revoke(DoorFd);
+ }
+
+ DBGPRINTF("delete_doorfiles: revoked door: DoorFd=%d\n",
+ DoorFd);
+}
+
+
+/* Create the door file. If the filesystem
+ * containing /etc is writable, create symlinks /etc/.syslog_door
+ * to them. On systems that do not support /var/run, create
+ * /etc/.syslog_door directly.
+ */
+void
+sun_open_door(void)
+{
+ struct stat buf;
+ door_info_t info;
+ char line[ERRMSG_LEN+1];
+ int err;
+
+ /* first see if another instance of imsolaris OR another
+ * syslogd is running by trying a door call - if it succeeds,
+ * there is already one active.
+ */
+
+ if (!DoorCreated) {
+ int door;
+
+ if ((door = open(DoorFileName, O_RDONLY)) >= 0) {
+ DBGPRINTF("open_door: %s opened "
+ "successfully\n", DoorFileName);
+
+ if (door_info(door, &info) >= 0) {
+ DBGPRINTF("open_door: "
+ "door_info:info.di_target = %ld\n",
+ info.di_target);
+
+ if (info.di_target > 0) {
+ (void) sprintf(line, "syslogd pid %ld"
+ " already running. Cannot "
+ "start another syslogd pid %ld",
+ info.di_target, getpid());
+ DBGPRINTF("open_door: error: "
+ "%s\n", line);
+ imsolaris_logerror(0, line);
+ exit(1);
+ }
+ }
+
+ (void) close(door);
+ } else {
+ if (lstat(DoorFileName, &buf) < 0) {
+ err = errno;
+
+ DBGPRINTF("open_door: lstat() of %s "
+ "failed, errno=%d\n",
+ DoorFileName, err);
+
+ if ((door = creat(DoorFileName, 0644)) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line),
+ "creat() of %s failed - fatal",
+ DoorFileName);
+ DBGPRINTF("open_door: error: %s, "
+ "errno=%d\n", line,
+ err);
+ imsolaris_logerror(err, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+
+ (void) fchmod(door,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+
+ DBGPRINTF("open_door: creat() of %s "
+ "succeeded\n",
+ DoorFileName);
+
+ (void) close(door);
+ }
+ }
+
+ if (strcmp(DoorFileName, DOORFILE) == 0) {
+ if (lstat(OLD_DOORFILE, &buf) == 0) {
+ DBGPRINTF("open_door: lstat() of %s "
+ "succeeded\n", OLD_DOORFILE);
+
+ if (S_ISDIR(buf.st_mode)) {
+ (void) snprintf(line, sizeof (line),
+ "%s is a directory - fatal",
+ OLD_DOORFILE);
+ DBGPRINTF("open_door: error: "
+ "%s\n", line);
+ imsolaris_logerror(0, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+
+ DBGPRINTF("open_door: %s is not a "
+ "directory\n", OLD_DOORFILE);
+ if (unlink(OLD_DOORFILE) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line),
+ "unlink() of %s failed",
+ OLD_DOORFILE);
+ DBGPRINTF("open_door: %s\n",
+ line);
+
+ if (err != EROFS) {
+ DBGPRINTF("open_door: "
+ "error: %s, "
+ "errno=%d\n",
+ line, err);
+ (void) strcat(line, " - fatal");
+ imsolaris_logerror(err, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+
+ DBGPRINTF("open_door: unlink "
+ "failure OK on RO file "
+ "system\n");
+ }
+ } else {
+ DBGPRINTF("open_door: file %s doesn't "
+ "exist\n", OLD_DOORFILE);
+ }
+
+ if (symlink(RELATIVE_DOORFILE, OLD_DOORFILE) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line),
+ "symlink %s -> %s failed", OLD_DOORFILE,
+ RELATIVE_DOORFILE);
+ DBGPRINTF("open_door: %s\n",
+ line);
+
+ if (err != EROFS) {
+ DBGPRINTF("open_door: error: %s, "
+ "errno=%d\n", line,
+ err);
+ (void) strcat(line, " - fatal");
+ imsolaris_logerror(err, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+
+ DBGPRINTF("open_door: symlink failure OK "
+ "on RO file system\n");
+ } else {
+ DBGPRINTF("open_door: symlink %s -> %s "
+ "succeeded\n",
+ OLD_DOORFILE, RELATIVE_DOORFILE);
+ }
+ }
+
+ if ((DoorFd = door_create(server, 0,
+ DOOR_REFUSE_DESC)) < 0) {
+ //???? DOOR_NO_CANEL requires newer libs??? DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
+ err = errno;
+ (void) sprintf(line, "door_create() failed - fatal");
+ DBGPRINTF("open_door: error: %s, errno=%d\n",
+ line, err);
+ imsolaris_logerror(err, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+ //???? (void) door_setparam(DoorFd, DOOR_PARAM_DATA_MAX, 0);
+ DBGPRINTF("open_door: door_create() succeeded, "
+ "DoorFd=%d\n", DoorFd);
+
+ DoorCreated = 1;
+ }
+
+ (void) fdetach(DoorFileName); /* just in case... */
+
+ (void) door_server_create(door_server_pool);
+
+ if (fattach(DoorFd, DoorFileName) < 0) {
+ err = errno;
+ (void) snprintf(line, sizeof (line), "fattach() of fd"
+ " %d to %s failed - fatal", DoorFd, DoorFileName);
+ DBGPRINTF("open_door: error: %s, errno=%d\n",
+ line, err);
+ imsolaris_logerror(err, line);
+ sun_delete_doorfiles();
+ exit(1);
+ }
+
+ DBGPRINTF("open_door: attached server() to %s\n",
+ DoorFileName);
+
+}
+
+
+/* Attempts to open the local log device
+ * and return a file descriptor.
+ */
+rsRetVal
+sun_openklog(char *name)
+{
+ DEFiRet;
+ int fd;
+ struct strioctl str;
+ char errBuf[1024];
+
+ if((fd = open(name, O_RDONLY)) < 0) {
+ rs_strerror_r(errno, errBuf, sizeof(errBuf));
+ DBGPRINTF("imsolaris:openklog: cannot open %s: %s\n",
+ name, errBuf);
+ ABORT_FINALIZE(RS_RET_ERR_OPEN_KLOG);
+ }
+ str.ic_cmd = I_CONSLOG;
+ str.ic_timout = 0;
+ str.ic_len = 0;
+ str.ic_dp = NULL;
+ if (ioctl(fd, I_STR, &str) < 0) {
+ rs_strerror_r(errno, errBuf, sizeof(errBuf));
+ DBGPRINTF("imsolaris:openklog: cannot register to log "
+ "console messages: %s\n", errBuf);
+ ABORT_FINALIZE(RS_RET_ERR_AQ_CONLOG);
+ }
+ sun_Pfd.fd = fd;
+ sun_Pfd.events = POLLIN;
+ DBGPRINTF("imsolaris/openklog: opened '%s' as fd %d.\n", name, fd);
+
+finalize_it:
+ RETiRet;
+}
diff --git a/plugins/imsolaris/sun_cddl.h b/plugins/imsolaris/sun_cddl.h
new file mode 100644
index 00000000..42e4b799
--- /dev/null
+++ b/plugins/imsolaris/sun_cddl.h
@@ -0,0 +1,7 @@
+rsRetVal sun_openklog(char *name);
+void prepare_sys_poll(void);
+void sun_sys_poll(void);
+void sun_open_door(void);
+void sun_delete_doorfiles(void);
+
+extern struct pollfd sun_Pfd; /* Pollfd for local log device */
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index d122e976..d3e9cabe 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -65,6 +65,7 @@
#include "net.h" /* for permittedPeers, may be removed when this is removed */
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* static data */
DEF_IMOD_STATIC_DATA
@@ -86,6 +87,7 @@ 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 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) */
@@ -97,7 +99,7 @@ static int
isPermittedHost(struct sockaddr *addr, char *fromHostFQDN, void __attribute__((unused)) *pUsrSrv,
void __attribute__((unused)) *pUsrSess)
{
- return net.isAllowedSender(UCHAR_CONSTANT("TCP"), addr, fromHostFQDN);
+ return net.isAllowedSender2(UCHAR_CONSTANT("TCP"), addr, fromHostFQDN, 1);
}
@@ -171,7 +173,7 @@ static rsRetVal setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
localRet = ruleset.GetRuleset(&pRuleset, pszName);
if(localRet == RS_RET_NOT_FOUND) {
- errmsg.LogError(0, NO_ERRCODE, "error: ruleset '%s' not found - ignored", pszName);
+ errmsg.LogError(0, RS_RET_RULESET_NOT_FOUND, "error: ruleset '%s' not found - ignored", pszName);
}
CHKiRet(localRet);
pBindRuleset = pRuleset;
@@ -198,6 +200,7 @@ static rsRetVal addTCPListener(void __attribute__((unused)) *pVal, uchar *pNewVa
CHKiRet(tcpsrv.SetCBOnErrClose(pOurTcpsrv, onErrClose));
CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, iStrmDrvrMode));
CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, iAddtlFrameDelim));
+ CHKiRet(tcpsrv.SetbDisableLFDelim(pOurTcpsrv, bDisableLFDelim));
CHKiRet(tcpsrv.SetNotificationOnRemoteClose(pOurTcpsrv, bEmitMsgOnClose));
/* now set optional params, but only if they were actually configured */
if(pszStrmDrvrAuthMode != NULL) {
@@ -254,6 +257,13 @@ CODESTARTafterRun
ENDafterRun
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
BEGINmodExit
CODESTARTmodExit
if(pOurTcpsrv != NULL)
@@ -281,6 +291,7 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
iStrmDrvrMode = 0;
bEmitMsgOnClose = 0;
iAddtlFrameDelim = TCPSRV_NO_ADDTL_DELIMITER;
+ bDisableLFDelim = 0;
free(pszInputName);
pszInputName = NULL;
free(pszStrmDrvrAuthMode);
@@ -293,6 +304,7 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -326,6 +338,8 @@ CODEmodInit_QueryRegCFSLineHdlr
eCmdHdlrGetWord, setPermittedPeer, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr(UCHAR_CONSTANT("inputtcpserveraddtlframedelimiter"), 0, eCmdHdlrInt,
NULL, &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,
diff --git a/plugins/imtemplate/imtemplate.c b/plugins/imtemplate/imtemplate.c
index 366408a0..0e2cac11 100644
--- a/plugins/imtemplate/imtemplate.c
+++ b/plugins/imtemplate/imtemplate.c
@@ -77,8 +77,10 @@
#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 */
@@ -137,7 +139,7 @@ imtemplateMyFunc(int iMyParam)
* 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)
+ * 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.
@@ -231,49 +233,25 @@ CODESTARTrunInput
* logs an error message as syslogd, just as printf, e.g.
* errmsg.LogError(NO_ERRCODE, "Error %d occured during %s", 1, "test");
*
- * There are several ways how a message can be enqueued. This part of the
- * interface is currently underspecified. Have a look at the function definitions
- * in syslogd.c (sorry, folks...).
- *
- * If you received a full syslog message that must be decoded by a message
- * parser, parseAndSubmitMessage() is the way to go. It's not just a funny name
- * but also a quite some legacy. Consequently, its interface is, ummm, not
- * well designed.
- * parseAndSubmitMessage((char*)fromHost, (char*) pRcvBuf, lenRcvd, bParseHost);
- * fromHost
- * is the host that we received the message from (a string)
- * pRcvBuf
- * is the received (to-be-decoded) message
- * lenRcvd
- * is the length of the received message. Please note that pRcvBuf is
- * NOT a standard C-string. Most importantly it is NOT expected to be
- * \0-terminated. Thus the lenght is vitally imporant (if it is wrong,
- * rsyslogd will probably segfault).
- * bParseHost
- * is a boolean (0-no, 1-yes). It tells the parser whether or not
- * a hostname should be parsed from the message. This is important
- * for sources that are known not to provide a hostname.
- * Use define MSG_PARSE_HOSTNAME and MSG_DONT_PARSE_HOSTNAME
- *
- * Another, more elaborate, way is to create the message object ourselves and
- * pass it to the rule engine. That way is more appropriate if the message
+ * 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));
- MsgSetUxTradMsg(pMsg, msg);
MsgSetRawMsg(pMsg, msg);
MsgSetHOSTNAME(pMsg, LocalHostName);
MsgSetTAG(pMsg, "rsyslogd:");
pMsg->iFacility = LOG_FAC(pri);
pMsg->iSeverity = LOG_PRI(pri);
- pMsg->bParseHOSTNAME = 0;
flags |= INTERNAL_MSG;
logmsg(pMsg, flags); / * some time, CHKiRet() will work here, too [today NOT!] * /
*
- * Note that UxTradMsg is a wild construct. For the time being, set it to
- * the raw message text. I am hard thinking at dropping that beast at all...
+ * 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
@@ -314,7 +292,7 @@ CODESTARTwillRun
if(udpLstnSocks == NULL)
ABORT_FINALIZE(RS_RET_NO_RUN);
- if((pRcvBuf = malloc(glbl.GetMaxLine * sizeof(char))) == NULL) {
+ if((pRcvBuf = MALLOC(glbl.GetMaxLine * sizeof(char))) == NULL) {
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
}
*
diff --git a/plugins/imudp/Makefile.am b/plugins/imudp/Makefile.am
index 517b1287..bc64b8c8 100644
--- a/plugins/imudp/Makefile.am
+++ b/plugins/imudp/Makefile.am
@@ -3,4 +3,4 @@ pkglib_LTLIBRARIES = imudp.la
imudp_la_SOURCES = imudp.c
imudp_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
imudp_la_LDFLAGS = -module -avoid-version
-imudp_la_LIBADD =
+imudp_la_LIBADD = $(IMUDP_LIBS)
diff --git a/plugins/imudp/imudp.c b/plugins/imudp/imudp.c
index f8555f00..a5002591 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -32,6 +32,12 @@
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
+#if HAVE_SYS_EPOLL_H
+# include <sys/epoll.h>
+#endif
+#ifdef HAVE_SCHED_H
+# include <sched.h>
+#endif
#include "rsyslog.h"
#include "dirty.h"
#include "net.h"
@@ -44,9 +50,11 @@
#include "parser.h"
#include "datetime.h"
#include "prop.h"
+#include "ruleset.h"
#include "unicode-helper.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* defines */
@@ -57,7 +65,9 @@ DEFobjCurrIf(glbl)
DEFobjCurrIf(net)
DEFobjCurrIf(datetime)
DEFobjCurrIf(prop)
+DEFobjCurrIf(ruleset)
+static int bDoACLCheck; /* are ACL checks neeed? Cached once immediately before listener startup */
static int iMaxLine; /* maximum UDP message size supported */
static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitted sender was last discarded
* This shall prevent remote DoS when the "discard on disallowed sender"
@@ -65,18 +75,110 @@ static time_t ttLastDiscard = 0; /* timestamp when a message from a non-permitte
*/
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 */
-// TODO: static ruleset_t *pBindRuleset = NULL; /* ruleset to bind listener to (use system default if unspecified) */
+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).
+ */
+static rsRetVal set_scheduling_priority(void *pVal, int value)
+{
+ 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);
+ }
+ *((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
+ } else {
+ errmsg.LogError(errno, NO_ERRCODE,
+ "imudp: invalid scheduling policy '%s' "
+ "- ignoring setting", pszSchedPolicy);
+ }
+ if (have_sched_policy == 0) {
+ free(pszSchedPolicy);
+ pszSchedPolicy = NULL;
+ ABORT_FINALIZE(RS_RET_VALIDATION_RUN);
+ }
+ if (seen_iSchedPrio)
+ CHKiRet(check_scheduling_priority(1));
+
+finalize_it:
+ 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
@@ -90,6 +192,7 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
int *newSocks;
int *tmpSocks;
int iSrc, iDst;
+ ruleset_t **tmpRulesets;
/* 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
@@ -110,26 +213,39 @@ static rsRetVal addListner(void __attribute__((unused)) *pVal, uchar *pNewVal)
if(udpLstnSocks == NULL) {
/* esay, we can just replace it */
udpLstnSocks = newSocks;
+ CHKmalloc(udpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (newSocks[0] + 1)));
+ for(iDst = 1 ; iDst <= newSocks[0] ; ++iDst)
+ udpRulesets[iDst] = pBindRuleset;
} else {
/* we need to add them */
- if((tmpSocks = malloc(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0]))) == NULL) {
- dbgprintf("out of memory trying to allocate udp listen socket array\n");
+ tmpSocks = (int*) MALLOC(sizeof(int) * (1 + newSocks[0] + udpLstnSocks[0]));
+ tmpRulesets = (ruleset_t**) MALLOC(sizeof(ruleset_t*) * (1 + newSocks[0] + udpLstnSocks[0]));
+ if(tmpSocks == NULL || tmpRulesets == NULL) {
+ DBGPRINTF("out of memory trying to allocate udp listen socket array\n");
/* in this case, we discard the new sockets but continue with what we
* already have
*/
free(newSocks);
+ free(tmpSocks);
+ free(tmpRulesets);
ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
} else {
/* ready to copy */
iDst = 1;
- for(iSrc = 1 ; iSrc <= udpLstnSocks[0] ; ++iSrc)
- tmpSocks[iDst++] = udpLstnSocks[iSrc];
- for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc)
- tmpSocks[iDst++] = newSocks[iSrc];
+ for(iSrc = 1 ; iSrc <= udpLstnSocks[0] ; ++iSrc, ++iDst) {
+ tmpSocks[iDst] = udpLstnSocks[iSrc];
+ tmpRulesets[iDst] = udpRulesets[iSrc];
+ }
+ for(iSrc = 1 ; iSrc <= newSocks[0] ; ++iSrc, ++iDst) {
+ tmpSocks[iDst] = newSocks[iSrc];
+ tmpRulesets[iDst] = pBindRuleset;
+ }
tmpSocks[0] = udpLstnSocks[0] + newSocks[0];
free(newSocks);
free(udpLstnSocks);
udpLstnSocks = tmpSocks;
+ free(udpRulesets);
+ udpRulesets = tmpRulesets;
}
}
}
@@ -141,7 +257,6 @@ finalize_it:
}
-#if 0 /* TODO: implement when tehre is time, requires restructure of socket array! */
/* accept a new ruleset to bind. Checks if it exists and complains, if not */
static rsRetVal
setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
@@ -162,7 +277,6 @@ finalize_it:
free(pszName); /* no longer needed */
RETiRet;
}
-#endif
/* This function is a helper to runInput. I have extracted it
@@ -180,8 +294,8 @@ finalize_it:
* on scheduling order. -- rgerhards, 2008-10-02
*/
static inline rsRetVal
-processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
- uchar *fromHost, uchar *fromHostFQDN, uchar *fromHostIP)
+processSocket(thrdInfo_t *pThrd, int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
+ ruleset_t *pRuleset)
{
DEFiRet;
int iNbrTimeUsed;
@@ -195,8 +309,11 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
prop_t *propFromHostIP = NULL;
char errStr[1024];
+ assert(pThrd != NULL);
iNbrTimeUsed = 0;
while(1) { /* loop is terminated if we have a bad receive, done below in the body */
+ 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);
if(lenRcvBuf < 0) {
@@ -205,7 +322,7 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
DBGPRINTF("INET socket error: %d = %s.\n", errno, errStr);
errmsg.LogError(errno, NO_ERRCODE, "recvfrom inet");
}
- ABORT_FINALIZE(RS_RET_ERR);
+ ABORT_FINALIZE(RS_RET_ERR); // this most often is NOT an error, state is not checked by caller!
}
if(lenRcvBuf == 0)
@@ -213,37 +330,39 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
/* if we reach this point, we had a good receive and can process the packet received */
/* check if we have a different sender than before, if so, we need to query some new values */
- if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) {
- CHKiRet(net.cvthname(&frominet, fromHost, fromHostFQDN, fromHostIP));
- memcpy(frominetPrev, &frominet, socklen); /* update cache indicator */
- /* Here we check if a host is permitted to send us
- * syslog messages. If it isn't, we do not further
- * process the message but log a warning (if we are
- * configured to do this).
- * rgerhards, 2005-09-26
- */
- *pbIsPermitted = net.isAllowedSender((uchar*)"UDP",
- (struct sockaddr *)&frominet, (char*)fromHostFQDN);
-
- if(!*pbIsPermitted) {
- DBGPRINTF("%s is not an allowed sender\n", (char*)fromHostFQDN);
- if(glbl.GetOption_DisallowWarning) {
- time_t tt;
-
- time(&tt);
- if(tt > ttLastDiscard + 60) {
- ttLastDiscard = tt;
- errmsg.LogError(0, NO_ERRCODE,
- "UDP message from disallowed sender %s discarded",
- (char*)fromHost);
+ if(bDoACLCheck) {
+ if(net.CmpHost(&frominet, frominetPrev, socklen) != 0) {
+ memcpy(frominetPrev, &frominet, socklen); /* update cache indicator */
+ /* Here we check if a host is permitted to send us syslog messages. If it isn't,
+ * we do not further process the message but log a warning (if we are
+ * configured to do this). However, if the check would require name resolution,
+ * it is postponed to the main queue. See also my blog post at
+ * http://blog.gerhards.net/2009/11/acls-imudp-and-accepting-messages.html
+ * rgerhards, 2009-11-16
+ */
+ *pbIsPermitted = net.isAllowedSender2((uchar*)"UDP",
+ (struct sockaddr *)&frominet, "", 0);
+
+ if(*pbIsPermitted == 0) {
+ DBGPRINTF("msg is not from an allowed sender\n");
+ if(glbl.GetOption_DisallowWarning) {
+ time_t tt;
+ datetime.GetTime(&tt);
+ if(tt > ttLastDiscard + 60) {
+ ttLastDiscard = tt;
+ errmsg.LogError(0, NO_ERRCODE,
+ "UDP message from disallowed sender discarded");
+ }
}
}
}
+ } else {
+ *pbIsPermitted = 1; /* no check -> everything permitted */
}
- DBGPRINTF("recv(%d,%d)/%s,acl:%d,msg:%s\n", fd, (int) lenRcvBuf, fromHost, *pbIsPermitted, pRcvBuf);
+ DBGPRINTF("recv(%d,%d),acl:%d,msg:%s\n", fd, (int) lenRcvBuf, *pbIsPermitted, pRcvBuf);
- if(*pbIsPermitted) {
+ if(*pbIsPermitted != 0) {
if((iTimeRequery == 0) || (iNbrTimeUsed++ % iTimeRequery) == 0) {
datetime.getCurrTime(&stTime, &ttGenTime);
}
@@ -251,11 +370,12 @@ processSocket(int fd, struct sockaddr_storage *frominetPrev, int *pbIsPermitted,
CHKiRet(msgConstructWithTime(&pMsg, &stTime, ttGenTime));
MsgSetRawMsg(pMsg, (char*)pRcvBuf, lenRcvBuf);
MsgSetInputName(pMsg, pInputName);
+ MsgSetRuleset(pMsg, pRuleset);
MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
- pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
- pMsg->bParseHOSTNAME = 1;
- MsgSetRcvFromStr(pMsg, fromHost, ustrlen(fromHost), &propFromHost);
- CHKiRet(MsgSetRcvFromIPStr(pMsg, fromHostIP, ustrlen(fromHostIP), &propFromHostIP));
+ 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));
}
}
@@ -269,40 +389,140 @@ finalize_it:
RETiRet;
}
+static void set_thread_schedparam(void)
+{
+ struct sched_param sparam;
+
+ if (pszSchedPolicy != NULL && seen_iSchedPrio == 0) {
+ errmsg.LogError(0, NO_ERRCODE,
+ "imudp: scheduling policy set, but without priority - ignoring settings");
+ } else if (pszSchedPolicy == NULL && seen_iSchedPrio != 0) {
+ errmsg.LogError(0, NO_ERRCODE,
+ "imudp: scheduling priority set, but without policy - ignoring settings");
+ } else if (pszSchedPolicy != NULL && seen_iSchedPrio != 0 &&
+ check_scheduling_priority(0) == 0) {
+#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");
+ }
+#endif
+ }
-/* This function is called to gather input.
- * Note that udpLstnSocks 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
- * rgerhards, 2008-10-07: I have implemented a very simple, yet in most cases probably
- * highly efficient "name caching". Before querying a name, I now check if the name to be
- * queried is the same as the one queried in the last message processed. If that is the
- * case, we can simple re-use the previous value. This algorithm works quite well with
- * few sender, especially if they emit messages in bursts. The more sender and the
- * more intermixed messages arrive, the less this algorithm works, but the overhead
- * is so minimal (a simple memory compare and move) that this does not hurt. Even
- * with a real name lookup cache, this optimization here is useful as it is quicker
- * than even a cache lookup).
+ if (pszSchedPolicy != NULL) {
+ free(pszSchedPolicy);
+ pszSchedPolicy = NULL;
+ }
+}
+
+/* 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.
+ * rgerhards, 2009-09-09
*/
-BEGINrunInput
+#if defined(HAVE_EPOLL_CREATE1) || defined(HAVE_EPOLL_CREATE)
+#define NUM_EPOLL_EVENTS 10
+rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
+{
+ DEFiRet;
+ int nfds;
+ int efd;
+ int i;
+ struct sockaddr_storage frominetPrev;
+ int bIsPermitted;
+ struct epoll_event *udpEPollEvt = NULL;
+ struct epoll_event currEvt[NUM_EPOLL_EVENTS];
+ char errStr[1024];
+
+ /* 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)));
+
+#if defined(EPOLL_CLOEXEC) && defined(HAVE_EPOLL_CREATE1)
+ DBGPRINTF("imudp uses epoll_create1()\n");
+ efd = epoll_create1(EPOLL_CLOEXEC);
+ if(efd < 0 && errno == ENOSYS)
+#endif
+ {
+ DBGPRINTF("imudp uses epoll_create()\n");
+ efd = epoll_create(NUM_EPOLL_EVENTS);
+ }
+
+ if(efd < 0) {
+ DBGPRINTF("epoll_create1() could not create fd\n");
+ ABORT_FINALIZE(RS_RET_IO_ERROR);
+ }
+
+ /* 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) {
+ udpEPollEvt[i].events = EPOLLIN | EPOLLET;
+ udpEPollEvt[i].data.u64 = i+1;
+ if(epoll_ctl(efd, EPOLL_CTL_ADD, udpLstnSocks[i+1], &(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);
+ }
+ }
+ }
+
+ while(1) {
+ /* wait for io to become ready */
+ nfds = epoll_wait(efd, currEvt, NUM_EPOLL_EVENTS, -1);
+ DBGPRINTF("imudp: epoll_wait() returned with %d fds\n", nfds);
+
+ if(pThrd->bShallStop == TRUE)
+ break; /* terminate input! */
+
+ for(i = 0 ; i < nfds ; ++i) {
+ processSocket(pThrd, udpLstnSocks[currEvt[i].data.u64], &frominetPrev, &bIsPermitted,
+ udpRulesets[currEvt[i].data.u64]);
+ }
+ }
+
+finalize_it:
+ if(udpEPollEvt != NULL)
+ free(udpEPollEvt);
+
+ RETiRet;
+}
+#else /* #if HAVE_EPOLL_CREATE1 */
+/* this is the code for the select() interface */
+rsRetVal rcvMainLoop(thrdInfo_t *pThrd)
+{
+ DEFiRet;
int maxfds;
int nfds;
int i;
fd_set readfds;
struct sockaddr_storage frominetPrev;
int bIsPermitted;
- uchar fromHost[NI_MAXHOST];
- uchar fromHostIP[NI_MAXHOST];
- uchar fromHostFQDN[NI_MAXHOST];
-CODESTARTrunInput
+
/* start "name caching" algo by making sure the previous system indicator
* is invalidated.
*/
+ set_thread_schedparam();
bIsPermitted = 0;
memset(&frominetPrev, 0, sizeof(frominetPrev));
- /* this is an endless loop - it is terminated when the thread is
- * signalled to do so. This, however, is handled by the framework,
- * right into the sleep below.
- */
+ DBGPRINTF("imudp uses select()\n");
+
while(1) {
/* Add the Unix Domain Sockets to the list of read
* descriptors.
@@ -311,7 +531,7 @@ CODESTARTrunInput
* is given without -a, we do not need to listen at all..
*/
maxfds = 0;
- FD_ZERO (&readfds);
+ FD_ZERO(&readfds);
/* Add the UDP listen sockets to the list of read descriptors. */
for (i = 0; i < *udpLstnSocks; i++) {
@@ -325,25 +545,37 @@ CODESTARTrunInput
if(Debug) {
dbgprintf("--------imUDP calling select, active file descriptors (max %d): ", maxfds);
for (nfds = 0; nfds <= maxfds; ++nfds)
- if ( FD_ISSET(nfds, &readfds) )
+ if(FD_ISSET(nfds, &readfds))
dbgprintf("%d ", nfds);
dbgprintf("\n");
}
/* wait for io to become ready */
nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
for(i = 0; nfds && i < *udpLstnSocks; i++) {
if(FD_ISSET(udpLstnSocks[i+1], &readfds)) {
- processSocket(udpLstnSocks[i+1], &frominetPrev, &bIsPermitted,
- fromHost, fromHostFQDN, fromHostIP);
+ processSocket(pThrd, udpLstnSocks[i+1], &frominetPrev, &bIsPermitted,
+ udpRulesets[i+1]);
--nfds; /* indicate we have processed one descriptor */
}
}
/* end of a run, back to loop for next recv() */
}
- return iRet;
+ RETiRet;
+}
+#endif /* #if HAVE_EPOLL_CREATE1 */
+
+/* This function is called to gather input.
+ * Note that udpLstnSocks 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
+CODESTARTrunInput
+ iRet = rcvMainLoop(pThrd);
ENDrunInput
@@ -356,6 +588,7 @@ CODESTARTwillRun
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)
@@ -363,9 +596,7 @@ CODESTARTwillRun
iMaxLine = glbl.GetMaxLine();
- if((pRcvBuf = malloc((iMaxLine + 1) * sizeof(char))) == NULL) {
- ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
- }
+ CHKmalloc(pRcvBuf = MALLOC((iMaxLine + 1) * sizeof(char)));
finalize_it:
ENDwillRun
@@ -377,6 +608,8 @@ CODESTARTafterRun
if(udpLstnSocks != NULL) {
net.closeUDPListenSockets(udpLstnSocks);
udpLstnSocks = NULL;
+ free(udpRulesets);
+ udpRulesets = NULL;
}
if(pRcvBuf != NULL) {
free(pRcvBuf);
@@ -394,13 +627,22 @@ CODESTARTmodExit
objRelease(glbl, CORE_COMPONENT);
objRelease(datetime, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
+ objRelease(ruleset, CORE_COMPONENT);
objRelease(net, LM_NET_FILENAME);
ENDmodExit
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
@@ -409,10 +651,6 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
free(pszBindAddr);
pszBindAddr = NULL;
}
- if(udpLstnSocks != NULL) {
- net.closeUDPListenSockets(udpLstnSocks);
- udpLstnSocks = NULL;
- }
iTimeRequery = TIME_REQUERY_DFLT;/* the default is to query only every second time */
return RS_RET_OK;
}
@@ -426,17 +664,20 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(datetime, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
CHKiRet(objUse(net, LM_NET_FILENAME));
/* register config file handlers */
- /* TODO: add - but this requires more changes, no time right now...
- CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverbindruleset", 0, eCmdHdlrGetWord,
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputudpserverbindruleset", 0, eCmdHdlrGetWord,
setRuleset, NULL, STD_LOADABLE_MODULE_ID));
- */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserverrun", 0, eCmdHdlrGetWord,
addListner, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpserveraddress", 0, eCmdHdlrGetWord,
NULL, &pszBindAddr, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpolicy", 0, eCmdHdlrGetWord,
+ &set_scheduling_policy, &pszSchedPolicy, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imudpschedulingpriority", 0, eCmdHdlrInt,
+ &set_scheduling_priority, &iSchedPrio, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"udpservertimerequery", 0, eCmdHdlrInt,
NULL, &iTimeRequery, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
diff --git a/plugins/imuxsock/Makefile.am b/plugins/imuxsock/Makefile.am
index a2fe0baa..34a0ad9a 100644
--- a/plugins/imuxsock/Makefile.am
+++ b/plugins/imuxsock/Makefile.am
@@ -1,6 +1,6 @@
pkglib_LTLIBRARIES = imuxsock.la
imuxsock_la_SOURCES = imuxsock.c
-imuxsock_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+imuxsock_la_CPPFLAGS = -DSD_EXPORT_SYMBOLS -I../../runtime/hashtable -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
imuxsock_la_LDFLAGS = -module -avoid-version
-imuxsock_la_LIBADD =
+imuxsock_la_LIBADD = $(RSRT_LIBS)
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c
index 5567a405..af034b9f 100644
--- a/plugins/imuxsock/imuxsock.c
+++ b/plugins/imuxsock/imuxsock.c
@@ -6,7 +6,7 @@
*
* File begun on 2007-12-20 by RGerhards (extracted from syslogd.c)
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007-2010 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -29,12 +29,14 @@
#include "rsyslog.h"
#include <stdlib.h>
#include <stdio.h>
+#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/un.h>
+#include <sys/socket.h>
#include "dirty.h"
#include "cfsysline.h"
#include "unicode-helper.h"
@@ -44,12 +46,20 @@
#include "net.h"
#include "glbl.h"
#include "msg.h"
+#include "parser.h"
#include "prop.h"
+#include "debug.h"
+#include "unlimited_select.h"
+#include "sd-daemon.h"
+#include "statsobj.h"
+#include "datetime.h"
+#include "hashtable.h"
MODULE_TYPE_INPUT
+MODULE_TYPE_NOKEEP
/* defines */
-#define MAXFUNIX 20
+#define MAXFUNIX 50
#ifndef _PATH_LOG
#ifdef BSD
#define _PATH_LOG "/var/run/log"
@@ -58,6 +68,10 @@ MODULE_TYPE_INPUT
#endif
#endif
+/* emulate struct ucred for platforms that do not have it */
+#ifndef HAVE_SCM_CREDENTIALS
+struct ucred { int pid; };
+#endif
/* handle some defines missing on more than one platform */
#ifndef SUN_LEN
@@ -69,26 +83,157 @@ DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+DEFobjCurrIf(statsobj)
+
+statsobj_t *modStats;
+STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit)
+STATSCOUNTER_DEF(ctrLostRatelimit, mutCtrLostRatelimit)
+STATSCOUNTER_DEF(ctrNumRatelimiters, mutCtrNumRatelimiters)
+
+struct rs_ratelimit_state {
+ unsigned short interval;
+ unsigned short burst;
+ unsigned done;
+ unsigned missed;
+ time_t begin;
+};
+typedef struct rs_ratelimit_state rs_ratelimit_state_t;
+
+
+/* a very simple "hash function" for process IDs - we simply use the
+ * pid itself: it is quite expected that all pids may log some time, but
+ * from a collision point of view it is likely that long-running daemons
+ * start early and so will stay right in the top spots of the
+ * collision list.
+ */
+static unsigned int
+hash_from_key_fn(void *k)
+{
+ return((unsigned) *((pid_t*) k));
+}
+
+static int
+key_equals_fn(void *key1, void *key2)
+{
+ return *((pid_t*) key1) == *((pid_t*) key2);
+}
+
-static prop_t *pInputName = NULL; /* our inputName currently is always "imuxsock", and this will hold it */
-static int startIndexUxLocalSockets; /* process funix from that index on (used to
+/* structure to describe a specific listener */
+typedef struct lstn_s {
+ uchar *sockName; /* read-only after startup */
+ prop_t *hostName; /* host-name override - if set, use this instead of actual name */
+ int fd; /* read-only after startup */
+ int flags; /* should parser parse host name? read-only after startup */
+ int flowCtl; /* flow control settings for this socket */
+ int ratelimitInterval;
+ int ratelimitBurst;
+ intTiny ratelimitSev; /* severity level (and below) for which rate-limiting shall apply */
+ struct hashtable *ht; /* our hashtable for rate-limiting */
+ 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 bWritePid; /* write original PID into tag */
+} lstn_t;
+static lstn_t listeners[MAXFUNIX];
+
+static prop_t *pLocalHostIP = NULL; /* there is only one global IP for all internally-generated messages */
+static prop_t *pInputName = NULL; /* our inputName currently is always "imudp", and this will hold it */
+static int startIndexUxLocalSockets; /* process fd from that index on (used to
* suppress local logging. rgerhards 2005-08-01
* read-only after startup
*/
-static int funixParseHost[MAXFUNIX] = { 0, }; /* should parser parse host name? read-only after startup */
-static int funixFlags[MAXFUNIX] = { IGNDATE, }; /* should parser parse host name? read-only after startup */
-static uchar *funixn[MAXFUNIX] = { (uchar*) _PATH_LOG }; /* read-only after startup */
-static uchar *funixHName[MAXFUNIX] = { NULL, }; /* host-name override - if set, use this instead of actual name */
-static int funixFlowCtl[MAXFUNIX] = { eFLOWCTL_NO_DELAY, }; /* flow control settings for this socket */
-static int funix[MAXFUNIX] = { -1, }; /* read-only after startup */
-static int nfunix = 1; /* number of Unix sockets open / read-only after startup */
+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 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 */
+#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_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 void
+initRatelimitState(struct rs_ratelimit_state *rs, unsigned short interval, unsigned short burst)
+{
+ rs->interval = interval;
+ rs->burst = burst;
+ rs->done = 0;
+ rs->missed = 0;
+ rs->begin = 0;
+}
+
+
+/* ratelimiting support, modelled after the linux kernel
+ * returns 1 if message is within rate limit and shall be
+ * processed, 0 otherwise.
+ * This implementation is NOT THREAD-SAFE and must not
+ * be called concurrently.
+ */
+static inline int
+withinRatelimit(struct rs_ratelimit_state *rs, time_t tt, pid_t pid)
+{
+ int ret;
+ uchar msgbuf[1024];
+
+ if(rs->interval == 0) {
+ ret = 1;
+ goto finalize_it;
+ }
+
+ assert(rs->burst != 0);
+
+ if(rs->begin == 0)
+ rs->begin = tt;
+
+ /* resume if we go out of out time window */
+ if(tt > rs->begin + rs->interval) {
+ if(rs->missed) {
+ snprintf((char*)msgbuf, sizeof(msgbuf),
+ "imuxsock lost %u messages from pid %lu due to rate-limiting",
+ rs->missed, (unsigned long) pid);
+ logmsgInternal(RS_RET_RATE_LIMITED, LOG_SYSLOG|LOG_INFO, msgbuf, 0);
+ rs->missed = 0;
+ }
+ rs->begin = 0;
+ rs->done = 0;
+ }
+
+ /* do actual limit check */
+ if(rs->burst > rs->done) {
+ rs->done++;
+ ret = 1;
+ } else {
+ if(rs->missed == 0) {
+ snprintf((char*)msgbuf, sizeof(msgbuf),
+ "imuxsock begins to drop messages from pid %lu due to rate-limiting",
+ (unsigned long) pid);
+ logmsgInternal(RS_RET_RATE_LIMITED, LOG_SYSLOG|LOG_INFO, msgbuf, 0);
+ }
+ rs->missed++;
+ ret = 0;
+ }
+
+finalize_it:
+ return ret;
+}
/* set the timestamp ignore / not ignore option for the system
@@ -98,7 +243,7 @@ static int bIgnoreTimestamp = 1; /* ignore timestamps present in the incoming me
static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal, int iNewVal)
{
DEFiRet;
- funixFlags[0] = iNewVal ? IGNDATE : NOFLAG;
+ listeners[0].flags = iNewVal ? IGNDATE : NOFLAG;
RETiRet;
}
@@ -107,7 +252,7 @@ static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal,
static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int iNewVal)
{
DEFiRet;
- funixFlowCtl[0] = iNewVal ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
+ listeners[0].flowCtl = iNewVal ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
RETiRet;
}
@@ -119,74 +264,337 @@ static rsRetVal setSystemLogFlowControl(void __attribute__((unused)) *pVal, int
* rgerhards, 2007-12-20
* added capability to specify hostname for socket -- rgerhards, 2008-08-01
*/
-static rsRetVal addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal)
+static rsRetVal
+addLstnSocketName(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
- if(nfunix < MAXFUNIX) {
+ DEFiRet;
+
+ if(nfd < MAXFUNIX) {
if(*pNewVal == ':') {
- funixParseHost[nfunix] = 1;
+ listeners[nfd].bParseHost = 1;
+ } else {
+ listeners[nfd].bParseHost = 0;
}
- else {
- funixParseHost[nfunix] = 0;
+ CHKiRet(prop.Construct(&(listeners[nfd].hostName)));
+ if(pLogHostName == NULL) {
+ CHKiRet(prop.SetString(listeners[nfd].hostName, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())));
+ } else {
+ CHKiRet(prop.SetString(listeners[nfd].hostName, pLogHostName, ustrlen(pLogHostName)));
+ /* reset hostname for next socket */
+ free(pLogHostName);
+ pLogHostName = NULL;
}
- funixHName[nfunix] = pLogHostName;
- pLogHostName = NULL; /* re-init for next, not freed because funixHName[] now owns it */
- funixFlowCtl[nfunix] = bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY;
- funixFlags[nfunix] = bIgnoreTimestamp ? IGNDATE : NOFLAG;
- funixn[nfunix++] = pNewVal;
- }
- else {
+ CHKiRet(prop.ConstructFinalize(listeners[nfd].hostName));
+ if(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 */
+ dbgprintf("imuxsock: turning off rate limiting because we could not "
+ "create hash table\n");
+ 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;
+ nfd++;
+ } else {
errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n",
pNewVal);
}
- return RS_RET_OK;
+finalize_it:
+ RETiRet;
}
-/* free the funixn[] socket names - needed as cleanup on several places
- * note that nfunix is NOT reset! funixn[0] is never freed, as it comes from
+
+/* discard all log sockets except for "socket" 0. Data for it comes from
* the constant memory pool - and if not, it is freeed via some other pointer.
*/
-static rsRetVal discardFunixn(void)
+static rsRetVal discardLogSockets(void)
{
int i;
- for (i = 1; i < nfunix; i++) {
- if(funixn[i] != NULL) {
- free(funixn[i]);
- funixn[i] = NULL;
+ for (i = 1; i < nfd; i++) {
+ if(listeners[i].sockName != NULL) {
+ free(listeners[i].sockName);
+ listeners[i].sockName = NULL;
}
- if(funixHName[i] != NULL) {
- free(funixHName[i]);
- funixHName[i] = NULL;
+ if(listeners[i].hostName != NULL) {
+ prop.Destruct(&(listeners[i].hostName));
+ }
+ if(listeners[i].ht != NULL) {
+ hashtable_destroy(listeners[i].ht, 1); /* 1 => free all values automatically */
}
}
-
+
return RS_RET_OK;
}
-static int create_unix_socket(const char *path)
+/* used to create a log socket if NOT passed in via systemd.
+ */
+static inline rsRetVal
+createLogSocket(lstn_t *pLstn)
{
struct sockaddr_un sunx;
- int fd;
-
- if (path[0] == '\0')
- return -1;
-
- unlink(path);
+ DEFiRet;
+ unlink((char*)pLstn->sockName);
memset(&sunx, 0, sizeof(sunx));
sunx.sun_family = AF_UNIX;
- (void) strncpy(sunx.sun_path, path, sizeof(sunx.sun_path));
- fd = socket(AF_UNIX, SOCK_DGRAM, 0);
- if (fd < 0 || bind(fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx)) < 0 ||
- chmod(path, 0666) < 0) {
- errmsg.LogError(errno, NO_ERRCODE, "connot create '%s'", path);
- dbgprintf("cannot create %s (%d).\n", path, errno);
- close(fd);
+ if(pLstn->bCreatePath) {
+ makeFileParentDirs((uchar*)pLstn->sockName, ustrlen(pLstn->sockName), 0755, -1, -1, 0);
+ }
+ strncpy(sunx.sun_path, (char*)pLstn->sockName, sizeof(sunx.sun_path));
+ pLstn->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if(pLstn->fd < 0 || bind(pLstn->fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx)) < 0 ||
+ chmod((char*)pLstn->sockName, 0666) < 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "connot create '%s'", pLstn->sockName);
+ dbgprintf("cannot create %s (%d).\n", pLstn->sockName, errno);
+ close(pLstn->fd);
+ pLstn->fd = -1;
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+finalize_it:
+ RETiRet;
+}
+
+
+static inline rsRetVal
+openLogSocket(lstn_t *pLstn)
+{
+ DEFiRet;
+ int one;
+
+ if(pLstn->sockName[0] == '\0')
return -1;
+
+ pLstn->fd = -1;
+
+ if (sd_fds > 0) {
+ /* Check if the current socket is a systemd activated one.
+ * If so, just use it.
+ */
+ int fd;
+
+ for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + sd_fds; fd++) {
+ if( sd_is_socket_unix(fd, SOCK_DGRAM, -1, (const char*) pLstn->sockName, 0) == 1) {
+ /* ok, it matches -- just use as is */
+ pLstn->fd = fd;
+
+ dbgprintf("imuxsock: Acquired UNIX socket '%s' (fd %d) from systemd.\n",
+ pLstn->sockName, pLstn->fd);
+ break;
+ }
+ /*
+ * otherwise it either didn't matched *this* socket and
+ * we just continue to check the next one or there were
+ * an error and we will create a new socket bellow.
+ */
+ }
+ }
+
+ if (pLstn->fd == -1) {
+ CHKiRet(createLogSocket(pLstn));
+ }
+
+# if HAVE_SCM_CREDENTIALS
+ if(pLstn->bUseCreds) {
+ one = 1;
+ if(setsockopt(pLstn->fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t) sizeof(one)) != 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "set SO_PASSCRED failed on '%s'", pLstn->sockName);
+ pLstn->bUseCreds = 0;
+ }
+ if(setsockopt(pLstn->fd, SOL_SOCKET, SCM_CREDENTIALS, &one, sizeof(one)) != 0) {
+ errmsg.LogError(errno, NO_ERRCODE, "set SCM_CREDENTIALS failed on '%s'", pLstn->sockName);
+ pLstn->bUseCreds = 0;
+ }
+ }
+# else /* HAVE_SCM_CREDENTIALS */
+ pLstn->bUseCreds = 0;
+# endif /* HAVE_SCM_CREDENTIALS */
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ close(pLstn->fd);
+ pLstn->fd = -1;
+ }
+
+ RETiRet;
+}
+
+
+/* find ratelimiter to use for this message. Currently, we use the
+ * pid, but may change to cgroup later (probably via a config switch).
+ * Returns NULL if not found or rate-limiting not activated for this
+ * listener (the latter being a performance enhancement).
+ */
+static inline rsRetVal
+findRatelimiter(lstn_t *pLstn, struct ucred *cred, rs_ratelimit_state_t **prl)
+{
+ rs_ratelimit_state_t *rl;
+ int r;
+ pid_t *keybuf;
+ DEFiRet;
+
+ if(cred == NULL)
+ FINALIZE;
+ if(pLstn->ratelimitInterval == 0) {
+ *prl = NULL;
+ FINALIZE;
+ }
+
+ rl = hashtable_search(pLstn->ht, &cred->pid);
+ if(rl == NULL) {
+ /* we need to add a new ratelimiter, process not seen before! */
+ dbgprintf("imuxsock: no ratelimiter for pid %lu, creating one\n",
+ (unsigned long) cred->pid);
+ STATSCOUNTER_INC(ctrNumRatelimiters, mutCtrNumRatelimiters);
+ CHKmalloc(rl = malloc(sizeof(rs_ratelimit_state_t)));
+ CHKmalloc(keybuf = malloc(sizeof(pid_t)));
+ *keybuf = cred->pid;
+ initRatelimitState(rl, pLstn->ratelimitInterval, pLstn->ratelimitBurst);
+ r = hashtable_insert(pLstn->ht, keybuf, rl);
+ if(r == 0)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
}
- return fd;
+
+ *prl = rl;
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* patch correct pid into tag. bufTAG MUST be CONF_TAG_MAXSIZE long!
+ */
+static inline void
+fixPID(uchar *bufTAG, int *lenTag, struct ucred *cred)
+{
+ int i;
+ char bufPID[16];
+ int lenPID;
+
+ if(cred == NULL)
+ return;
+
+ lenPID = snprintf(bufPID, sizeof(bufPID), "[%lu]:", (unsigned long) cred->pid);
+
+ for(i = *lenTag ; i >= 0 && bufTAG[i] != '[' ; --i)
+ /*JUST SKIP*/;
+
+ if(i < 0)
+ i = *lenTag - 1; /* go right at end of TAG, pid was not present (-1 for ':') */
+
+ if(i + lenPID > CONF_TAG_MAXSIZE)
+ return; /* do not touch, as things would break */
+
+ memcpy(bufTAG + i, bufPID, lenPID);
+ *lenTag = i + lenPID;
+}
+
+
+/* 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)
+{
+ msg_t *pMsg;
+ int lenMsg;
+ int offs;
+ int i;
+ uchar *parse;
+ int pri;
+ int facil;
+ int sever;
+ uchar bufParseTAG[CONF_TAG_MAXSIZE];
+ struct syslogTime st;
+ time_t tt;
+ rs_ratelimit_state_t *ratelimiter = NULL;
+ DEFiRet;
+
+ /* TODO: handle format errors?? */
+ /* we need to parse the pri first, because we need the severity for
+ * rate-limiting as well.
+ */
+ parse = pRcv;
+ lenMsg = lenRcv;
+ offs = 1; /* '<' */
+
+ parse++;
+ pri = 0;
+ while(offs < lenMsg && isdigit(*parse)) {
+ pri = pri * 10 + *parse - '0';
+ ++parse;
+ ++offs;
+ }
+ facil = LOG_FAC(pri);
+ sever = LOG_PRI(pri);
+
+ if(sever >= pLstn->ratelimitSev) {
+ /* note: if cred == NULL, then ratelimiter == NULL as well! */
+ findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */
+ }
+
+ datetime.getCurrTime(&st, &tt);
+ if(ratelimiter != NULL && !withinRatelimit(ratelimiter, tt, cred->pid)) {
+ STATSCOUNTER_INC(ctrLostRatelimit, mutCtrLostRatelimit);
+ FINALIZE;
+ }
+
+ /* we now create our own message object and submit it to the queue */
+ CHKiRet(msgConstructWithTime(&pMsg, &st, tt));
+ MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv);
+ parser.SanitizeMsg(pMsg);
+ lenMsg = pMsg->iLenRawMsg - offs;
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetFlowControlType(pMsg, pLstn->flowCtl);
+
+ pMsg->iFacility = facil;
+ pMsg->iSeverity = sever;
+ MsgSetAfterPRIOffs(pMsg, offs);
+
+ parse++; lenMsg--; /* '>' */
+
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) {
+ DBGPRINTF("we have a problem, invalid timestamp in msg!\n");
+ }
+
+ /* pull tag */
+
+ i = 0;
+ while(lenMsg > 0 && *parse != ' ' && i < CONF_TAG_MAXSIZE) {
+ bufParseTAG[i++] = *parse++;
+ --lenMsg;
+ }
+ bufParseTAG[i] = '\0'; /* terminate string */
+ if(pLstn->bWritePid)
+ fixPID(bufParseTAG, &i, cred);
+ MsgSetTAG(pMsg, bufParseTAG, i);
+
+ MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg);
+
+ if(pLstn->bParseHost) {
+ pMsg->msgFlags = pLstn->flags | PARSE_HOSTNAME;
+ } else {
+ pMsg->msgFlags = pLstn->flags;
+ }
+
+ MsgSetRcvFrom(pMsg, pLstn->hostName);
+ CHKiRet(MsgSetRcvFromIP(pMsg, pLocalHostIP));
+ CHKiRet(submitMsg(pMsg));
+
+ STATSCOUNTER_INC(ctrSubmit, mutCtrSubmit);
+finalize_it:
+ RETiRet;
}
@@ -197,15 +605,22 @@ static int create_unix_socket(const char *path)
* of the socket which is to be processed. This eases access to the
* growing number of properties. -- rgerhards, 2008-08-01
*/
-static rsRetVal readSocket(int fd, int iSock)
+static rsRetVal readSocket(lstn_t *pLstn)
{
DEFiRet;
int iRcvd;
int iMaxLine;
+ struct msghdr msgh;
+ struct iovec msgiov;
+# if HAVE_SCM_CREDENTIALS
+ struct cmsghdr *cm;
+# endif
+ struct ucred *cred;
uchar bufRcv[4096+1];
+ char aux[128];
uchar *pRcv = NULL; /* receive buffer */
- assert(iSock >= 0);
+ assert(pLstn->fd >= 0);
iMaxLine = glbl.GetMaxLine();
@@ -218,21 +633,47 @@ static rsRetVal readSocket(int fd, int iSock)
if((size_t) iMaxLine < sizeof(bufRcv) - 1) {
pRcv = bufRcv;
} else {
- CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1)));
+ CHKmalloc(pRcv = (uchar*) MALLOC(sizeof(uchar) * (iMaxLine + 1)));
}
- iRcvd = recv(fd, pRcv, iMaxLine, 0);
- dbgprintf("Message from UNIX socket: #%d\n", fd);
- if (iRcvd > 0) {
- parseAndSubmitMessage(funixHName[iSock] == NULL ? glbl.GetLocalHostName() : funixHName[iSock],
- (uchar*)"127.0.0.1", pRcv,
- iRcvd, funixParseHost[iSock] ? (funixFlags[iSock] | PARSE_HOSTNAME) : funixFlags[iSock],
- funixFlowCtl[iSock], pInputName, NULL, 0);
- } else if (iRcvd < 0 && errno != EINTR) {
+ memset(&msgh, 0, sizeof(msgh));
+ memset(&msgiov, 0, sizeof(msgiov));
+# if HAVE_SCM_CREDENTIALS
+ if(pLstn->bUseCreds) {
+ memset(&aux, 0, sizeof(aux));
+ msgh.msg_control = aux;
+ msgh.msg_controllen = sizeof(aux);
+ }
+# endif
+ msgiov.iov_base = pRcv;
+ msgiov.iov_len = iMaxLine;
+ msgh.msg_iov = &msgiov;
+ msgh.msg_iovlen = 1;
+ iRcvd = recvmsg(pLstn->fd, &msgh, MSG_DONTWAIT);
+
+ 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) {
+ cred = (struct ucred*) CMSG_DATA(cm);
+ dbgprintf("XXX: got credentials pid %d\n", (int) cred->pid);
+ break;
+ }
+ }
+ dbgprintf("XXX: post CM loop\n");
+ }
+# endif /* HAVE_SCM_CREDENTIALS */
+ CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred));
+ } else if(iRcvd < 0 && errno != EINTR) {
char errStr[1024];
rs_strerror_r(errno, errStr, sizeof(errStr));
dbgprintf("UNIX socket error: %d = %s.\n", errno, errStr);
- errmsg.LogError(errno, NO_ERRCODE, "recvfrom UNIX");
+ errmsg.LogError(errno, NO_ERRCODE, "imuxsock: recvfrom UNIX");
}
finalize_it:
@@ -249,7 +690,13 @@ BEGINrunInput
int nfds;
int i;
int fd;
- fd_set readfds;
+#ifdef USE_UNLIMITED_SELECT
+ fd_set *pReadfds = malloc(glbl.GetFdSetSize());
+#else
+ fd_set readfds;
+ fd_set *pReadfds = &readfds;
+#endif
+
CODESTARTrunInput
/* this is an endless loop - it is terminated when the thread is
* signalled to do so. This, however, is handled by the framework,
@@ -263,34 +710,41 @@ CODESTARTrunInput
* is given without -a, we do not need to listen at all..
*/
maxfds = 0;
- FD_ZERO (&readfds);
+ FD_ZERO (pReadfds);
/* Copy master connections */
- for (i = startIndexUxLocalSockets; i < nfunix; i++) {
- if (funix[i] != -1) {
- FD_SET(funix[i], &readfds);
- if (funix[i]>maxfds) maxfds=funix[i];
+ for (i = startIndexUxLocalSockets; i < nfd; i++) {
+ if (listeners[i].fd!= -1) {
+ FD_SET(listeners[i].fd, pReadfds);
+ if(listeners[i].fd > maxfds)
+ maxfds=listeners[i].fd;
}
}
if(Debug) {
dbgprintf("--------imuxsock calling select, active file descriptors (max %d): ", maxfds);
for (nfds= 0; nfds <= maxfds; ++nfds)
- if ( FD_ISSET(nfds, &readfds) )
+ if ( FD_ISSET(nfds, pReadfds) )
dbgprintf("%d ", nfds);
dbgprintf("\n");
}
/* wait for io to become ready */
- nfds = select(maxfds+1, (fd_set *) &readfds, NULL, NULL, NULL);
-
- for (i = 0; i < nfunix && nfds > 0; i++) {
- if ((fd = funix[i]) != -1 && FD_ISSET(fd, &readfds)) {
- readSocket(fd, i);
+ nfds = select(maxfds+1, (fd_set *) pReadfds, NULL, NULL, NULL);
+ if(glbl.GetGlobalInputTermState() == 1)
+ break; /* terminate input! */
+
+ for (i = 0; i < nfd && nfds > 0; i++) {
+ if(glbl.GetGlobalInputTermState() == 1)
+ ABORT_FINALIZE(RS_RET_FORCE_TERM); /* terminate input! */
+ if ((fd = listeners[i].fd) != -1 && FD_ISSET(fd, pReadfds)) {
+ readSocket(&(listeners[i]));
--nfds; /* indicate we have processed one */
}
}
}
+finalize_it:
+ freeFdSet(pReadfds);
RETiRet;
ENDrunInput
@@ -298,16 +752,53 @@ ENDrunInput
BEGINwillRun
CODESTARTwillRun
register int i;
+ int actSocks;
/* first apply some config settings */
- startIndexUxLocalSockets = bOmitLocalLogging ? 1 : 0;
+# 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)
- funixn[0] = pLogSockName;
+ listeners[0].sockName = pLogSockName;
+ 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 */
- for (i = startIndexUxLocalSockets ; i < nfunix ; i++) {
- if ((funix[i] = create_unix_socket((char*) funixn[i])) != -1)
- dbgprintf("Opened UNIX socket '%s' (fd %d).\n", funixn[i], funix[i]);
+ 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) */
@@ -324,40 +815,63 @@ CODESTARTafterRun
int i;
/* do cleanup here */
/* Close the UNIX sockets. */
- for (i = 0; i < nfunix; i++)
- if (funix[i] != -1)
- close(funix[i]);
-
- /* Clean-up files. */
- for (i = 0; i < nfunix; i++)
- if (funixn[i] && funix[i] != -1)
- unlink((char*) funixn[i]);
+ for (i = 0; i < nfd; i++)
+ if (listeners[i].fd != -1)
+ close(listeners[i].fd);
+
+ /* 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.
+ */
+ if (sd_fds > 0 &&
+ listeners[i].fd >= SD_LISTEN_FDS_START &&
+ listeners[i].fd < SD_LISTEN_FDS_START + sd_fds)
+ continue;
+
+ DBGPRINTF("imuxsock: unlinking unix socket file[%d] %s\n", i, listeners[i].sockName);
+ unlink((char*) listeners[i].sockName);
+ }
/* free no longer needed string */
- if(pLogSockName != NULL)
- free(pLogSockName);
- if(pLogHostName != NULL) {
- free(pLogHostName);
- }
+ free(pLogSockName);
+ free(pLogHostName);
- discardFunixn();
- nfunix = 1;
+ discardLogSockets();
+ nfd = 1;
if(pInputName != NULL)
prop.Destruct(&pInputName);
+
ENDafterRun
BEGINmodExit
CODESTARTmodExit
+ statsobj.Destruct(&modStats);
+
+ objRelease(parser, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
objRelease(prop, CORE_COMPONENT);
+ objRelease(statsobj, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
ENDmodExit
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
@@ -372,10 +886,19 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
pLogHostName = NULL;
}
- discardFunixn();
- nfunix = 1;
+ 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;
return RS_RET_OK;
}
@@ -389,15 +912,37 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
CHKiRet(objUse(prop, CORE_COMPONENT));
+ CHKiRet(objUse(statsobj, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
dbgprintf("imuxsock version %s initializing\n", PACKAGE_VERSION);
- /* initialize funixn[] array */
+ /* init system log socket settings */
+ listeners[0].flags = IGNDATE;
+ listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG);
+ listeners[0].hostName = NULL;
+ listeners[0].flowCtl = eFLOWCTL_NO_DELAY;
+ listeners[0].fd = -1;
+ listeners[0].bParseHost = 0;
+ listeners[0].bUseCreds = 0;
+ listeners[0].bCreatePath = 0;
+
+ /* initialize socket names */
for(i = 1 ; i < MAXFUNIX ; ++i) {
- funixn[i] = NULL;
- funix[i] = -1;
+ listeners[i].sockName = NULL;
+ listeners[i].fd = -1;
}
+ CHKiRet(prop.Construct(&pLocalHostIP));
+ CHKiRet(prop.SetString(pLocalHostIP, UCHAR_CONSTANT("127.0.0.1"), sizeof("127.0.0.1") - 1));
+ CHKiRet(prop.ConstructFinalize(pLocalHostIP));
+
+ /* now init listen socket zero, the local log socket */
+ CHKiRet(prop.Construct(&(listeners[0].hostName)));
+ CHKiRet(prop.SetString(listeners[0].hostName, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName())));
+ CHKiRet(prop.ConstructFinalize(listeners[0].hostName));
+
/* register config file handlers */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"omitlocallogging", 0, eCmdHdlrBinary,
NULL, &bOmitLocalLogging, STD_LOADABLE_MODULE_ID));
@@ -409,8 +954,18 @@ CODEmodInit_QueryRegCFSLineHdlr
NULL, &pLogHostName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary,
NULL, &bUseFlowCtl, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary,
+ NULL, &bCreatePath, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusepidfromsystem", 0, eCmdHdlrBinary,
+ NULL, &bWritePid, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"addunixlistensocket", 0, eCmdHdlrGetWord,
addLstnSocketName, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitinterval", 0, eCmdHdlrInt,
+ NULL, &ratelimitInterval, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitburst", 0, eCmdHdlrInt,
+ NULL, &ratelimitBurst, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitseverity", 0, eCmdHdlrInt,
+ NULL, &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
@@ -423,6 +978,26 @@ CODEmodInit_QueryRegCFSLineHdlr
setSystemLogTimestampIgnore, NULL, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary,
setSystemLogFlowControl, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary,
+ NULL, &bWritePidSysSock, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt,
+ NULL, &ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt,
+ NULL, &ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt,
+ NULL, &ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID));
+
+ /* support statistics gathering */
+ CHKiRet(statsobj.Construct(&modStats));
+ CHKiRet(statsobj.SetName(modStats, UCHAR_CONSTANT("imuxsock")));
+ CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("submitted"),
+ ctrType_IntCtr, &ctrSubmit));
+ CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.discarded"),
+ ctrType_IntCtr, &ctrLostRatelimit));
+ CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.numratelimiters"),
+ ctrType_IntCtr, &ctrNumRatelimiters));
+ CHKiRet(statsobj.ConstructFinalize(modStats));
+
ENDmodInit
/* vim:set ai:
*/
diff --git a/plugins/mmsnmptrapd/Makefile.am b/plugins/mmsnmptrapd/Makefile.am
new file mode 100644
index 00000000..ca027ca7
--- /dev/null
+++ b/plugins/mmsnmptrapd/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = mmsnmptrapd.la
+
+mmsnmptrapd_la_SOURCES = mmsnmptrapd.c
+mmsnmptrapd_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+mmsnmptrapd_la_LDFLAGS = -module -avoid-version
+mmsnmptrapd_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/mmsnmptrapd/mmsnmptrapd.c b/plugins/mmsnmptrapd/mmsnmptrapd.c
new file mode 100644
index 00000000..767829d6
--- /dev/null
+++ b/plugins/mmsnmptrapd/mmsnmptrapd.c
@@ -0,0 +1,430 @@
+/* mmsnmptrapd.c
+ * This is a message modification module. It takes messages generated
+ * from snmptrapd and modifies them so that the look like they
+ * originated from the real originator.
+ *
+ * NOTE: read comments in module-template.h for details on the calling interface!
+ *
+ * File begun on 2011-05-05 by RGerhards
+ *
+ * Copyright 2011 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 <ctype.h>
+#include "conf.h"
+#include "msg.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "cfsysline.h"
+#include "unicode-helper.h"
+#include "dirty.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);
+
+/* static data */
+DEFobjCurrIf(errmsg);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+struct severMap_s {
+ uchar *name;
+ int code;
+ struct severMap_s *next;
+};
+
+typedef struct _instanceData {
+ uchar *pszTagName;
+ uchar *pszTagID; /* chaced: name plus trailing shlash (for compares) */
+ int lenTagID; /* cached length of tag ID, for performance reasons */
+ struct severMap_s *severMap;
+} instanceData;
+
+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;
+configSettings_t cs;
+
+//TODO: enable for v6
+#if 0
+SCOPING_SUPPORT; /* must be set AFTER configSettings_t is defined */
+
+BEGINinitConfVars /* (re)set config variables to default values */
+CODESTARTinitConfVars
+ cs.pszTagName = NULL;
+ cs.pszSeverityMapping = NULL;
+ resetConfigVariables(NULL, NULL);
+ENDinitConfVars
+#endif
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+ struct severMap_s *node, *nodeDel;
+CODESTARTfreeInstance
+ for(node = pData->severMap ; node != NULL ; ) {
+ nodeDel = node;
+ node = node->next;
+ free(nodeDel->name);
+ free(nodeDel);
+ }
+ free(pData->pszTagName);
+ free(pData->pszTagID);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("mmsnmptrapd\n");
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+
+/* check if a string is numeric (int) */
+static inline int
+isNumeric(uchar *str)
+{
+ int r = 1;
+ if(*str == '-' || *str == '+')
+ ++str;
+ while(*str) {
+ if(!isdigit(*str)) {
+ r = 0;
+ goto done;
+ }
+ ++str;
+ }
+done:
+ return r;
+}
+
+/* get a substring delimited by a character (or end of string). The
+ * string is trimmed, that is leading and trailing spaces are removed.
+ * The caller must provide a buffer which shall receive the substring.
+ * String length is returned as result. The input string is updated
+ * on exit, so that it may be used for another query starting at that
+ * position.
+ */
+static int
+getSubstring(uchar **psrc, uchar delim, uchar *dst, int lenDst)
+{
+ uchar *dstwrk = dst;
+ uchar *src = *psrc;
+ while(*src && isspace(*src)) {
+ ++src; /* trim leading spaces */
+ }
+ while(*src && *src != delim && --lenDst > 0) {
+ *dstwrk++ = *src++;
+ }
+ dstwrk--;
+ while(dstwrk > dst && isspace(*dst))
+ --dstwrk; /* trim trailing spaces */
+ *++dstwrk = '\0';
+
+ /* final results */
+ if(*src == delim)
+ ++src;
+ *psrc = src;
+ return(dstwrk - dst);
+}
+
+
+/* get string up to the next SP or '/'. Stops at max size.
+ * dst, lenDst (receive buffer) must be given. lenDst is
+ * max length on entry and actual length on exit.
+ */
+static int
+getTagComponent(uchar *tag, uchar *dst, int *lenDst)
+{
+ int end = *lenDst - 1; /* -1 for NUL-char! */
+ int i;
+
+ i = 0;
+ if(tag[i] != '/')
+ goto done;
+ ++tag;
+ while(i < end && tag[i] != '\0' && tag[i] != ' ' && tag[i] != '/') {
+ dst[i] = tag[i];
+ ++i;
+ }
+ dst[i] = '\0';
+dbgprintf("XXXX: getTagComponent dst on output: '%s', len %d\n", dst, i);
+ *lenDst = i;
+done:
+ return i;
+}
+
+
+/* lookup severity code based on provided severity
+ * returns -1 if severity could not be found.
+ */
+static inline int
+lookupSeverityCode(instanceData *pData, uchar *sever)
+{
+ struct severMap_s *node;
+ int sevCode = -1;
+
+ for(node = pData->severMap ; node != NULL ; node = node->next) {
+ if(!ustrcmp(node->name, sever)) {
+ sevCode = node->code;
+ break;
+ }
+ }
+ return sevCode;
+}
+
+
+BEGINdoAction
+ int lenTAG;
+ int lenSever;
+ int lenHost;
+ int sevCode;
+ msg_t *pMsg;
+ uchar *pszTag;
+ uchar pszSever[512];
+ uchar pszHost[512];
+CODESTARTdoAction
+ pMsg = (msg_t*) ppString[0];
+ dbgprintf("XXXX: mmsnmptrapd called with pMsg %p\n", pMsg);
+ getTAG(pMsg, &pszTag, &lenTAG);
+ if(strncmp((char*)pszTag, (char*)pData->pszTagID, pData->lenTagID)) {
+ DBGPRINTF("tag '%s' not matching, mmsnmptrapd ignoring this message\n",
+ pszTag);
+ FINALIZE;
+ }
+
+ lenSever = sizeof(pszSever);
+dbgprintf("XXXX: pszTag: '%s', lenID %d\n", pszTag, pData->lenTagID);
+ getTagComponent(pszTag+pData->lenTagID-1, pszSever, &lenSever);
+ lenHost = sizeof(pszHost);
+ getTagComponent(pszTag+pData->lenTagID+lenSever, pszHost, &lenHost);
+ dbgprintf("XXXX: mmsnmptrapd sever '%s'(%d), host '%s'(%d)\n", pszSever, lenSever, pszHost,lenHost);
+
+ if(pszHost[lenHost-1] == ':') {
+ pszHost[lenHost-1] = '\0';
+ --lenHost;
+ }
+ sevCode = lookupSeverityCode(pData, pszSever);
+dbgprintf("XXXX: severity for message is %d\n", sevCode);
+ /* now apply new settings */
+ MsgSetTAG(pMsg, pData->pszTagName, pData->lenTagID);
+ MsgSetHOSTNAME(pMsg, pszHost, lenHost);
+ if(sevCode != -1)
+ pMsg->iSeverity = sevCode; /* we update like the parser does! */
+finalize_it:
+ENDdoAction
+
+
+/* Build the severity mapping table based on user-provided configuration
+ * settings.
+ */
+static inline rsRetVal
+buildSeverityMapping(instanceData *pData)
+{
+ uchar pszSev[512];
+ uchar pszSevCode[512];
+ int sevCode;
+ uchar *mapping;
+ struct severMap_s *node;
+ DEFiRet;
+
+ mapping = cs.pszSeverityMapping;
+
+ while(1) { /* broken inside when all entries are processed */
+ if(getSubstring(&mapping, '/', pszSev, sizeof(pszSev)) == 0) {
+ FINALIZE;
+ }
+ if(getSubstring(&mapping, ',', pszSevCode, sizeof(pszSevCode)) == 0) {
+ errmsg.LogError(0, RS_RET_ERR, "error: invalid severity mapping, cannot "
+ "extract code. given: '%s'\n", cs.pszSeverityMapping);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ sevCode = atoi((char*) pszSevCode);
+ if(!isNumeric(pszSevCode))
+ sevCode = -1;
+ if(sevCode < 0 || sevCode > 7) {
+ errmsg.LogError(0, RS_RET_ERR, "error: severity code %d outside of valid "
+ "range 0..7 (was string '%s')\n", sevCode, pszSevCode);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+ CHKmalloc(node = MALLOC(sizeof(struct severMap_s)));
+ CHKmalloc(node->name = ustrdup(pszSev));
+ node->code = sevCode;
+ /* we enqueue at the top, so the two lines below do all we need! */
+ node->next = pData->severMap;
+ pData->severMap = node;
+ DBGPRINTF("mmsnmptrapd: severity string '%s' mapped to code %d\n",
+ pszSev, sevCode);
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":mmsnmptrapd:", sizeof(":mmsnmptrapd:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":mmsnmptrapd:") - 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(cs.pszTagName == NULL) {
+ pData->pszTagName = (uchar*) strdup("snmptrapd:");
+ pData->pszTagID = (uchar*) strdup("snmptrapd/");
+ } else {
+ int lenTag = ustrlen(cs.pszTagName);
+ /* new tag value (with colon at the end) */
+ CHKmalloc(pData->pszTagName = MALLOC(lenTag + 2));
+ memcpy(pData->pszTagName, cs.pszTagName, lenTag);
+ memcpy(pData->pszTagName+lenTag, ":", 2);
+ /* tag ID for comparisions */
+ CHKmalloc(pData->pszTagID = MALLOC(lenTag + 2));
+ memcpy(pData->pszTagID, cs.pszTagName, lenTag);
+ memcpy(pData->pszTagID+lenTag, "/", 2);
+ free(cs.pszTagName); /* no longer needed */
+ }
+ pData->lenTagID = ustrlen(pData->pszTagID);
+ if(cs.pszSeverityMapping != NULL) {
+ CHKiRet(buildSeverityMapping(pData));
+ }
+
+ /* all config vars auto-reset! */
+ cs.pszTagName = NULL;
+ free(cs.pszSeverityMapping);
+ cs.pszSeverityMapping = NULL;
+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;
+ free(cs.pszTagName);
+ cs.pszTagName = NULL;
+ free(cs.pszSeverityMapping);
+ cs.pszSeverityMapping = NULL;
+ RETiRet;
+}
+
+
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bMsgPassingSupported;
+CODESTARTmodInit
+//TODO v6: add SCOPINGmodInit
+ *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("mmsnmptrapd: msg-passing is not supported by rsyslog core, "
+ "can not continue.\n");
+ ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
+ }
+
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ /* TODO: config vars ininit can be replaced by commented-out code above in v6 */
+ cs.pszTagName = NULL;
+ cs.pszSeverityMapping = NULL;
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdtag", 0, eCmdHdlrInt,
+ NULL, &cs.pszTagName, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"mmsnmptrapdseveritymapping", 0, eCmdHdlrGetWord,
+ NULL, &cs.pszSeverityMapping, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/omdbalerting/Makefile.am b/plugins/omdbalerting/Makefile.am
new file mode 100644
index 00000000..becf29b0
--- /dev/null
+++ b/plugins/omdbalerting/Makefile.am
@@ -0,0 +1,8 @@
+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
new file mode 100644
index 00000000..35de5818
--- /dev/null
+++ b/plugins/omdbalerting/omdbalerting.c
@@ -0,0 +1,145 @@
+/* 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/omgssapi/omgssapi.c b/plugins/omgssapi/omgssapi.c
index 782ac22f..e4fdf0c0 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -58,6 +58,7 @@
#include "errmsg.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
@@ -193,7 +194,7 @@ static rsRetVal TCPSendGSSInit(void *pvData)
base = (gss_base_service_name == NULL) ? "host" : gss_base_service_name;
out_tok.length = strlen(pData->f_hname) + strlen(base) + 2;
- CHKmalloc(out_tok.value = malloc(out_tok.length));
+ CHKmalloc(out_tok.value = MALLOC(out_tok.length));
strcpy(out_tok.value, base);
strcat(out_tok.value, "@");
strcat(out_tok.value, pData->f_hname);
@@ -409,13 +410,13 @@ CODESTARTdoAction
* hard-coded but this may be changed to a config parameter.
* rgerhards, 2006-11-30
*/
- if(pData->compressionLevel && (l > MIN_SIZE_FOR_COMPRESS)) {
+ if(pData->compressionLevel && (l > CONF_MIN_SIZE_FOR_COMPRESS)) {
Bytef *out;
uLongf destLen = sizeof(out) / sizeof(Bytef);
uLong srcLen = l;
int ret;
/* TODO: optimize malloc sequence? -- rgerhards, 2008-09-02 */
- CHKmalloc(out = (Bytef*) malloc(iMaxLine + iMaxLine/100 + 12));
+ CHKmalloc(out = (Bytef*) MALLOC(iMaxLine + iMaxLine/100 + 12));
out[0] = 'z';
out[1] = '\0';
ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
@@ -563,7 +564,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
tmp = ++p;
for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
/* SKIP AND COUNT */;
- pData->port = malloc(i + 1);
+ pData->port = MALLOC(i + 1);
if(pData->port == NULL) {
errmsg.LogError(0, NO_ERRCODE, "Could not get memory to store syslog forwarding port, "
"using default port, results may not be what you intend\n");
diff --git a/plugins/omhdfs/Makefile.am b/plugins/omhdfs/Makefile.am
new file mode 100644
index 00000000..95e6b102
--- /dev/null
+++ b/plugins/omhdfs/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = omhdfs.la
+
+omhdfs_la_SOURCES = omhdfs.c
+omhdfs_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS) $(JAVA_INCLUDES)
+omhdfs_la_LDFLAGS = -module -avoid-version -lhdfs $(JAVA_LIBS)
+omhdfs_la_LIBADD = $(RSRT_LIBS)
diff --git a/plugins/omhdfs/omhdfs.c b/plugins/omhdfs/omhdfs.c
new file mode 100644
index 00000000..8b72747f
--- /dev/null
+++ b/plugins/omhdfs/omhdfs.c
@@ -0,0 +1,476 @@
+/* omhdfs.c
+ * This is an output module to support Hadoop's HDFS.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * 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 <time.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <pthread.h>
+#include <hdfs.h>
+
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "template.h"
+#include "conf.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+#include "errmsg.h"
+#include "hashtable.h"
+#include "hashtable_itr.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/* global data */
+static struct hashtable *files; /* holds all file objects that we know */
+
+/* globals for default values */
+static uchar *fileName = NULL;
+static uchar *hdfsHost = NULL;
+static uchar *dfltTplName = NULL; /* default template name to use */
+int hdfsPort = 0;
+/* end globals for default values */
+
+typedef struct {
+ uchar *name;
+ hdfsFS fs;
+ hdfsFile fh;
+ const char *hdfsHost;
+ tPort hdfsPort;
+ int nUsers;
+ pthread_mutex_t mut;
+} file_t;
+
+
+typedef struct _instanceData {
+ file_t *pFile;
+} instanceData;
+
+/* forward definitions (down here, need data types) */
+static inline rsRetVal fileClose(file_t *pFile);
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ printf("omhdfs: file:%s", pData->pFile->name);
+ENDdbgPrintInstInfo
+
+
+/* note that hdfsFileExists() does not work, so we did our
+ * own function to see if a pathname exists. Returns 0 if the
+ * file does not exists, something else otherwise. Note that
+ * we can also check a directroy (if that matters...)
+ */
+static int
+HDFSFileExists(hdfsFS fs, uchar *name)
+{
+ int r;
+ hdfsFileInfo *info;
+
+ info = hdfsGetPathInfo(fs, (char*)name);
+ /* if things go wrong, we assume it is because the file
+ * does not exist. We do not get too much information...
+ */
+ if(info == NULL) {
+ r = 0;
+ } else {
+ r = 1;
+ hdfsFreeFileInfo(info, 1);
+ }
+ return r;
+}
+
+static inline rsRetVal
+HDFSmkdir(hdfsFS fs, uchar *name)
+{
+ DEFiRet;
+ if(hdfsCreateDirectory(fs, (char*)name) == -1)
+ ABORT_FINALIZE(RS_RET_ERR);
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* ---BEGIN FILE OBJECT---------------------------------------------------- */
+/* This code handles the "file object". This is split from the actual
+ * instance data, because several instances may write into the same file.
+ * If so, we need to use a single object, and also synchronize their writes.
+ * So we keep the file object separately, and just stick a reference into
+ * the instance data.
+ */
+
+static inline rsRetVal
+fileObjConstruct(file_t **ppFile)
+{
+ file_t *pFile;
+ DEFiRet;
+
+ CHKmalloc(pFile = malloc(sizeof(file_t)));
+ pFile->name = NULL;
+ pFile->hdfsHost = NULL;
+ pFile->fh = NULL;
+ pFile->nUsers = 0;
+
+ *ppFile = pFile;
+finalize_it:
+ RETiRet;
+}
+
+static inline void
+fileObjAddUser(file_t *pFile)
+{
+ /* init mutex only when second user is added */
+ ++pFile->nUsers;
+ if(pFile->nUsers == 2)
+ pthread_mutex_init(&pFile->mut, NULL);
+ DBGPRINTF("omhdfs: file %s now being used by %d actions\n", pFile->name, pFile->nUsers);
+}
+
+static rsRetVal
+fileObjDestruct(file_t **ppFile)
+{
+ file_t *pFile = *ppFile;
+ if(pFile->nUsers > 1)
+ pthread_mutex_destroy(&pFile->mut);
+ fileClose(pFile);
+ free(pFile->name);
+ free((char*)pFile->hdfsHost);
+ free(pFile->fh);
+
+ return RS_RET_OK;
+}
+
+
+/* check, and potentially create, all names inside a path */
+static rsRetVal
+filePrepare(file_t *pFile)
+{
+ uchar *p;
+ uchar *pszWork;
+ size_t len;
+ DEFiRet;
+
+ if(HDFSFileExists(pFile->fs, pFile->name))
+ FINALIZE;
+
+ /* file does not exist, create it (and eventually parent directories */
+ if(1) { // check if bCreateDirs
+ len = ustrlen(pFile->name) + 1;
+ CHKmalloc(pszWork = MALLOC(sizeof(uchar) * len));
+ memcpy(pszWork, pFile->name, len);
+ for(p = pszWork+1 ; *p ; p++)
+ if(*p == '/') {
+ /* temporarily terminate string, create dir and go on */
+ *p = '\0';
+ if(!HDFSFileExists(pFile->fs, pszWork)) {
+ CHKiRet(HDFSmkdir(pFile->fs, pszWork));
+ }
+ *p = '/';
+ }
+ free(pszWork);
+ return 0;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* this function is to be used as destructor for the
+ * hash table code.
+ */
+static void
+fileObjDestruct4Hashtable(void *ptr)
+{
+ file_t *pFile = (file_t*) ptr;
+ fileObjDestruct(&pFile);
+}
+
+
+static inline rsRetVal
+fileOpen(file_t *pFile)
+{
+ DEFiRet;
+
+ assert(pFile->fh == NULL);
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_lock(&pFile->mut);
+
+ DBGPRINTF("omhdfs: try to connect to HDFS at host '%s', port %d\n",
+ pFile->hdfsHost, pFile->hdfsPort);
+ pFile->fs = hdfsConnect(pFile->hdfsHost, pFile->hdfsPort);
+ if(pFile->fs == NULL) {
+ DBGPRINTF("omhdfs: error can not connect to hdfs\n");
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+
+ CHKiRet(filePrepare(pFile));
+
+ pFile->fh = hdfsOpenFile(pFile->fs, (char*)pFile->name, O_WRONLY|O_APPEND, 0, 0, 0);
+ if(pFile->fh == NULL) {
+ /* maybe the file does not exist, so we try to create it now.
+ * Note that we can not use hdfsExists() because of a deficit in
+ * it: https://issues.apache.org/jira/browse/HDFS-1154
+ * As of my testing, libhdfs at least seems to return ENOENT if
+ * the file does not exist.
+ */
+ if(errno == ENOENT) {
+ DBGPRINTF("omhdfs: ENOENT trying to append to '%s', now trying create\n",
+ pFile->name);
+ pFile->fh = hdfsOpenFile(pFile->fs, (char*)pFile->name, O_WRONLY|O_CREAT, 0, 0, 0);
+ }
+ }
+ if(pFile->fh == NULL) {
+ DBGPRINTF("omhdfs: failed to open %s for writing!\n", pFile->name);
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+
+finalize_it:
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_unlock(&pFile->mut);
+ RETiRet;
+}
+
+
+static inline rsRetVal
+fileWrite(file_t *pFile, uchar *buf)
+{
+ size_t lenWrite;
+ DEFiRet;
+
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_lock(&pFile->mut);
+
+ /* open file if not open. This must be done *here* and while mutex-protected
+ * because of HUP handling (which is async to normal processing!).
+ */
+ if(pFile->fh == NULL) {
+ fileOpen(pFile);
+ if(pFile->fh == NULL) {
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ }
+
+ lenWrite = strlen((char*) buf);
+ tSize num_written_bytes = hdfsWrite(pFile->fs, pFile->fh, buf, lenWrite);
+ if((unsigned) num_written_bytes != lenWrite) {
+ errmsg.LogError(errno, RS_RET_ERR_HDFS_WRITE, "omhdfs: failed to write %s, expected %lu bytes, "
+ "written %lu\n", pFile->name, (unsigned long) lenWrite,
+ (unsigned long) num_written_bytes);
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+
+finalize_it:
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_unlock(&pFile->mut);
+ RETiRet;
+}
+
+
+static inline rsRetVal
+fileClose(file_t *pFile)
+{
+ DEFiRet;
+
+ if(pFile->fh == NULL)
+ FINALIZE;
+
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_lock(&pFile->mut);
+
+ hdfsCloseFile(pFile->fs, pFile->fh);
+ pFile->fh = NULL;
+
+ if(pFile->nUsers > 1)
+ d_pthread_mutex_unlock(&pFile->mut);
+
+finalize_it:
+ RETiRet;
+}
+
+/* ---END FILE OBJECT---------------------------------------------------- */
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ pData->pFile = NULL;
+ENDcreateInstance
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ if(pData->pFile != NULL)
+ fileObjDestruct(&pData->pFile);
+ENDfreeInstance
+
+
+BEGINtryResume
+CODESTARTtryResume
+ fileClose(pData->pFile);
+ fileOpen(pData->pFile);
+ if(pData->pFile->fh == NULL){
+ dbgprintf("omhdfs: tried to resume file %s, but still no luck...\n",
+ pData->pFile->name);
+ iRet = RS_RET_SUSPENDED;
+ }
+ENDtryResume
+
+BEGINdoAction
+CODESTARTdoAction
+ DBGPRINTF("omuxsock: action to to write to %s\n", pData->pFile->name);
+ iRet = fileWrite(pData->pFile, ppString[0]);
+ENDdoAction
+
+
+BEGINparseSelectorAct
+ file_t *pFile;
+ int r;
+ uchar *keybuf;
+CODESTARTparseSelectorAct
+
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omhdfs:", sizeof(":omhdfs:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omhdfs:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ CHKiRet(createInstance(&pData));
+ CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0,
+ (dfltTplName == NULL) ? (uchar*)"RSYSLOG_FileFormat" : dfltTplName));
+
+ if(fileName == NULL) {
+ errmsg.LogError(0, RS_RET_ERR_HDFS_OPEN, "omhdfs: no file name specified, can not continue");
+ ABORT_FINALIZE(RS_RET_FILE_NOT_SPECIFIED);
+ }
+
+ pFile = hashtable_search(files, fileName);
+ if(pFile == NULL) {
+ /* we need a new file object, this one not seen before */
+ CHKiRet(fileObjConstruct(&pFile));
+ CHKmalloc(pFile->name = fileName);
+ CHKmalloc(keybuf = ustrdup(fileName));
+ fileName = NULL; /* re-set, data passed to file object */
+ CHKmalloc(pFile->hdfsHost = strdup((hdfsHost == NULL) ? "default" : (char*) hdfsHost));
+ pFile->hdfsPort = hdfsPort;
+ fileOpen(pFile);
+ if(pFile->fh == NULL){
+ errmsg.LogError(0, RS_RET_ERR_HDFS_OPEN, "omhdfs: failed to open %s - "
+ "retrying later", pFile->name);
+ iRet = RS_RET_SUSPENDED;
+ }
+ r = hashtable_insert(files, keybuf, pFile);
+ if(r == 0)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+ fileObjAddUser(pFile);
+ pData->pFile = pFile;
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINdoHUP
+ file_t *pFile;
+ struct hashtable_itr *itr;
+CODESTARTdoHUP
+ DBGPRINTF("omhdfs: HUP received (file count %d)\n", hashtable_count(files));
+ /* Iterator constructor only returns a valid iterator if
+ * the hashtable is not empty */
+ itr = hashtable_iterator(files);
+ if(hashtable_count(files) > 0)
+ {
+ do {
+ pFile = (file_t *) hashtable_iterator_value(itr);
+ fileClose(pFile);
+ DBGPRINTF("omhdfs: HUP, closing file %s\n", pFile->name);
+ } while (hashtable_iterator_advance(itr));
+ }
+ENDdoHUP
+
+
+/* Reset config variables for this module to default values.
+ * rgerhards, 2007-07-17
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ hdfsHost = NULL;
+ hdfsPort = 0;
+ return RS_RET_OK;
+}
+
+
+BEGINmodExit
+CODESTARTmodExit
+ objRelease(errmsg, CORE_COMPONENT);
+ if(files != NULL)
+ hashtable_destroy(files, 1); /* 1 => free all values automatically */
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_doHUP
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION;
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKmalloc(files = create_hashtable(20, hash_from_string, key_equals_string,
+ fileObjDestruct4Hashtable));
+
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsfilename", 0, eCmdHdlrGetWord, NULL, &fileName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfshost", 0, eCmdHdlrGetWord, NULL, &hdfsHost, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsport", 0, eCmdHdlrInt, NULL, &hdfsPort, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omhdfsdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &dfltTplName, NULL));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+CODEmodInit_QueryRegCFSLineHdlr
+ENDmodInit
diff --git a/plugins/omlibdbi/omlibdbi.c b/plugins/omlibdbi/omlibdbi.c
index 67b1edf9..e6f3fbd9 100644
--- a/plugins/omlibdbi/omlibdbi.c
+++ b/plugins/omlibdbi/omlibdbi.c
@@ -43,6 +43,7 @@
#include "dirty.h"
#include "syslogd-types.h"
#include "cfsysline.h"
+#include "conf.h"
#include "srUtils.h"
#include "template.h"
#include "module-template.h"
@@ -50,6 +51,7 @@
#include "errmsg.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -107,6 +109,11 @@ static void closeConn(instanceData *pData)
BEGINfreeInstance
CODESTARTfreeInstance
closeConn(pData);
+ free(pData->drvrName);
+ free(pData->host);
+ free(pData->usrName);
+ free(pData->pwd);
+ free(pData->dbName);
ENDfreeInstance
@@ -170,7 +177,8 @@ static rsRetVal initConn(instanceData *pData, int bSilent)
errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi or libdbi drivers not present on this system - suspending.");
ABORT_FINALIZE(RS_RET_SUSPENDED);
} else if(iDrvrsLoaded < 0) {
- errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi could not be initialized - suspending.");
+ errmsg.LogError(0, RS_RET_SUSPENDED, "libdbi error: libdbi could not be "
+ "initialized (do you have any dbi drivers installed?) - suspending.");
ABORT_FINALIZE(RS_RET_SUSPENDED);
}
bDbiInitialized = 1; /* we are done for the rest of our existence... */
@@ -366,6 +374,7 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbipassword", 0, eCmdHdlrGetWord, NULL, &pwd, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"actionlibdbidbname", 0, eCmdHdlrGetWord, NULL, &dbName, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr( (uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ DBGPRINTF("omlibdbi compiled with version %s loaded, libdbi version %s\n", VERSION, dbi_version());
ENDmodInit
/* vim:set ai:
diff --git a/plugins/ommail/ommail.c b/plugins/ommail/ommail.c
index 3a7669c9..886513c0 100644
--- a/plugins/ommail/ommail.c
+++ b/plugins/ommail/ommail.c
@@ -50,15 +50,18 @@
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
+#include "datetime.h"
#include "glbl.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
+DEFobjCurrIf(datetime)
/* we add a little support for multiple recipients. We do this via a
* singly-linked list, enqueued from the top. -- rgerhards, 2008-08-04
@@ -478,7 +481,7 @@ mkSMTPTimestamp(uchar *pszBuf, size_t lenBuf)
static const char szDay[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static const char szMonth[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
- time(&tCurr);
+ datetime.GetTime(&tCurr);
gmtime_r(&tCurr, &tmCurr);
snprintf((char*)pszBuf, lenBuf, "Date: %s, %2d %s %4d %2d:%02d:%02d UT\r\n", szDay[tmCurr.tm_wday], tmCurr.tm_mday,
szMonth[tmCurr.tm_mon], 1900 + tmCurr.tm_year, tmCurr.tm_hour, tmCurr.tm_min, tmCurr.tm_sec);
@@ -669,6 +672,7 @@ CODESTARTmodExit
freeConfigVariables();
/* release what we no longer need */
+ objRelease(datetime, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
@@ -698,6 +702,7 @@ CODEmodInit_QueryRegCFSLineHdlr
/* tell which objects we need */
CHKiRet(objUse(errmsg, CORE_COMPONENT));
CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
dbgprintf("ommail version %s initializing\n", VERSION);
diff --git a/plugins/ommysql/ommysql.c b/plugins/ommysql/ommysql.c
index d6870a7b..f8bb4aa6 100644
--- a/plugins/ommysql/ommysql.c
+++ b/plugins/ommysql/ommysql.c
@@ -46,6 +46,7 @@
#include "cfsysline.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -60,9 +61,13 @@ typedef struct _instanceData {
char f_dbuid[_DB_MAXUNAMELEN+1]; /* DB user */
char f_dbpwd[_DB_MAXPWDLEN+1]; /* DB user's password */
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 */
} instanceData;
/* config variables */
+static uchar * pszMySQLConfigFile = NULL; /* MySQL Client Configuration File */
+static uchar * pszMySQLConfigSection = NULL; /* MySQL Client Configuration Section */
static int iSrvPort = 0; /* database server port */
@@ -91,6 +96,14 @@ static void closeMySQL(instanceData *pData)
mysql_close(pData->f_hmysql);
pData->f_hmysql = NULL;
}
+ if(pData->f_configfile!=NULL){
+ free(pData->f_configfile);
+ pData->f_configfile=NULL;
+ }
+ if(pData->f_configsection!=NULL){
+ free(pData->f_configsection);
+ pData->f_configsection=NULL;
+ }
}
BEGINfreeInstance
@@ -152,6 +165,25 @@ static rsRetVal initMySQL(instanceData *pData, int bSilent)
errmsg.LogError(0, RS_RET_SUSPENDED, "can not initialize MySQL handle");
iRet = RS_RET_SUSPENDED;
} else { /* we could get the handle, now on with work... */
+ mysql_options(pData->f_hmysql,MYSQL_READ_DEFAULT_GROUP,((pData->f_configsection!=NULL)?(char*)pData->f_configsection:"client"));
+ if(pData->f_configfile!=NULL){
+ FILE * fp;
+ fp=fopen((char*)pData->f_configfile,"r");
+ int err=errno;
+ if(fp==NULL){
+ char msg[512];
+ snprintf(msg,sizeof(msg)/sizeof(char),"Could not open '%s' for reading",pData->f_configfile);
+ if(bSilent) {
+ char errStr[512];
+ rs_strerror_r(err, errStr, sizeof(errStr));
+ dbgprintf("mysql configuration error(%d): %s - %s\n",err,msg,errStr);
+ } else
+ errmsg.LogError(err,NO_ERRCODE,"mysql configuration error: %s\n",msg);
+ } else {
+ fclose(fp);
+ mysql_options(pData->f_hmysql,MYSQL_READ_DEFAULT_FILE,pData->f_configfile);
+ }
+ }
/* Connect to database */
if(mysql_real_connect(pData->f_hmysql, pData->f_dbsrv, pData->f_dbuid,
pData->f_dbpwd, pData->f_dbname, pData->f_dbsrvPort, NULL, 0) == NULL) {
@@ -278,6 +310,8 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
ABORT_FINALIZE(RS_RET_INVALID_PARAMS);
} else {
pData->f_dbsrvPort = (unsigned) iSrvPort; /* set configured port */
+ pData->f_configfile = pszMySQLConfigFile;
+ pData->f_configsection = pszMySQLConfigSection;
pData->f_hmysql = NULL; /* initialize, but connect only on first message (important for queued mode!) */
}
@@ -302,6 +336,10 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
{
DEFiRet;
iSrvPort = 0; /* zero is the default port */
+ free(pszMySQLConfigFile);
+ pszMySQLConfigFile = NULL;
+ free(pszMySQLConfigSection);
+ pszMySQLConfigSection = NULL;
RETiRet;
}
@@ -313,6 +351,8 @@ CODEmodInit_QueryRegCFSLineHdlr
/* register our config handlers */
CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionommysqlserverport", 0, eCmdHdlrInt, NULL, &iSrvPort, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigfile",0,eCmdHdlrGetWord,NULL,&pszMySQLConfigFile,STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"ommysqlconfigsection",0,eCmdHdlrGetWord,NULL,&pszMySQLConfigSection,STD_LOADABLE_MODULE_ID));
ENDmodInit
/* vi:set ai:
diff --git a/plugins/omoracle/omoracle.c b/plugins/omoracle/omoracle.c
index 331b7dd4..a37533ee 100644
--- a/plugins/omoracle/omoracle.c
+++ b/plugins/omoracle/omoracle.c
@@ -47,9 +47,9 @@
$OmoracleStatement \
insert into foo(hostname,message)values(:host,:message)
- Also note that identifiers to placeholders are arbitrarry. You
- need to define the properties on the template in the correct order
- you want them passed to the statement!
+ Also note that identifiers to placeholders are arbitrary. You need
+ to define the properties on the template in the correct order you
+ want them passed to the statement!
This file is licensed under the terms of the GPL version 3 or, at
your choice, any later version. Exceptionally (perhaps), you are
@@ -82,12 +82,14 @@
#include "omoracle.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/** */
DEF_OMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
-/** */
+/** Structure defining a batch of items to be sent to the database in
+ * the same statement execution. */
struct oracle_batch
{
/* Batch size */
@@ -126,6 +128,13 @@ typedef struct _instanceData {
struct oracle_batch batch;
} instanceData;
+/* To be honest, strlcpy is faster than strncpy and makes very easy to
+ * detect if a message has been truncated. */
+#ifndef strlcpy
+#define strlcpy(dst,src,sz) snprintf((dst), (sz), "%s", (src))
+#endif
+
+
/** Database name, to be filled by the $OmoracleDB directive */
static char* db_name;
/** Database user name, to be filled by the $OmoracleDBUser
@@ -162,8 +171,10 @@ static int oci_errors(void* handle, ub4 htype, sword status)
return OCI_SUCCESS;
break;
case OCI_SUCCESS_WITH_INFO:
- errmsg.LogError(0, NO_ERRCODE, "OCI SUCCESS - With info\n");
- break;
+ OCIErrorGet(handle, 1, NULL, &errcode, buf, sizeof buf, htype);
+ errmsg.LogError(0, NO_ERRCODE, "OCI SUCCESS - With info: %s",
+ buf);
+ return OCI_SUCCESS_WITH_INFO;
case OCI_NEED_DATA:
errmsg.LogError(0, NO_ERRCODE, "OCI NEEDS MORE DATA\n");
break;
@@ -180,6 +191,9 @@ static int oci_errors(void* handle, ub4 htype, sword status)
break;
case OCI_INVALID_HANDLE:
errmsg.LogError(0, NO_ERRCODE, "OCI INVALID HANDLE\n");
+ /* In this case we may have to trigger a call to
+ * tryResume(). */
+ return RS_RET_SUSPENDED;
break;
case OCI_STILL_EXECUTING:
errmsg.LogError(0, NO_ERRCODE, "Still executing...\n");
@@ -332,6 +346,48 @@ CODESTARTcreateInstance
finalize_it:
ENDcreateInstance
+/* Analyses the errors during a batch statement execution, and logs
+ * all the corresponding ORA-MESSAGES, together with some useful
+ * information. */
+static void log_detailed_err(instanceData* pData)
+{
+ DEFiRet;
+ int errs, i, row, code, j;
+ OCIError *er = NULL, *er2 = NULL;
+ unsigned char buf[MAX_BUFSIZE];
+
+ OCIAttrGet(pData->statement, OCI_HTYPE_STMT, &errs, 0,
+ OCI_ATTR_NUM_DML_ERRORS, pData->error);
+ errmsg.LogError(0, NO_ERRCODE, "OCI: %d errors in execution of "
+ "statement: %s", errs, pData->txt_statement);
+
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, &er, OCI_HTYPE_ERROR,
+ 0, NULL));
+ CHECKENV(pData->environment,
+ OCIHandleAlloc(pData->environment, &er2, OCI_HTYPE_ERROR,
+ 0, NULL));
+
+ for (i = 0; i < errs; i++) {
+ OCIParamGet(pData->error, OCI_HTYPE_ERROR,
+ er2, &er, i);
+ OCIAttrGet(er, OCI_HTYPE_ERROR, &row, 0,
+ OCI_ATTR_DML_ROW_OFFSET, er2);
+ errmsg.LogError(0, NO_ERRCODE, "OCI failure in row %d:", row);
+ for (j = 0; j < pData->batch.arguments; j++)
+ errmsg.LogError(0, NO_ERRCODE, "%s",
+ pData->batch.parameters[j][row]);
+ OCIErrorGet(er, 1, NULL, &code, buf, sizeof buf,
+ OCI_HTYPE_ERROR);
+ errmsg.LogError(0, NO_ERRCODE, "FAILURE DETAILS: %s", buf);
+ }
+
+finalize_it:
+ OCIHandleFree(er, OCI_HTYPE_ERROR);
+ OCIHandleFree(er2, OCI_HTYPE_ERROR);
+}
+
+
/* Inserts all stored statements into the database, releasing any
* allocated memory. */
static int insert_to_db(instanceData* pData)
@@ -346,6 +402,10 @@ static int insert_to_db(instanceData* pData)
OCI_BATCH_ERRORS));
finalize_it:
+ if (iRet == OCI_SUCCESS_WITH_INFO) {
+ log_detailed_err(pData);
+ iRet = RS_RET_OK;
+ }
pData->batch.n = 0;
OCITransCommit(pData->service, pData->error, 0);
dbgprintf ("omoracle insertion to DB %s\n", iRet == RS_RET_OK ?
@@ -477,7 +537,7 @@ CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
BEGINdoAction
- int i;
+ int i, sz;
char **params = (char**) ppString[0];
CODESTARTdoAction
@@ -488,9 +548,13 @@ CODESTARTdoAction
for (i = 0; i < pData->batch.arguments && params[i]; i++) {
dbgprintf("batch[%d][%d]=%s\n", i, pData->batch.n, params[i]);
- strncpy(pData->batch.parameters[i][pData->batch.n], params[i],
- pData->batch.param_size);
- CHKmalloc(pData->batch.parameters[i][pData->batch.n]);
+ sz = strlcpy(pData->batch.parameters[i][pData->batch.n],
+ params[i], pData->batch.param_size);
+ if (sz >= pData->batch.param_size)
+ errmsg.LogError(0, NO_ERRCODE,
+ "Possibly truncated %d column of '%s' "
+ "statement: %s", i,
+ pData->txt_statement, params[i]);
}
pData->batch.n++;
diff --git a/plugins/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index eb774835..ea4b4b75 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -49,6 +49,7 @@
#include "errmsg.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -65,6 +66,8 @@ typedef struct _instanceData {
} instanceData;
+static rsRetVal writePgSQL(uchar *psz, instanceData *pData);
+
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -146,8 +149,13 @@ static rsRetVal initPgSQL(instanceData *pData, int bSilent)
dbgprintf("host=%s dbname=%s uid=%s\n",pData->f_dbsrv,pData->f_dbname,pData->f_dbuid);
+ /* Force PostgreSQL to use ANSI-SQL conforming strings, otherwise we may
+ * get all sorts of side effects (e.g.: backslash escapes) and warnings
+ */
+ const char *PgConnectionOptions = "-c standard_conforming_strings=on";
+
/* Connect to database */
- if((pData->f_hpgsql=PQsetdbLogin(pData->f_dbsrv, NULL, NULL, NULL,
+ if((pData->f_hpgsql=PQsetdbLogin(pData->f_dbsrv, NULL, PgConnectionOptions, NULL,
pData->f_dbname, pData->f_dbuid, pData->f_dbpwd)) == NULL) {
reportDBError(pData, bSilent);
closePgSQL(pData); /* ignore any error we may get */
@@ -189,7 +197,8 @@ tryExec(uchar *pszCmd, instanceData *pData)
* a sql format error - connection aborts were properly handled
* before my patch. -- rgerhards, 2009-04-17
*/
-rsRetVal writePgSQL(uchar *psz, instanceData *pData)
+static rsRetVal
+writePgSQL(uchar *psz, instanceData *pData)
{
int bHadError = 0;
DEFiRet;
@@ -227,16 +236,44 @@ BEGINtryResume
CODESTARTtryResume
if(pData->f_hpgsql == NULL) {
iRet = initPgSQL(pData, 1);
+ if(iRet == RS_RET_OK) {
+ /* the code above seems not to actually connect to the database. As such, we do a
+ * dummy statement (a pointless select...) to verify the connection and return
+ * success only when that statemetn succeeds. Note that I am far from being a
+ * PostgreSQL expert, so any patch that does the desired result in a more
+ * intelligent way is highly welcome. -- rgerhards, 2009-12-16
+ */
+ iRet = writePgSQL((uchar*)"select 'a' as a", pData);
+ }
+
}
ENDtryResume
+
+BEGINbeginTransaction
+CODESTARTbeginTransaction
+dbgprintf("ompgsql: beginTransaction\n");
+ iRet = writePgSQL((uchar*) "begin", pData); /* TODO: make user-configurable */
+ENDbeginTransaction
+
+
BEGINdoAction
CODESTARTdoAction
dbgprintf("\n");
- iRet = writePgSQL(ppString[0], pData);
+ CHKiRet(writePgSQL(ppString[0], pData));
+ if(bCoreSupportsBatching)
+ iRet = RS_RET_DEFER_COMMIT;
+finalize_it:
ENDdoAction
+BEGINendTransaction
+CODESTARTendTransaction
+ iRet = writePgSQL((uchar*) "commit;", pData); /* TODO: make user-configurable */
+dbgprintf("ompgsql: endTransaction\n");
+ENDendTransaction
+
+
BEGINparseSelectorAct
int iPgSQLPropErr = 0;
CODESTARTparseSelectorAct
@@ -314,6 +351,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */
ENDqueryEtryPt
@@ -322,6 +360,9 @@ CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(objUse(errmsg, 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
/* vi:set ai:
*/
diff --git a/plugins/omprog/omprog.c b/plugins/omprog/omprog.c
index 01fa7cea..56192579 100644
--- a/plugins/omprog/omprog.c
+++ b/plugins/omprog/omprog.c
@@ -45,6 +45,7 @@
#include "cfsysline.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -169,7 +170,7 @@ openPipe(instanceData *pData)
/*NO CODE HERE - WILL NEVER BE REACHED!*/
}
- DBGPRINTF("child has pid %d\n", cpid);
+ DBGPRINTF("child has pid %d\n", (int) cpid);
pData->fdPipe = pipefd[1];
pData->pid = cpid;
close(pipefd[0]);
@@ -191,7 +192,6 @@ cleanup(instanceData *pData)
assert(pData != NULL);
assert(pData->bIsRunning == 1);
-RUNLOG_VAR("%d", pData->pid);
ret = waitpid(pData->pid, &status, 0);
if(ret != pData->pid) {
/* if waitpid() fails, we can not do much - try to ignore it... */
diff --git a/plugins/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
index 4a21627d..1453d68f 100644
--- a/plugins/omrelp/omrelp.c
+++ b/plugins/omrelp/omrelp.c
@@ -43,8 +43,10 @@
#include "module-template.h"
#include "glbl.h"
#include "errmsg.h"
+#include "debug.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -270,7 +272,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(1)
tmp = ++p;
for(i=0 ; *p && isdigit((int) *p) ; ++p, ++i)
/* SKIP AND COUNT */;
- pData->port = malloc(i + 1);
+ pData->port = MALLOC(i + 1);
if(pData->port == NULL) {
errmsg.LogError(0, NO_ERRCODE, "Could not get memory to store relp port, "
"using default port, results may not be what you intend\n");
diff --git a/plugins/omruleset/Makefile.am b/plugins/omruleset/Makefile.am
new file mode 100644
index 00000000..fdd91a6e
--- /dev/null
+++ b/plugins/omruleset/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omruleset.la
+
+omruleset_la_SOURCES = omruleset.c
+omruleset_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+omruleset_la_LDFLAGS = -module -avoid-version
+omruleset_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/omruleset/omruleset.c b/plugins/omruleset/omruleset.c
new file mode 100644
index 00000000..c439bd83
--- /dev/null
+++ b/plugins/omruleset/omruleset.c
@@ -0,0 +1,232 @@
+/* omruleset.c
+ * This is a very special output module. It permits to pass a message object
+ * to another rule set. While this is a very simple action, it enables very
+ * complex configurations, e.g. it supports high-speed "and" conditions, sending
+ * data to the same file in a non-racy way, include functionality as well as
+ * some high-performance optimizations (in case the rule sets have the necessary
+ * queue definitions). So while this code is small, it is pretty important.
+ *
+ * NOTE: read comments in module-template.h for details on the calling interface!
+ *
+ * File begun on 2009-11-02 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 "template.h"
+#include "module-template.h"
+#include "errmsg.h"
+#include "ruleset.h"
+#include "cfsysline.h"
+#include "dirty.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* static data */
+DEFobjCurrIf(ruleset);
+DEFobjCurrIf(errmsg);
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+
+/* config variables */
+ruleset_t *pRuleset = NULL; /* ruleset to enqueue message to (NULL = Default, not recommended) */
+uchar *pszRulesetName = NULL;
+
+
+typedef struct _instanceData {
+ ruleset_t *pRuleset; /* ruleset to enqueue to */
+ uchar *pszRulesetName; /* primarily for debugging/display purposes */
+} instanceData;
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ free(pData->pszRulesetName);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ dbgprintf("omruleset target %s[%p]\n", (char*) pData->pszRulesetName, pData->pRuleset);
+ENDdbgPrintInstInfo
+
+
+BEGINtryResume
+CODESTARTtryResume
+ENDtryResume
+
+/* Note that we change the flow control type to "no delay", because at this point in
+ * rsyslog procesing we can not really slow down the producer any longer, as we already
+ * work off a queue. So a delay would just block out execution for longer than needed.
+ */
+BEGINdoAction
+ msg_t *pMsg;
+CODESTARTdoAction
+ CHKmalloc(pMsg = MsgDup((msg_t*) ppString[0]));
+ DBGPRINTF(":omruleset: forwarding message %p to ruleset %s[%p]\n", pMsg,
+ (char*) pData->pszRulesetName, pData->pRuleset);
+ MsgSetFlowControlType(pMsg, eFLOWCTL_NO_DELAY);
+ MsgSetRuleset(pMsg, pData->pRuleset);
+ submitMsg(pMsg);
+finalize_it:
+ENDdoAction
+
+/* set the ruleset name */
+static rsRetVal
+setRuleset(void __attribute__((unused)) *pVal, uchar *pszName)
+{
+ rsRetVal localRet;
+ 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);
+ }
+ CHKiRet(localRet);
+ pszRulesetName = pszName; /* save for later display purposes */
+
+finalize_it:
+ if(iRet != RS_RET_OK) { /* cleanup needed? */
+ free(pszName);
+ }
+ RETiRet;
+}
+
+
+BEGINparseSelectorAct
+ int iTplOpts;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omruleset:", sizeof(":omruleset:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ if(pRuleset == NULL) {
+ errmsg.LogError(0, RS_RET_NO_RULESET, "error: no ruleset was specified, use "
+ "$ActionOmrulesetRulesetName directive first!");
+ ABORT_FINALIZE(RS_RET_NO_RULESET);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omruleset:") - 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;
+ iTplOpts = OMSR_TPL_AS_MSG;
+ /* we call the message 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, iTplOpts, (uchar*) "RSYSLOG_FileFormat"));
+ pData->pRuleset = pRuleset;
+ pData->pszRulesetName = pszRulesetName;
+ pRuleset = NULL; /* re-set, because there is a high risk of unwanted behavior if we leave it in! */
+ pszRulesetName = NULL; /* note: we must not free, as we handed over this pointer to the instanceDat to the instanceDataa! */
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+BEGINmodExit
+CODESTARTmodExit
+ free(pszRulesetName);
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(ruleset, 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;
+ pRuleset = NULL;
+ RETiRet;
+}
+
+
+BEGINmodInit()
+ rsRetVal localRet;
+ rsRetVal (*pomsrGetSupportedTplOpts)(unsigned long *pOpts);
+ unsigned long opts;
+ int bMsgPassingSupported; /* does core support template passing as an array? */
+CODESTARTmodInit
+ *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, what is not acceptable */
+ }
+
+ if(!bMsgPassingSupported) {
+ DBGPRINTF("omruleset: msg-passing is not supported by rsyslog core, can not continue.\n");
+ ABORT_FINALIZE(RS_RET_NO_MSG_PASSING);
+ }
+
+ CHKiRet(objUse(ruleset, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomrulesetrulesetname", 0, eCmdHdlrGetWord,
+ setRuleset, NULL, STD_LOADABLE_MODULE_ID));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler,
+ resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vi:set ai:
+ */
diff --git a/plugins/omsnmp/omsnmp.c b/plugins/omsnmp/omsnmp.c
index 4db60e62..443cfaab 100644
--- a/plugins/omsnmp/omsnmp.c
+++ b/plugins/omsnmp/omsnmp.c
@@ -47,6 +47,7 @@
#include "errmsg.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -222,7 +223,7 @@ static rsRetVal omsnmp_sendsnmp(instanceData *pData, uchar *psz)
ABORT_FINALIZE(RS_RET_DISABLE_ACTION);
}
- pdu->enterprise = (oid *) malloc(enterpriseoidlen * sizeof(oid));
+ pdu->enterprise = (oid *) MALLOC(enterpriseoidlen * sizeof(oid));
memcpy(pdu->enterprise, enterpriseoid, enterpriseoidlen * sizeof(oid));
pdu->enterprise_length = enterpriseoidlen;
diff --git a/plugins/omstdout/omstdout.c b/plugins/omstdout/omstdout.c
index b3ec6287..dc9912e3 100644
--- a/plugins/omstdout/omstdout.c
+++ b/plugins/omstdout/omstdout.c
@@ -44,6 +44,7 @@
#include "cfsysline.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
@@ -103,7 +104,7 @@ CODESTARTdoAction
* So this code here is also more or less an example of how to do that.
* rgerhards, 2009-04-03
*/
- szParams = (char**) (ppString[0]);
+ szParams = (char**)(void*) (ppString[0]);
/* In array-passing mode, ppString[] contains a NULL-terminated array
* of char *pointers.
*/
diff --git a/plugins/omtemplate/omtemplate.c b/plugins/omtemplate/omtemplate.c
index 5577f8c6..1472ebeb 100644
--- a/plugins/omtemplate/omtemplate.c
+++ b/plugins/omtemplate/omtemplate.c
@@ -45,6 +45,7 @@
#include "cfsysline.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
diff --git a/plugins/omtesting/omtesting.c b/plugins/omtesting/omtesting.c
index 411bcf88..6d178467 100644
--- a/plugins/omtesting/omtesting.c
+++ b/plugins/omtesting/omtesting.c
@@ -22,7 +22,7 @@
* NOTE: read comments in module-template.h to understand how this file
* works!
*
- * Copyright 2007 Rainer Gerhards and Adiscon GmbH.
+ * Copyright 2007, 2009 Rainer Gerhards and Adiscon GmbH.
*
* This file is part of rsyslog.
*
@@ -46,22 +46,35 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <time.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include "dirty.h"
#include "syslogd-types.h"
#include "module-template.h"
+#include "conf.h"
+#include "cfsysline.h"
MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
/* internal structures
*/
DEF_OMOD_STATIC_DATA
+static int bEchoStdout = 0; /* echo non-failed messages to stdout */
+
typedef struct _instanceData {
+ enum { MD_SLEEP, MD_FAIL, MD_RANDFAIL, MD_ALWAYS_SUSPEND }
+ mode;
+ int bEchoStdout;
int iWaitSeconds;
int iWaitUSeconds; /* milli-seconds (one million of a second, just to make sure...) */
+ int iCurrCallNbr;
+ int iFailFrequency;
+ int iResumeAfter;
+ int iCurrRetries;
} instanceData;
BEGINcreateInstance
@@ -85,19 +98,108 @@ CODESTARTisCompatibleWithFeature
ENDisCompatibleWithFeature
-BEGINtryResume
-CODESTARTtryResume
-ENDtryResume
+/* implement "fail" command in retry processing */
+static rsRetVal doFailOnResume(instanceData *pData)
+{
+ DEFiRet;
-BEGINdoAction
-CODESTARTdoAction
+ dbgprintf("fail retry curr %d, max %d\n", pData->iCurrRetries, pData->iResumeAfter);
+ if(++pData->iCurrRetries == pData->iResumeAfter) {
+ iRet = RS_RET_OK;
+ } else {
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+/* implement "fail" command */
+static rsRetVal doFail(instanceData *pData)
+{
+ DEFiRet;
+
+ dbgprintf("fail curr %d, frquency %d\n", pData->iCurrCallNbr, pData->iFailFrequency);
+ if(pData->iCurrCallNbr++ % pData->iFailFrequency == 0) {
+ pData->iCurrRetries = 0;
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+/* implement "sleep" command */
+static rsRetVal doSleep(instanceData *pData)
+{
+ DEFiRet;
struct timeval tvSelectTimeout;
dbgprintf("sleep(%d, %d)\n", pData->iWaitSeconds, pData->iWaitUSeconds);
tvSelectTimeout.tv_sec = pData->iWaitSeconds;
tvSelectTimeout.tv_usec = pData->iWaitUSeconds; /* milli seconds */
select(0, NULL, NULL, NULL, &tvSelectTimeout);
- //dbgprintf(":omtesting: end doAction(), iRet %d\n", iRet);
+ RETiRet;
+}
+
+
+/* implement "randomfail" command */
+static rsRetVal doRandFail(void)
+{
+ DEFiRet;
+ if((rand() >> 4) < (RAND_MAX >> 5)) { /* rougly same probability */
+ iRet = RS_RET_OK;
+ dbgprintf("omtesting randfail: succeeded this time\n");
+ } else {
+ iRet = RS_RET_SUSPENDED;
+ dbgprintf("omtesting randfail: failed this time\n");
+ }
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ dbgprintf("omtesting tryResume() called\n");
+ switch(pData->mode) {
+ case MD_SLEEP:
+ break;
+ case MD_FAIL:
+ iRet = doFailOnResume(pData);
+ break;
+ case MD_RANDFAIL:
+ iRet = doRandFail();
+ break;
+ case MD_ALWAYS_SUSPEND:
+ iRet = RS_RET_SUSPENDED;
+ }
+ dbgprintf("omtesting tryResume() returns iRet %d\n", iRet);
+ENDtryResume
+
+
+BEGINdoAction
+CODESTARTdoAction
+ dbgprintf("omtesting received msg '%s'\n", ppString[0]);
+ switch(pData->mode) {
+ case MD_SLEEP:
+ iRet = doSleep(pData);
+ break;
+ case MD_FAIL:
+ iRet = doFail(pData);
+ break;
+ case MD_RANDFAIL:
+ iRet = doRandFail();
+ break;
+ case MD_ALWAYS_SUSPEND:
+ iRet = RS_RET_SUSPENDED;
+ break;
+ }
+
+ if(iRet == RS_RET_OK && pData->bEchoStdout) {
+ fprintf(stdout, "%s", ppString[0]);
+ fflush(stdout);
+ }
+ dbgprintf(":omtesting: end doAction(), iRet %d\n", iRet);
ENDdoAction
@@ -113,7 +215,7 @@ BEGINparseSelectorAct
int i;
uchar szBuf[1024];
CODESTARTparseSelectorAct
-CODE_STD_STRING_REQUESTparseSelectorAct(0)
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
/* code here is quick and dirty - if you like, clean it up. But keep
* in mind it is just a testing aid ;) -- rgerhards, 2007-12-31
*/
@@ -135,6 +237,7 @@ CODE_STD_STRING_REQUESTparseSelectorAct(0)
if(isspace(*p))
++p;
+ dbgprintf("omtesting command: '%s'\n", szBuf);
if(!strcmp((char*) szBuf, "sleep")) {
/* parse seconds */
for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
@@ -152,12 +255,43 @@ CODE_STD_STRING_REQUESTparseSelectorAct(0)
if(isspace(*p))
++p;
pData->iWaitUSeconds = atoi((char*) szBuf);
- }
- /* once there are other modes, here is the spot to add it! */
- else {
+ pData->mode = MD_SLEEP;
+ } else if(!strcmp((char*) szBuf, "fail")) {
+ /* "fail fail-freqency resume-after"
+ * fail-frequency specifies how often doAction() fails
+ * resume-after speicifes how fast tryResume() should come back with success
+ * all numbers being "times called"
+ */
+ /* parse fail-frequence */
+ for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
+ szBuf[i] = *p++;
+ }
+ szBuf[i] = '\0';
+ if(isspace(*p))
+ ++p;
+ pData->iFailFrequency = atoi((char*) szBuf);
+ /* parse resume-after */
+ for(i = 0 ; *p && !isspace(*p) && ((unsigned) i < sizeof(szBuf) - 1) ; ++i) {
+ szBuf[i] = *p++;
+ }
+ szBuf[i] = '\0';
+ if(isspace(*p))
+ ++p;
+ pData->iResumeAfter = atoi((char*) szBuf);
+ pData->iCurrCallNbr = 1;
+ pData->mode = MD_FAIL;
+ } else if(!strcmp((char*) szBuf, "randfail")) {
+ pData->mode = MD_RANDFAIL;
+ } else if(!strcmp((char*) szBuf, "always_suspend")) {
+ pData->mode = MD_ALWAYS_SUSPEND;
+ } else {
dbgprintf("invalid mode '%s', doing 'sleep 1 0' - fix your config\n", szBuf);
}
+ pData->bEchoStdout = bEchoStdout;
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (uchar*)"RSYSLOG_TraditionalForwardFormat"));
+
CODE_STD_FINALIZERparseSelectorAct
ENDparseSelectorAct
@@ -177,6 +311,10 @@ BEGINmodInit()
CODESTARTmodInit
*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"actionomtestingechostdout", 0, eCmdHdlrBinary, NULL,
+ &bEchoStdout, STD_LOADABLE_MODULE_ID));
+ /* we seed the random-number generator in any case... */
+ srand(time(NULL));
ENDmodInit
/*
* vi:set ai:
diff --git a/plugins/omudpspoof/Makefile.am b/plugins/omudpspoof/Makefile.am
new file mode 100644
index 00000000..79c495a0
--- /dev/null
+++ b/plugins/omudpspoof/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omudpspoof.la
+
+omudpspoof_la_SOURCES = omudpspoof.c
+omudpspoof_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) $(UDPSPOOF_CFLAGS)
+omudpspoof_la_LDFLAGS = -module -avoid-version
+omudpspoof_la_LIBADD = $(UDPSPOOF_LIBS)
+
+EXTRA_DIST =
diff --git a/plugins/omudpspoof/omudpspoof.c b/plugins/omudpspoof/omudpspoof.c
new file mode 100644
index 00000000..48d7a68e
--- /dev/null
+++ b/plugins/omudpspoof/omudpspoof.c
@@ -0,0 +1,503 @@
+/* omudpspoof.c
+ *
+ * This is a udp-based output module that support spoofing.
+ *
+ * This file builds on UDP spoofing code contributed by
+ * David Lang <david@lang.hm>. I then created a "real" rsyslog module
+ * out of that code and omfwd. I decided to make it a separate module because
+ * omfwd already mixes up too many things (TCP & UDP & a differnt modes,
+ * this has historic reasons), it would not be a good idea to also add
+ * spoofing to it. And, looking at the requirements, there is little in
+ * common between omfwd and this module.
+ *
+ * Note: I have briefly checked libnet source code and I somewhat have the feeling
+ * that under some circumstances we may get into trouble with the lib. For
+ * example, it registers an atexit() handler, which should not play nicely
+ * with our dynamically loaded modules. Anyhow, I refrain from looking deeper
+ * at libnet code, especially as testing does not show any real issues. If some
+ * occur, it may be easier to modify libnet for dynamic load environments than
+ * using a work-around (as a side not, libnet looks somewhat unmaintained, the CVS
+ * I can see on sourceforge dates has no updates done less than 7 years ago).
+ * On the other hand, it looks like libnet is thread safe (at least is appropriately
+ * compiled, which I hope the standard packages are). So I do not guard calls to
+ * it with my own mutex calls.
+ * rgerhards, 2009-07-10
+ *
+ * Copyright 2009 David Lang (spoofing code)
+ * 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 <time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fnmatch.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#ifdef USE_NETZIP
+#include <zlib.h>
+#endif
+#include "conf.h"
+#include "syslogd-types.h"
+#include "srUtils.h"
+#include "net.h"
+#include "template.h"
+#include "msg.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "dirty.h"
+#include "unicode-helper.h"
+#include "debug.h"
+
+
+#include <libnet.h>
+#define _BSD_SOURCE 1
+#define __BSD_SOURCE 1
+#define __FAVOR_BSD 1
+
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(net)
+
+typedef struct _instanceData {
+ uchar *host;
+ uchar *port;
+ int *pSockArray; /* sockets to use for UDP */
+ int compressionLevel; /* 0 - no compression, else level for zlib */
+ struct addrinfo *f_addr;
+ u_short sourcePort;
+ u_short sourcePortStart; /* for sorce port iteration */
+ u_short sourcePortEnd;
+} instanceData;
+
+#define DFLT_SOURCE_PORT_START 32000
+#define DFLT_SOURCE_PORT_END 42000
+
+/* config data */
+static uchar *pszTplName = NULL; /* name of the default template to use */
+static uchar *pszSourceNameTemplate = NULL; /* name of the template containing the spoofing address */
+static uchar *pszTargetHost = NULL;
+static uchar *pszTargetPort = NULL;
+static int iCompressionLevel = 0; /* zlib compressionlevel, the usual values */
+static int iSourcePortStart = DFLT_SOURCE_PORT_START;
+static int iSourcePortEnd = DFLT_SOURCE_PORT_END;
+
+
+/* add some variables needed for libnet */
+libnet_t *libnet_handle;
+char errbuf[LIBNET_ERRBUF_SIZE];
+
+/* forward definitions */
+static rsRetVal doTryResume(instanceData *pData);
+
+
+/* Close the UDP sockets.
+ * rgerhards, 2009-05-29
+ */
+static rsRetVal
+closeUDPSockets(instanceData *pData)
+{
+ DEFiRet;
+ assert(pData != NULL);
+ if(pData->pSockArray != NULL) {
+ net.closeUDPListenSockets(pData->pSockArray);
+ pData->pSockArray = NULL;
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
+ }
+ RETiRet;
+}
+
+
+/* get the syslog forward port
+ * We may change the implementation to try to lookup the port
+ * if it is unspecified. So far, we use the IANA default auf 514.
+ * rgerhards, 2007-06-28
+ */
+static inline uchar *getFwdPt(instanceData *pData)
+{
+ return (pData->port == NULL) ? UCHAR_CONSTANT("514") : pData->port;
+}
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* final cleanup */
+ closeUDPSockets(pData);
+ free(pData->port);
+ free(pData->host);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ DBGPRINTF("%s", pData->host);
+ENDdbgPrintInstInfo
+
+
+/* Send a message via UDP
+ * rgehards, 2007-12-20
+ */
+static inline rsRetVal
+UDPSend(instanceData *pData, uchar *pszSourcename, char *msg, size_t len)
+{
+ struct addrinfo *r;
+ int lsent = 0;
+ int bSendSuccess;
+ int j, build_ip;
+ u_char opt[20];
+ struct sockaddr_in *tempaddr,source_ip;
+ libnet_ptag_t ip, ipo;
+ libnet_ptag_t udp;
+ DEFiRet;
+
+ if(pData->pSockArray == NULL) {
+ CHKiRet(doTryResume(pData));
+ }
+
+ ip = ipo = udp = 0;
+ if(pData->sourcePort++ >= pData->sourcePortEnd){
+ pData->sourcePort = pData->sourcePortStart;
+ }
+
+ inet_pton(AF_INET, (char*)pszSourcename, &(source_ip.sin_addr));
+
+ bSendSuccess = FALSE;
+ for (r = pData->f_addr; r; r = r->ai_next) {
+ tempaddr = (struct sockaddr_in *)r->ai_addr;
+ libnet_clear_packet(libnet_handle);
+ /* note: libnet does need ports in host order NOT in network byte order! -- rgerhards, 2009-11-12 */
+ udp = libnet_build_udp(
+ ntohs(pData->sourcePort),/* source port */
+ ntohs(tempaddr->sin_port),/* destination port */
+ LIBNET_UDP_H + len, /* packet length */
+ 0, /* checksum */
+ (u_char*)msg, /* payload */
+ len, /* payload size */
+ libnet_handle, /* libnet handle */
+ udp); /* libnet id */
+ if (udp == -1) {
+ DBGPRINTF("Can't build UDP header: %s\n", libnet_geterror(libnet_handle));
+ }
+
+ build_ip = 0;
+ /* this is not a legal options string */
+ for (j = 0; j < 20; j++) {
+ opt[j] = libnet_get_prand(LIBNET_PR2);
+ }
+ ipo = libnet_build_ipv4_options(opt, 20, libnet_handle, ipo);
+ if (ipo == -1) {
+ DBGPRINTF("Can't build IP options: %s\n", libnet_geterror(libnet_handle));
+ }
+ ip = libnet_build_ipv4(
+ LIBNET_IPV4_H + 20 + len + LIBNET_UDP_H, /* length */
+ 0, /* TOS */
+ 242, /* IP ID */
+ 0, /* IP Frag */
+ 64, /* TTL */
+ IPPROTO_UDP, /* protocol */
+ 0, /* checksum */
+ source_ip.sin_addr.s_addr,
+ tempaddr->sin_addr.s_addr,
+ NULL, /* payload */
+ 0, /* payload size */
+ libnet_handle, /* libnet handle */
+ ip); /* libnet id */
+ if (ip == -1) {
+ DBGPRINTF("Can't build IP header: %s\n", libnet_geterror(libnet_handle));
+ }
+
+ /* Write it to the wire. */
+ lsent = libnet_write(libnet_handle);
+ if (lsent == -1) {
+ DBGPRINTF("Write error: %s\n", libnet_geterror(libnet_handle));
+ } else {
+ bSendSuccess = TRUE;
+ break;
+ }
+ }
+ /* finished looping */
+ if (bSendSuccess == FALSE) {
+ DBGPRINTF("error forwarding via udp, suspending\n");
+ iRet = RS_RET_SUSPENDED;
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* try to resume connection if it is not ready
+ * rgerhards, 2007-08-02
+ */
+static rsRetVal doTryResume(instanceData *pData)
+{
+ int iErr;
+ struct addrinfo *res;
+ struct addrinfo hints;
+ DEFiRet;
+
+ if(pData->pSockArray != NULL)
+ FINALIZE;
+
+ /* The remote address is not yet known and needs to be obtained */
+ DBGPRINTF(" %s\n", pData->host);
+ memset(&hints, 0, sizeof(hints));
+ /* port must be numeric, because config file syntax requires this */
+ hints.ai_flags = AI_NUMERICSERV;
+ hints.ai_family = glbl.GetDefPFFamily();
+ hints.ai_socktype = SOCK_DGRAM;
+ if((iErr = (getaddrinfo((char*)pData->host, (char*)getFwdPt(pData), &hints, &res))) != 0) {
+ DBGPRINTF("could not get addrinfo for hostname '%s':'%s': %d%s\n",
+ pData->host, getFwdPt(pData), iErr, gai_strerror(iErr));
+ ABORT_FINALIZE(RS_RET_SUSPENDED);
+ }
+ DBGPRINTF("%s found, resuming.\n", pData->host);
+ pData->f_addr = res;
+ pData->pSockArray = net.create_udp_socket((uchar*)pData->host, NULL, 0);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ if(pData->f_addr != NULL) {
+ freeaddrinfo(pData->f_addr);
+ pData->f_addr = NULL;
+ }
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = doTryResume(pData);
+ENDtryResume
+
+BEGINdoAction
+ char *psz; /* temporary buffering */
+ register unsigned l;
+ int iMaxLine;
+CODESTARTdoAction
+ CHKiRet(doTryResume(pData));
+
+ iMaxLine = glbl.GetMaxLine();
+
+ DBGPRINTF(" %s:%s/udpspoofs\n", pData->host, getFwdPt(pData));
+
+ psz = (char*) ppString[0];
+ l = strlen((char*) psz);
+ if((int) l > iMaxLine)
+ l = iMaxLine;
+
+# ifdef USE_NETZIP
+ /* Check if we should compress and, if so, do it. We also
+ * check if the message is large enough to justify compression.
+ * The smaller the message, the less likely is a gain in compression.
+ * To save CPU cycles, we do not try to compress very small messages.
+ * What "very small" means needs to be configured. Currently, it is
+ * hard-coded but this may be changed to a config parameter.
+ * rgerhards, 2006-11-30
+ */
+ if(pData->compressionLevel && (l > CONF_MIN_SIZE_FOR_COMPRESS)) {
+ Bytef *out;
+ uLongf destLen = iMaxLine + iMaxLine/100 +12; /* recommended value from zlib doc */
+ uLong srcLen = l;
+ int ret;
+ /* TODO: optimize malloc sequence? -- rgerhards, 2008-09-02 */
+ CHKmalloc(out = (Bytef*) MALLOC(destLen));
+ out[0] = 'z';
+ out[1] = '\0';
+ ret = compress2((Bytef*) out+1, &destLen, (Bytef*) psz,
+ srcLen, pData->compressionLevel);
+ DBGPRINTF("Compressing message, length was %d now %d, return state %d.\n",
+ l, (int) destLen, ret);
+ if(ret != Z_OK) {
+ /* if we fail, we complain, but only in debug mode
+ * Otherwise, we are silent. In any case, we ignore the
+ * failed compression and just sent the uncompressed
+ * data, which is still valid. So this is probably the
+ * best course of action.
+ * rgerhards, 2006-11-30
+ */
+ DBGPRINTF("Compression failed, sending uncompressed message\n");
+ } else if(destLen+1 < l) {
+ /* only use compression if there is a gain in using it! */
+ DBGPRINTF("there is gain in compression, so we do it\n");
+ psz = (char*) out;
+ l = destLen + 1; /* take care for the "z" at message start! */
+ }
+ ++destLen;
+ }
+# endif
+
+ CHKiRet(UDPSend(pData, ppString[1], psz, l));
+
+finalize_it:
+ENDdoAction
+
+
+BEGINparseSelectorAct
+ uchar *sourceTpl;
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(2)
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omudpspoof:", sizeof(":omudpspoof:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omudpspoof:") - 1; /* eat indicator sequence (-1 because of '\0'!) */
+ CHKiRet(createInstance(&pData));
+
+ sourceTpl = (pszSourceNameTemplate == NULL) ? UCHAR_CONSTANT("RSYSLOG_omudpspoofDfltSourceTpl")
+ : pszSourceNameTemplate;
+
+ if(pszTargetHost == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "No $ActionOMUDPSpoofTargetHost given, can not continue with this action.");
+ ABORT_FINALIZE(RS_RET_HOST_NOT_SPECIFIED);
+ }
+
+ /* fill instance properties */
+ CHKmalloc(pData->host = ustrdup(pszTargetHost));
+ if(pszTargetPort == NULL)
+ pData->port = NULL;
+ else
+ CHKmalloc(pData->port = ustrdup(pszTargetPort));
+ CHKiRet(OMSRsetEntry(*ppOMSR, 1, ustrdup(sourceTpl), OMSR_NO_RQD_TPL_OPTS));
+ pData->compressionLevel = iCompressionLevel;
+ pData->sourcePort = pData->sourcePortStart = iSourcePortStart;
+ pData->sourcePortEnd = iSourcePortEnd;
+
+ /* process template */
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, OMSR_NO_RQD_TPL_OPTS,
+ (pszTplName == NULL) ? (uchar*)"RSYSLOG_TraditionalForwardFormat" : pszTplName));
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+/* a common function to free our configuration variables - used both on exit
+ * and on $ResetConfig processing. -- rgerhards, 2008-05-16
+ */
+static void
+freeConfigVars(void)
+{
+ free(pszTplName);
+ pszTplName = NULL;
+ free(pszTargetHost);
+ pszTargetHost = NULL;
+ free(pszTargetPort);
+ pszTargetPort = NULL;
+}
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* destroy the libnet state needed for forged UDP sources */
+ libnet_destroy(libnet_handle);
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(net, LM_NET_FILENAME);
+ freeConfigVars();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+/* Reset config variables for this module to default values.
+ * rgerhards, 2008-03-28
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ freeConfigVars();
+ /* we now must reset all non-string values */
+ iCompressionLevel = 0;
+ iSourcePortStart = DFLT_SOURCE_PORT_START;
+ iSourcePortEnd = DFLT_SOURCE_PORT_END;
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(net,LM_NET_FILENAME));
+
+ /* Initialize the libnet library. Root priviledges are required.
+ * this initializes a IPv4 socket to use for forging UDP packets.
+ */
+ libnet_handle = libnet_init(
+ LIBNET_RAW4, /* injection type */
+ NULL, /* network interface */
+ errbuf); /* errbuf */
+
+ if(libnet_handle == NULL) {
+ errmsg.LogError(0, NO_ERRCODE, "Error initializing libnet, can not continue ");
+ ABORT_FINALIZE(RS_RET_ERR_LIBNET_INIT);
+ }
+
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &pszTplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourcenametemplate", 0, eCmdHdlrGetWord, NULL, &pszSourceNameTemplate, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargethost", 0, eCmdHdlrGetWord, NULL, &pszTargetHost, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspooftargetport", 0, eCmdHdlrGetWord, NULL, &pszTargetPort, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportstart", 0, eCmdHdlrInt, NULL, &iSourcePortStart, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpspoofsourceportend", 0, eCmdHdlrInt, NULL, &iSourcePortEnd, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"actionomudpcompressionlevel", 0, eCmdHdlrInt, NULL, &iCompressionLevel, NULL));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/omuxsock/Makefile.am b/plugins/omuxsock/Makefile.am
new file mode 100644
index 00000000..997232d9
--- /dev/null
+++ b/plugins/omuxsock/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = omuxsock.la
+
+omuxsock_la_SOURCES = omuxsock.c
+omuxsock_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS)
+omuxsock_la_LDFLAGS = -module -avoid-version
+omuxsock_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/omuxsock/omuxsock.c b/plugins/omuxsock/omuxsock.c
new file mode 100644
index 00000000..0e336c51
--- /dev/null
+++ b/plugins/omuxsock/omuxsock.c
@@ -0,0 +1,316 @@
+/* omuxsock.c
+ * This is the implementation of datgram unix domain socket forwarding.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2007-07-20 by RGerhards (extracted from syslogd.c)
+ * This file is under development and has not yet arrived at being fully
+ * self-contained and a real object. So far, it is mostly an excerpt
+ * of the "old" message code without any modifications. However, it
+ * helps to have things at the right place one we go to the meat of it.
+ *
+ * Copyright 2010 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 <time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include "conf.h"
+#include "srUtils.h"
+#include "template.h"
+#include "msg.h"
+#include "cfsysline.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_OUTPUT
+MODULE_TYPE_NOKEEP
+
+/* internal structures
+ */
+DEF_OMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+
+#define INVLD_SOCK -1
+
+typedef struct _instanceData {
+ permittedPeers_t *pPermPeers;
+ uchar *sockName;
+ int sock;
+ int bIsConnected; /* are we connected to remote host? 0 - no, 1 - yes, UDP means addr resolved */
+ struct sockaddr_un addr;
+} instanceData;
+
+/* config data */
+static uchar *tplName = NULL; /* name of the default template to use */
+static uchar *sockName = NULL; /* name of the default template to use */
+
+static rsRetVal doTryResume(instanceData *pData);
+
+/* Close socket.
+ */
+static inline rsRetVal
+closeSocket(instanceData *pData)
+{
+ DEFiRet;
+ if(pData->sock != INVLD_SOCK) {
+ close(pData->sock);
+ pData->sock = INVLD_SOCK;
+ }
+pData->bIsConnected = 0; // TODO: remove this variable altogether
+ RETiRet;
+}
+
+
+
+BEGINcreateInstance
+CODESTARTcreateInstance
+ pData->sock = INVLD_SOCK;
+ENDcreateInstance
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURERepeatedMsgReduction)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINfreeInstance
+CODESTARTfreeInstance
+ /* final cleanup */
+ closeSocket(pData);
+ free(pData->sockName);
+ENDfreeInstance
+
+
+BEGINdbgPrintInstInfo
+CODESTARTdbgPrintInstInfo
+ DBGPRINTF("%s", pData->sockName);
+ENDdbgPrintInstInfo
+
+
+/* Send a message via UDP
+ * rgehards, 2007-12-20
+ */
+static rsRetVal sendMsg(instanceData *pData, char *msg, size_t len)
+{
+ DEFiRet;
+ unsigned lenSent = 0;
+
+ if(pData->sock == INVLD_SOCK) {
+ CHKiRet(doTryResume(pData));
+ }
+
+ if(pData->sock != INVLD_SOCK) {
+ /* we need to track if we have success sending to the remote
+ * peer. Success is indicated by at least one sendto() call
+ * succeeding. We track this be bSendSuccess. We can not simply
+ * rely on lsent, as a call might initially work, but a later
+ * call fails. Then, lsent has the error status, even though
+ * the sendto() succeeded. -- rgerhards, 2007-06-22
+ */
+ lenSent = sendto(pData->sock, msg, len, 0, &pData->addr, sizeof(pData->addr));
+ if(lenSent == len) {
+ int eno = errno;
+ char errStr[1024];
+ DBGPRINTF("omuxsock suspending: sendto(), socket %d, error: %d = %s.\n",
+ pData->sock, eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ }
+ }
+
+finalize_it:
+ RETiRet;
+}
+
+
+/* open socket to remote system
+ */
+static inline rsRetVal
+openSocket(instanceData *pData)
+{
+ DEFiRet;
+ assert(pData->sock == INVLD_SOCK);
+
+ if((pData->sock = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
+ char errStr[1024];
+ int eno = errno;
+ DBGPRINTF("error %d creating AF_UNIX/SOCK_DGRAM: %s.\n",
+ eno, rs_strerror_r(eno, errStr, sizeof(errStr)));
+ pData->sock = INVLD_SOCK;
+ ABORT_FINALIZE(RS_RET_NO_SOCKET);
+
+ }
+
+ /* set up server address structure */
+ memset(&pData->addr, 0, sizeof(pData->addr));
+ pData->addr.sun_family = AF_UNIX;
+ strcpy(pData->addr.sun_path, (char*)pData->sockName);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ closeSocket(pData);
+ }
+ RETiRet;
+}
+
+
+
+/* try to resume connection if it is not ready
+ */
+static rsRetVal doTryResume(instanceData *pData)
+{
+ DEFiRet;
+
+ DBGPRINTF("omuxsock trying to resume\n");
+ closeSocket(pData);
+ iRet = openSocket(pData);
+
+ if(iRet != RS_RET_OK) {
+ iRet = RS_RET_SUSPENDED;
+ }
+
+ RETiRet;
+}
+
+
+BEGINtryResume
+CODESTARTtryResume
+ iRet = doTryResume(pData);
+ENDtryResume
+
+BEGINdoAction
+ char *psz = NULL; /* temporary buffering */
+ register unsigned l;
+ int iMaxLine;
+CODESTARTdoAction
+ CHKiRet(doTryResume(pData));
+
+ iMaxLine = glbl.GetMaxLine();
+
+ DBGPRINTF(" omuxsock:%s\n", pData->sockName);
+
+ psz = (char*) ppString[0];
+ l = strlen((char*) psz);
+ if((int) l > iMaxLine)
+ l = iMaxLine;
+
+ CHKiRet(sendMsg(pData, psz, l));
+
+finalize_it:
+ENDdoAction
+
+
+BEGINparseSelectorAct
+CODESTARTparseSelectorAct
+CODE_STD_STRING_REQUESTparseSelectorAct(1)
+
+ /* first check if this config line is actually for us */
+ if(strncmp((char*) p, ":omuxsock:", sizeof(":omuxsock:") - 1)) {
+ ABORT_FINALIZE(RS_RET_CONFLINE_UNPROCESSED);
+ }
+
+ /* ok, if we reach this point, we have something for us */
+ p += sizeof(":omuxsock:") - 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;
+ CHKiRet(cflineParseTemplateName(&p, *ppOMSR, 0, 0, tplName == NULL ? UCHAR_CONSTANT("RSYSLOG_TraditionalForwardFormat")
+ : tplName ));
+
+ if(sockName == NULL) {
+ errmsg.LogError(0, RS_RET_NO_SOCK_CONFIGURED, "No output socket configured for omuxsock\n");
+ ABORT_FINALIZE(RS_RET_NO_SOCK_CONFIGURED);
+ }
+
+ pData->sockName = sockName;
+ sockName = NULL; /* pData is now owner and will fee it */
+
+CODE_STD_FINALIZERparseSelectorAct
+ENDparseSelectorAct
+
+
+/* a common function to free our configuration variables - used both on exit
+ * and on $ResetConfig processing. -- rgerhards, 2008-05-16
+ */
+static inline void
+freeConfigVars(void)
+{
+ free(tplName);
+ tplName = NULL;
+ free(sockName);
+ sockName = NULL;
+}
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+
+ freeConfigVars();
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+/* Reset config variables for this module to default values.
+ * rgerhards, 2008-03-28
+ */
+static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal)
+{
+ freeConfigVars();
+ return RS_RET_OK;
+}
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ CHKiRet(regCfSysLineHdlr((uchar *)"omuxsockdefaulttemplate", 0, eCmdHdlrGetWord, NULL, &tplName, NULL));
+ CHKiRet(regCfSysLineHdlr((uchar *)"omuxsocksocket", 0, eCmdHdlrGetWord, NULL, &sockName, NULL));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID));
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/pmaixforwardedfrom/Makefile.am b/plugins/pmaixforwardedfrom/Makefile.am
new file mode 100644
index 00000000..af359d31
--- /dev/null
+++ b/plugins/pmaixforwardedfrom/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = pmaixforwardedfrom.la
+
+pmaixforwardedfrom_la_SOURCES = pmaixforwardedfrom.c
+pmaixforwardedfrom_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -I ../../tools
+pmaixforwardedfrom_la_LDFLAGS = -module -avoid-version
+pmaixforwardedfrom_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c
new file mode 100644
index 00000000..fe3e85fa
--- /dev/null
+++ b/plugins/pmaixforwardedfrom/pmaixforwardedfrom.c
@@ -0,0 +1,168 @@
+/* pmaixforwardedfrom.c
+ *
+ * this cleans up messages forwarded from AIX
+ *
+ * instead of actually parsing the message, this modifies the message and then falls through to allow a later parser to handle the now modified message
+ *
+ * created 2010-12-13 by David Lang based on pmlastmsg
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.aixforwardedfrom")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINparse
+ uchar *p2parse;
+ uchar *opening;
+ int lenMsg;
+#define OpeningText "Message forwarded from "
+CODESTARTparse
+ dbgprintf("Message will now be parsed by fix AIX Forwarded From parser.\n");
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+
+ /* check if this message is of the type we handle in this (very limited) parser */
+ /* first, we permit SP */
+ while(lenMsg && *p2parse == ' ') {
+ --lenMsg;
+ ++p2parse;
+ }
+dbgprintf("pmaixforwardedfrom: msg to look at: [%d]'%s'\n", lenMsg, p2parse);
+ if((unsigned) lenMsg < 42) {
+ /* too short, can not be "our" message */
+ /* minimum message, 16 character timestamp, 'Message forwarded from ", 1 character name, ': '*/
+dbgprintf("msg too short!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+
+ /* skip over timestamp */
+ lenMsg -=16;
+ p2parse +=16;
+ /* if there is the string "Message forwarded from " were the hostname should be */
+ if(strncasecmp((char*) p2parse, OpeningText, sizeof(OpeningText)-1) != 0) {
+ /* wrong opening text */
+dbgprintf("not a AIX message forwarded from mangled log!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ /* bump the message portion up by 23 characters to overwrite the "Message forwarded from " with the hostname */
+ lenMsg -=23;
+ memmove(p2parse, p2parse + 23, lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=23;
+ pMsg->iLenMSG -=23;
+ /* now look for the : after the hostname to walk past the hostname, also watch for a space in case this isn't really an AIX log, but has a similar preamble */
+ while(lenMsg && *p2parse != ' ' && *p2parse != ':') {
+ --lenMsg;
+ ++p2parse;
+ }
+ if (lenMsg && *p2parse != ':') {
+dbgprintf("not a AIX message forwarded from mangled log but similar enough that the preamble has been removed\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ /* bump the message portion up by one character to overwrite the extra : */
+ lenMsg -=1;
+ memmove(p2parse, p2parse + 1, lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=1;
+ pMsg->iLenMSG -=1;
+ /* now, claim to abort so that something else can parse the now modified message */
+ DBGPRINTF("pmaixforwardedfrom: new mesage: [%d]'%s'\n", lenMsg, pMsg->pszRawMsg + pMsg->offAfterPRI);
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+
+finalize_it:
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ DBGPRINTF("aixforwardedfrom parser init called, compiled with version %s\n", VERSION);
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/pmcisconames/Makefile.am b/plugins/pmcisconames/Makefile.am
new file mode 100644
index 00000000..16ed347d
--- /dev/null
+++ b/plugins/pmcisconames/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = pmcisconames.la
+
+pmcisconames_la_SOURCES = pmcisconames.c
+pmcisconames_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -I ../../tools
+pmcisconames_la_LDFLAGS = -module -avoid-version
+pmcisconames_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/pmcisconames/pmcisconames.c b/plugins/pmcisconames/pmcisconames.c
new file mode 100644
index 00000000..61688cbf
--- /dev/null
+++ b/plugins/pmcisconames/pmcisconames.c
@@ -0,0 +1,178 @@
+/* pmcisconames.c
+ *
+ * this detects logs sent by Cisco devices that mangle their syslog output when you tell them to log by name by adding ' :' between the name and the %XXX-X-XXXXXXX: tag
+ *
+ * instead of actually parsing the message, this modifies the message and then falls through to allow a later parser to handle the now modified message
+ *
+ * created 2010-12-13 by David Lang based on pmlastmsg
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.cisconames")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINparse
+ uchar *p2parse;
+ int lenMsg;
+#define OpeningText ": %"
+CODESTARTparse
+ dbgprintf("Message will now be parsed by fix Cisco Names parser.\n");
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+
+ /* check if this message is of the type we handle in this (very limited) parser */
+ /* first, we permit SP */
+ while(lenMsg && *p2parse == ' ') {
+ --lenMsg;
+ ++p2parse;
+ }
+dbgprintf("pmcisconames: msg to look at: [%d]'%s'\n", lenMsg, p2parse);
+ if((unsigned) lenMsg < 34) {
+ /* too short, can not be "our" message */
+ /* minimum message, 16 character timestamp, 1 character name, ' : %ASA-1-000000: '*/
+dbgprintf("msg too short!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ /* check if the timestamp is a 16 character or 21 character timestamp
+ 'Mmm DD HH:MM:SS ' spaces at 3,6,15 : at 9,12
+ 'Mmm DD YYYY HH:MM:SS ' spaces at 3,6,11,20 : at 14,17
+ check for the : first as that will differentiate the two conditions the fastest
+ this allows the compiler to short circuit the rst of the tests if it is the wrong timestamp
+ but still check the rest to see if it looks correct
+ */
+ if ( *(p2parse + 9) == ':' && *(p2parse + 12) == ':' && *(p2parse + 3) == ' ' && *(p2parse + 6) == ' ' && *(p2parse + 15) == ' ') {
+ /* skip over timestamp */
+ dbgprintf("short timestamp found\n");
+ lenMsg -=16;
+ p2parse +=16;
+ } else {
+ if ( *(p2parse + 14) == ':' && *(p2parse + 17) == ':' && *(p2parse + 3) == ' ' && *(p2parse + 6) == ' ' && *(p2parse + 11) == ' ' && *(p2parse + 20) == ' ') {
+ /* skip over timestamp */
+ dbgprintf("long timestamp found\n");
+ lenMsg -=21;
+ p2parse +=21;
+ } else {
+ dbgprintf("timestamp is not one of the valid formats\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ }
+ /* now look for the next space to walk past the hostname */
+ while(lenMsg && *p2parse != ' ') {
+ --lenMsg;
+ ++p2parse;
+ }
+ /* skip the space after the hostname */
+ lenMsg -=1;
+ p2parse +=1;
+ /* if the syslog tag is : and the next thing starts with a % assume that this is a mangled cisco log and fix it */
+ if(strncasecmp((char*) p2parse, OpeningText, sizeof(OpeningText)-1) != 0) {
+ /* wrong opening text */
+dbgprintf("not a cisco name mangled log!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ /* bump the message portion up by two characters to overwrite the extra : */
+ lenMsg -=2;
+ memmove(p2parse, p2parse + 2, lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=2;
+ pMsg->iLenMSG -=2;
+ /* now, claim to abort so that something else can parse the now modified message */
+ DBGPRINTF("pmcisconames: new mesage: [%d]'%s'\n", lenMsg, p2parse);
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+
+finalize_it:
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ DBGPRINTF("cisconames parser init called, compiled with version %s\n", VERSION);
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/pmlastmsg/Makefile.am b/plugins/pmlastmsg/Makefile.am
new file mode 100644
index 00000000..f360243c
--- /dev/null
+++ b/plugins/pmlastmsg/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = pmlastmsg.la
+
+pmlastmsg_la_SOURCES = pmlastmsg.c
+pmlastmsg_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -I ../../tools
+pmlastmsg_la_LDFLAGS = -module -avoid-version
+pmlastmsg_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/pmlastmsg/pmlastmsg.c b/plugins/pmlastmsg/pmlastmsg.c
new file mode 100644
index 00000000..259c5d41
--- /dev/null
+++ b/plugins/pmlastmsg/pmlastmsg.c
@@ -0,0 +1,176 @@
+/* pmlastmsg.c
+ * This is a parser module specifically for those horrible
+ * "<PRI>last message repeated n times" messages notoriously generated
+ * by some syslog implementations. Note that this parser should be placed
+ * on top of the parser stack -- it takes out only these messages and
+ * leaves all others for processing by the other parsers.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2010-07-13 by RGerhards
+ *
+ * Copyright 2010 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.lastline")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+/* parse a legay-formatted syslog message.
+ */
+BEGINparse
+ uchar *p2parse;
+ int lenMsg;
+#define OpeningText "last message repeated "
+#define ClosingText " times"
+CODESTARTparse
+ dbgprintf("Message will now be parsed by \"last message repated n times\" parser.\n");
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+
+ /* check if this message is of the type we handle in this (very limited) parser */
+ /* first, we permit SP */
+ while(lenMsg && *p2parse == ' ') {
+ --lenMsg;
+ ++p2parse;
+ }
+dbgprintf("pmlastmsg: msg to look at: [%d]'%s'\n", lenMsg, p2parse);
+ if((unsigned) lenMsg < sizeof(OpeningText)-1 + sizeof(ClosingText)-1 + 1) {
+ /* too short, can not be "our" message */
+dbgprintf("msg too short!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+
+ if(strncasecmp((char*) p2parse, OpeningText, sizeof(OpeningText)-1) != 0) {
+ /* wrong opening text */
+dbgprintf("wrong opening text!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+ lenMsg -= sizeof(OpeningText) - 1;
+ p2parse += sizeof(OpeningText) - 1;
+
+ /* now we need an integer --> digits */
+ while(lenMsg && isdigit(*p2parse)) {
+ --lenMsg;
+ ++p2parse;
+ }
+
+ if(lenMsg != sizeof(ClosingText)-1) {
+ /* size must fit, else it is not "our" message... */
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+
+ if(strncasecmp((char*) p2parse, ClosingText, lenMsg) != 0) {
+ /* wrong closing text */
+dbgprintf("strcasecmp: %d\n", strncasecmp((char*) p2parse, ClosingText, lenMsg));
+dbgprintf("pmlastmsg: closing msg to look at: [%d]'%s', (%s)\n", lenMsg, p2parse, ClosingText);
+dbgprintf("wrong closing text!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+
+ /* OK, now we know we need to process this message, so we do that
+ * (and it is fairly simple in our case...)
+ */
+ DBGPRINTF("pmlastmsg detected a \"last message repeated n times\" message\n");
+
+ setProtocolVersion(pMsg, 0);
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
+ MsgSetMSGoffs(pMsg, pMsg->offAfterPRI); /* we don't have a header! */
+ MsgSetTAG(pMsg, (uchar*)"", 0);
+
+finalize_it:
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ dbgprintf("lastmsg parser init called, compiled with version %s\n", VERSION);
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/pmrfc3164sd/Makefile.am b/plugins/pmrfc3164sd/Makefile.am
new file mode 100644
index 00000000..d1662d4d
--- /dev/null
+++ b/plugins/pmrfc3164sd/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = pmrfc3164sd.la
+
+pmrfc3164sd_la_SOURCES = pmrfc3164sd.c
+pmrfc3164sd_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -I ../../tools
+pmrfc3164sd_la_LDFLAGS = -module -avoid-version
+pmrfc3164sd_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/pmrfc3164sd/pmrfc3164sd.c b/plugins/pmrfc3164sd/pmrfc3164sd.c
new file mode 100644
index 00000000..53204ece
--- /dev/null
+++ b/plugins/pmrfc3164sd/pmrfc3164sd.c
@@ -0,0 +1,344 @@
+/* pmrfc3164sd.c
+ * This is a parser module for RFC3164(legacy syslog)-formatted messages.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2009-11-04 by RGerhards
+ *
+ * Copyright 2007, 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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+#include "syslogd.h"
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("contrib.rfc3164sd")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+/* Helper to parseRFCSyslogMsg. This function parses the structured
+ * data field of a message. It does NOT parse inside structured data,
+ * just gets the field as whole. Parsing the single entities is left
+ * to other functions. The parsepointer is advanced
+ * to after the terminating SP. The caller must ensure that the
+ * provided buffer is large enough to hold the to be extracted value.
+ * Returns 0 if everything is fine or 1 if either the field is not
+ * SP-terminated or any other error occurs. -- rger, 2005-11-24
+ * The function now receives the size of the string and makes sure
+ * that it does not process more than that. The *pLenStr counter is
+ * updated on exit. -- rgerhards, 2009-09-23
+ */
+static int parseRFCStructuredData(uchar **pp2parse, uchar *pResult, int *pLenStr)
+{
+ uchar *p2parse;
+ int bCont = 1;
+ int iRet = 0;
+ int lenStr;
+
+ assert(pp2parse != NULL);
+ assert(*pp2parse != NULL);
+ assert(pResult != NULL);
+
+ p2parse = *pp2parse;
+ lenStr = *pLenStr;
+
+ /* this is the actual parsing loop
+ * Remeber: structured data starts with [ and includes any characters
+ * until the first ] followed by a SP. There may be spaces inside
+ * structured data. There may also be \] inside the structured data, which
+ * do NOT terminate an element.
+ */
+
+ /* trim */
+ while(lenStr > 0 && *p2parse == ' ') {
+ ++p2parse; /* eat SP, but only if not at end of string */
+ --lenStr;
+ }
+
+ if(lenStr == 0 || *p2parse != '[')
+ return 1; /* this is NOT structured data! */
+
+ if(*p2parse == '-') { /* empty structured data? */
+ *pResult++ = '-';
+ ++p2parse;
+ --lenStr;
+ } else {
+ while(bCont) {
+ if(lenStr < 2) {
+ /* we now need to check if we have only structured data */
+ if(lenStr > 0 && *p2parse == ']') {
+ *pResult++ = *p2parse;
+ p2parse++;
+ lenStr--;
+ bCont = 0;
+ } else {
+ iRet = 1; /* this is not valid! */
+ bCont = 0;
+ }
+ } else if(*p2parse == '\\' && *(p2parse+1) == ']') {
+ /* this is escaped, need to copy both */
+ *pResult++ = *p2parse++;
+ *pResult++ = *p2parse++;
+ lenStr -= 2;
+ } else if(*p2parse == ']' && *(p2parse+1) == ' ') {
+ /* found end, just need to copy the ] and eat the SP */
+ *pResult++ = *p2parse;
+ p2parse += 2;
+ lenStr -= 2;
+ bCont = 0;
+ } else {
+ *pResult++ = *p2parse++;
+ --lenStr;
+ }
+ }
+ }
+
+ if(lenStr > 0 && *p2parse == ' ') {
+ ++p2parse; /* eat SP, but only if not at end of string */
+ --lenStr;
+ } else {
+ iRet = 1; /* there MUST be an SP! */
+ }
+ *pResult = '\0';
+
+ /* set the new parse pointer */
+ *pp2parse = p2parse;
+ *pLenStr = lenStr;
+ return 0;
+}
+
+/* parse a legay-formatted syslog message.
+ */
+BEGINparse
+ uchar *p2parse;
+ int lenMsg;
+ int bTAGCharDetected;
+ int i; /* general index for parsing */
+ uchar bufParseTAG[CONF_TAG_MAXSIZE];
+ uchar bufParseHOSTNAME[CONF_HOSTNAME_MAXSIZE];
+ uchar *pBuf = NULL;
+CODESTARTparse
+ dbgprintf("Message will now be parsed by the legacy syslog parser with structured-data support.\n");
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ setProtocolVersion(pMsg, 0);
+
+ /* Check to see if msg contains a timestamp. We start by assuming
+ * that the message timestamp is the time of reception (which we
+ * generated ourselfs and then try to actually find one inside the
+ * message. There we go from high-to low precison and are done
+ * when we find a matching one. -- rgerhards, 2008-09-16
+ */
+ if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3339 */;
+ } else if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
+ } else if(*p2parse == ' ' && lenMsg > 1) { /* try to see if it is slighly malformed - HP procurve seems to do that sometimes */
+ ++p2parse; /* move over space */
+ --lenMsg;
+ if(datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &p2parse, &lenMsg) == RS_RET_OK) {
+ /* indeed, we got it! */
+ /* we are done - parse pointer is moved by ParseTIMESTAMP3164 */;
+ } else {/* parse pointer needs to be restored, as we moved it off-by-one
+ * for this try.
+ */
+ --p2parse;
+ ++lenMsg;
+ }
+ }
+
+ if(pMsg->msgFlags & IGNDATE) {
+ /* we need to ignore the msg data, so simply copy over reception date */
+ memcpy(&pMsg->tTIMESTAMP, &pMsg->tRcvdAt, sizeof(struct syslogTime));
+ }
+
+ /* rgerhards, 2006-03-13: next, we parse the hostname and tag. But we
+ * do this only when the user has not forbidden this. I now introduce some
+ * code that allows a user to configure rsyslogd to treat the rest of the
+ * message as MSG part completely. In this case, the hostname will be the
+ * machine that we received the message from and the tag will be empty. This
+ * is meant to be an interim solution, but for now it is in the code.
+ */
+ if(bParseHOSTNAMEandTAG && !(pMsg->msgFlags & INTERNAL_MSG)) {
+ /* parse HOSTNAME - but only if this is network-received!
+ * rger, 2005-11-14: we still have a problem with BSD messages. These messages
+ * do NOT include a host name. In most cases, this leads to the TAG to be treated
+ * as hostname and the first word of the message as the TAG. Clearly, this is not
+ * of advantage ;) I think I have now found a way to handle this situation: there
+ * are certain characters which are frequently used in TAG (e.g. ':'), which are
+ * *invalid* in host names. So while parsing the hostname, I check for these characters.
+ * If I find them, I set a simple flag but continue. After parsing, I check the flag.
+ * If it was set, then we most probably do not have a hostname but a TAG. Thus, I change
+ * the fields. I think this logic shall work with any type of syslog message.
+ * rgerhards, 2009-06-23: and I now have extended this logic to every character
+ * that is not a valid hostname.
+ */
+ bTAGCharDetected = 0;
+ if(lenMsg > 0 && pMsg->msgFlags & PARSE_HOSTNAME) {
+ i = 0;
+ while(i < lenMsg && (isalnum(p2parse[i]) || p2parse[i] == '.' || p2parse[i] == '.'
+ || p2parse[i] == '_' || p2parse[i] == '-') && i < (CONF_HOSTNAME_MAXSIZE - 1)) {
+ bufParseHOSTNAME[i] = p2parse[i];
+ ++i;
+ }
+
+ if(i == lenMsg) {
+ /* we have a message that is empty immediately after the hostname,
+ * but the hostname thus is valid! -- rgerhards, 2010-02-22
+ */
+ p2parse += i;
+ lenMsg -= i;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
+ } else if(i > 0 && p2parse[i] == ' ' && isalnum(p2parse[i-1])) {
+ /* we got a hostname! */
+ p2parse += i + 1; /* "eat" it (including SP delimiter) */
+ lenMsg -= i + 1;
+ bufParseHOSTNAME[i] = '\0';
+ MsgSetHOSTNAME(pMsg, bufParseHOSTNAME, i);
+ }
+ }
+
+ /* now parse TAG - that should be present in message from all sources.
+ * This code is somewhat not compliant with RFC 3164. As of 3164,
+ * the TAG field is ended by any non-alphanumeric character. In
+ * practice, however, the TAG often contains dashes and other things,
+ * which would end the TAG. So it is not desirable. As such, we only
+ * accept colon and SP to be terminators. Even there is a slight difference:
+ * a colon is PART of the TAG, while a SP is NOT part of the tag
+ * (it is CONTENT). Starting 2008-04-04, we have removed the 32 character
+ * size limit (from RFC3164) on the tag. This had bad effects on existing
+ * envrionments, as sysklogd didn't obey it either (probably another bug
+ * in RFC3164...). We now receive the full size, but will modify the
+ * outputs so that only 32 characters max are used by default.
+ */
+ i = 0;
+ while(lenMsg > 0 && *p2parse != ':' && *p2parse != ' ' && i < CONF_TAG_MAXSIZE) {
+ bufParseTAG[i++] = *p2parse++;
+ --lenMsg;
+ }
+ if(lenMsg > 0 && *p2parse == ':') {
+ ++p2parse;
+ --lenMsg;
+ bufParseTAG[i++] = ':';
+ }
+
+ /* no TAG can only be detected if the message immediatly ends, in which case an empty TAG
+ * is considered OK. So we do not need to check for empty TAG. -- rgerhards, 2009-06-23
+ */
+ bufParseTAG[i] = '\0'; /* terminate string */
+ MsgSetTAG(pMsg, bufParseTAG, i);
+ } else {/* we enter this code area when the user has instructed rsyslog NOT
+ * to parse HOSTNAME and TAG - rgerhards, 2006-03-13
+ */
+ if(!(pMsg->msgFlags & INTERNAL_MSG)) {
+ DBGPRINTF("HOSTNAME and TAG not parsed by user configuraton.\n");
+ }
+ }
+
+ CHKmalloc(pBuf = MALLOC(sizeof(uchar) * (lenMsg + 1)));
+
+ /* STRUCTURED-DATA */
+ if (parseRFCStructuredData(&p2parse, pBuf, &lenMsg) == 0)
+ MsgSetStructuredData(pMsg, (char*)pBuf);
+ else
+ MsgSetStructuredData(pMsg, "-");
+
+ /* The rest is the actual MSG */
+ MsgSetMSGoffs(pMsg, p2parse - pMsg->pszRawMsg);
+
+finalize_it:
+ if(pBuf != NULL)
+ free(pBuf);
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ dbgprintf("rfc3164sd parser init called\n");
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/pmsnare/Makefile.am b/plugins/pmsnare/Makefile.am
new file mode 100644
index 00000000..5b2696ac
--- /dev/null
+++ b/plugins/pmsnare/Makefile.am
@@ -0,0 +1,8 @@
+pkglib_LTLIBRARIES = pmsnare.la
+
+pmsnare_la_SOURCES = pmsnare.c
+pmsnare_la_CPPFLAGS = $(RSRT_CFLAGS) $(PTHREADS_CFLAGS) -I ../../tools
+pmsnare_la_LDFLAGS = -module -avoid-version
+pmsnare_la_LIBADD =
+
+EXTRA_DIST =
diff --git a/plugins/pmsnare/pmsnare.c b/plugins/pmsnare/pmsnare.c
new file mode 100644
index 00000000..f3658d11
--- /dev/null
+++ b/plugins/pmsnare/pmsnare.c
@@ -0,0 +1,239 @@
+/* pmsnare.c
+ *
+ * this detects logs sent by Snare and cleans them up so that they can be processed by the normal parser
+ *
+ * there are two variations of this, if the client is set to 'syslog' mode it sends
+ *
+ * <pri>timestamp<sp>hostname<sp>tag<tab>otherstuff
+ *
+ * if the client is not set to syslog it sends
+ *
+ * hostname<tab>tag<tab>otherstuff
+ *
+ * ToDo, take advantage of items in the message itself to set more friendly information
+ * where the normal parser will find it by re-writing more of the message
+ *
+ * Intereting information includes:
+ *
+ * in the case of windows snare messages:
+ * the system hostname is field 12
+ * the severity is field 3 (criticality ranging form 0 to 4)
+ * the source of the log is field 4 and may be able to be mapped to facility
+ *
+ *
+ * created 2010-12-13 by David Lang based on pmlastmsg
+ *
+ * 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 <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "glbl.h"
+#include "errmsg.h"
+#include "parser.h"
+#include "datetime.h"
+#include "unicode-helper.h"
+
+MODULE_TYPE_PARSER
+MODULE_TYPE_NOKEEP
+PARSER_NAME("rsyslog.snare")
+
+/* internal structures
+ */
+DEF_PMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+DEFobjCurrIf(glbl)
+DEFobjCurrIf(parser)
+DEFobjCurrIf(datetime)
+
+
+/* static data */
+static int bParseHOSTNAMEandTAG; /* cache for the equally-named global param - performance enhancement */
+
+
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATUREAutomaticSanitazion)
+ iRet = RS_RET_OK;
+ if(eFeat == sFEATUREAutomaticPRIParsing)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
+BEGINparse
+ uchar *p2parse;
+ int lenMsg;
+ int snaremessage;
+ int tablength;
+
+CODESTARTparse
+ #define TabRepresentation "#011"
+ tablength=sizeof(TabRepresentation);
+ dbgprintf("Message will now be parsed by fix Snare parser.\n");
+ assert(pMsg != NULL);
+ assert(pMsg->pszRawMsg != NULL);
+
+ /* check if this message is of the type we handle in this (very limited) parser
+
+ find out if we have a space separated or tab separated for the first item
+ if tab separated see if the second word is one of our expected tags
+ if so replace the tabs with spaces so that hostname and syslog tag are going to be parsed properly
+ optionally replace the hostname at the beginning of the message with one from later in the message
+ else, wrong message, abort
+ else, assume that we have a valid timestamp, move over to the syslog tag
+ if that is tab separated from the rest of the message and one of our expected tags
+ if so, replace the tab with a space so that it will be parsed properly
+ optionally replace the hostname at the beginning of the message withone from later in the message
+
+ */
+ snaremessage=0;
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ dbgprintf("pmsnare: msg to look at: [%d]'%s'\n", lenMsg, p2parse);
+ if((unsigned) lenMsg < 30) {
+ /* too short, can not be "our" message */
+ dbgprintf("msg too short!\n");
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+ }
+
+ while(lenMsg && *p2parse != ' ' && *p2parse != '\t' && *p2parse != '#') {
+ --lenMsg;
+ ++p2parse;
+ }
+ dbgprintf("pmsnare: separator [%d]'%s' msg after the first separator: [%d]'%s'\n", tablength,TabRepresentation,lenMsg, p2parse);
+ if ((lenMsg > tablength) && (*p2parse == '\t' || strncasecmp((char*) p2parse, TabRepresentation , tablength-1) == 0)) {
+ //if ((lenMsg > tablength) && (*p2parse == '\t' || *p2parse == '#')) {
+ dbgprintf("pmsnare: tab separated message\n");
+ if(strncasecmp((char*) (p2parse + tablength - 1), "MSWinEventLog", 13) == 0) {
+ snaremessage=13; /* 0 means not a snare message, a number is how long the tag is */
+ }
+ if(strncasecmp((char*) (p2parse + tablength - 1), "LinuxKAudit", 11) == 0) {
+ snaremessage=11; /* 0 means not a snare message, a number is how long the tag is */
+ }
+ if(snaremessage) {
+ /* replace the tab with a space and if needed move the message portion up by the length of TabRepresentation -2 characters to overwrite the extra : */
+ *p2parse = ' ';
+ lenMsg -=(tablength-2);
+ p2parse++;
+ lenMsg--;
+ memmove(p2parse, p2parse + (tablength-2), lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=(tablength-2);
+ pMsg->iLenMSG -=(tablength-2);
+ p2parse += snaremessage;
+ lenMsg -= snaremessage;
+ *p2parse = ' ';
+ p2parse++;
+ lenMsg--;
+ lenMsg -=(tablength-2);
+ memmove(p2parse, p2parse + (tablength-2), lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=(tablength-2);
+ pMsg->iLenMSG -=(tablength-2);
+ dbgprintf("found a Snare message with snare not set to send syslog messages\n");
+ }
+ } else {
+ /* go back to the beginning of the message */
+ lenMsg = pMsg->iLenRawMsg - pMsg->offAfterPRI; /* note: offAfterPRI is already the number of PRI chars (do not add one!) */
+ p2parse = pMsg->pszRawMsg + pMsg->offAfterPRI; /* point to start of text, after PRI */
+ /* skip over timestamp and space*/
+ lenMsg -=17;
+ p2parse +=17;
+ /* skip over what should be the hostname */
+ while(lenMsg && *p2parse != ' ') {
+ --lenMsg;
+ ++p2parse;
+ }
+ if (lenMsg){
+ --lenMsg;
+ ++p2parse;
+ }
+ dbgprintf("pmsnare: separator [%d]'%s' msg after the timestamp and hostname: [%d]'%s'\n", tablength,TabRepresentation,lenMsg, p2parse);
+ if(lenMsg > 13 && strncasecmp((char*) p2parse, "MSWinEventLog", 13) == 0) {
+ snaremessage=13; /* 0 means not a snare message, a number is how long the tag is */
+ }
+ if(lenMsg > 11 && strncasecmp((char*) p2parse, "LinuxKAudit", 11) == 0) {
+ snaremessage=11; /* 0 means not a snare message, a number is how long the tag is */
+ }
+ if(snaremessage) {
+ p2parse += snaremessage;
+ lenMsg -= snaremessage;
+ *p2parse = ' ';
+ p2parse++;
+ lenMsg--;
+ lenMsg -=(tablength-2);
+ memmove(p2parse, p2parse + (tablength-2), lenMsg);
+ *(p2parse + lenMsg) = '\n';
+ *(p2parse + lenMsg + 1) = '\0';
+ pMsg->iLenRawMsg -=(tablength-2);
+ pMsg->iLenMSG -=(tablength-2);
+ dbgprintf("found a Snare message with snare set to send syslog messages\n");
+ }
+
+ }
+ DBGPRINTF("pmsnare: new message: [%d]'%s'\n", lenMsg, pMsg->pszRawMsg + pMsg->offAfterPRI);
+ ABORT_FINALIZE(RS_RET_COULD_NOT_PARSE);
+
+finalize_it:
+ENDparse
+
+
+BEGINmodExit
+CODESTARTmodExit
+ /* release what we no longer need */
+ objRelease(errmsg, CORE_COMPONENT);
+ objRelease(glbl, CORE_COMPONENT);
+ objRelease(parser, CORE_COMPONENT);
+ objRelease(datetime, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_PMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(glbl, CORE_COMPONENT));
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+ CHKiRet(objUse(parser, CORE_COMPONENT));
+ CHKiRet(objUse(datetime, CORE_COMPONENT));
+
+ DBGPRINTF("snare parser init called, compiled with version %s\n", VERSION);
+ bParseHOSTNAMEandTAG = glbl.GetParseHOSTNAMEandTAG(); /* cache value, is set only during rsyslogd option processing */
+
+
+ENDmodInit
+
+/* vim:set ai:
+ */
diff --git a/plugins/sm_cust_bindcdr/Makefile.am b/plugins/sm_cust_bindcdr/Makefile.am
new file mode 100644
index 00000000..1f71d499
--- /dev/null
+++ b/plugins/sm_cust_bindcdr/Makefile.am
@@ -0,0 +1,6 @@
+pkglib_LTLIBRARIES = sm_cust_bindcdr.la
+
+sm_cust_bindcdr_la_SOURCES = sm_cust_bindcdr.c
+sm_cust_bindcdr_la_CPPFLAGS = -I$(top_srcdir) $(PTHREADS_CFLAGS) $(RSRT_CFLAGS)
+sm_cust_bindcdr_la_LDFLAGS = -module -avoid-version
+sm_cust_bindcdr_la_LIBADD =
diff --git a/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c
new file mode 100644
index 00000000..baad667e
--- /dev/null
+++ b/plugins/sm_cust_bindcdr/sm_cust_bindcdr.c
@@ -0,0 +1,390 @@
+/* sm_cust_bindcdr.c
+ * This is a custom developed plugin to process bind information into
+ * a specific SQL statement. While the actual processing may be too specific
+ * to be of general use, this module serves as a template on how this type
+ * of processing can be done.
+ *
+ * Format generated:
+ * "%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag%%msg:::sp-if-no-1st-sp%%msg:::drop-last-lf%\n"
+ * Note that this is the same as smtradfile.c, except that we do have a RFC3339 timestamp. However,
+ * we have copied over the code from there, it is too simple to go through all the hassle
+ * of having a single code base.
+ *
+ * NOTE: read comments in module-template.h to understand how this file
+ * works!
+ *
+ * File begun on 2011-03-17 by RGerhards
+ *
+ * Copyright 2011 Rainer Gerhards and Adiscon GmbH.
+ *
+ * This file is part of rsyslog.
+ *
+ * Rsyslog is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Rsyslog is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Rsyslog. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * A copy of the GPL can be found in the file "COPYING" in this distribution.
+ */
+#include "config.h"
+#include "rsyslog.h"
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include "conf.h"
+#include "syslogd-types.h"
+#include "cfsysline.h"
+#include "template.h"
+#include "msg.h"
+#include "module-template.h"
+#include "unicode-helper.h"
+#include "errmsg.h"
+
+MODULE_TYPE_STRGEN
+MODULE_TYPE_NOKEEP
+STRGEN_NAME("Custom_BindCDR,sql")
+
+/* internal structures
+ */
+DEF_SMOD_STATIC_DATA
+DEFobjCurrIf(errmsg)
+
+/* list of "allowed" IPs */
+typedef struct allowedip_s {
+ uchar *pszIP;
+ struct allowedip_s *next;
+} allowedip_t;
+
+static allowedip_t *root;
+
+
+/* config data */
+
+/* check if the provided IP is (already) in the allowed list
+ */
+static int
+isAllowed(uchar *pszIP)
+{
+ allowedip_t *pallow;
+ int ret = 0;
+
+ for(pallow = root ; pallow != NULL ; pallow = pallow->next) {
+ if(!ustrcmp(pallow->pszIP, pszIP)) {
+ ret = 1;
+ goto finalize_it;
+ }
+ }
+finalize_it: return ret;
+}
+
+/* This function is called to add an additional allowed IP. It adds
+ * the IP to the linked list of them. An error is emitted if the IP
+ * already exists.
+ */
+static rsRetVal addAllowedIP(void __attribute__((unused)) *pVal, uchar *pNewVal)
+{
+ allowedip_t *pNew;
+ DEFiRet;
+
+ if(isAllowed(pNewVal)) {
+ errmsg.LogError(0, NO_ERRCODE, "error: allowed IP '%s' already configured "
+ "duplicate ignored", pNewVal);
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ CHKmalloc(pNew = malloc(sizeof(allowedip_t)));
+ pNew->pszIP = pNewVal;
+ pNew->next = root;
+ root = pNew;
+ DBGPRINTF("sm_cust_bindcdr: allowed IP '%s' added.\n", pNewVal);
+
+finalize_it:
+ if(iRet != RS_RET_OK) {
+ free(pNewVal);
+ }
+
+ RETiRet;
+}
+
+/* This strgen tries to minimize the amount of reallocs be first obtaining pointers to all strings
+ * needed (including their length) and then calculating the actual space required. So when we
+ * finally copy, we know exactly what we need. So we do at most one alloc.
+ * An actual message sample for what we intend to parse is (one line):
+ <30>Mar 24 13:01:51 named[6085]: 24-Mar-2011 13:01:51.865 queries: info: client 10.0.0.96#39762: view trusted: query: 8.6.0.9.9.4.1.4.6.1.8.3.mobilecrawler.com IN TXT + (10.0.0.96)
+ */
+//previos dev: #define SQL_STMT "INSERT INTO CDR(`Date`,`Time`, timeMS, client, view, query, ip) VALUES ('"
+#define SQL_STMT "INSERT INTO CDR(`date`,ip,user,dest) VALUES ('"
+#define ADD_SQL_DELIM \
+ memcpy(*ppBuf + iBuf, "', '", sizeof("', '") - 1); \
+ iBuf += sizeof("', '") - 1;
+#define SQL_STMT_END "');\n"
+BEGINstrgen
+ int iBuf;
+ uchar *psz;
+ uchar szDate[64];
+ unsigned lenDate;
+ uchar szTime[64];
+ unsigned lenTime;
+ uchar szMSec[64];
+ unsigned lenMSec;
+ uchar szClient[64];
+ unsigned lenClient;
+ uchar szView[64];
+ unsigned lenView;
+ uchar szQuery[64];
+ unsigned lenQuery;
+ uchar szIP[64];
+ unsigned lenIP;
+ size_t lenTotal;
+CODESTARTstrgen
+ /* first create an empty statement. This is to be replaced if
+ * we have better data to fill in.
+ */
+ /* now make sure buffer is large enough */
+ if(*pLenBuf < 2)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, 2));
+ memcpy(*ppBuf, ";", sizeof(";"));
+
+ /* first obtain all strings and their length (if not fixed) */
+ /* Note that there are two date fields present, one in the header
+ * and one more in the actual message. We use the one from the message
+ * and parse that our. We check validity based on some fixe fields. In-
+ * depth verification is probably not worth the effort (CPU time), because
+ * we do various other checks on the message format below).
+ */
+ psz = getMSG(pMsg);
+ if(psz[0] == ' ' && psz[3] == '-' && psz[7] == '-') {
+ memcpy(szDate, psz+8, 4);
+ szDate[4] = '-';
+ if(!strncmp((char*)psz+4, "Jan", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '1';
+ } else if(!strncmp((char*)psz+4, "Feb", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '2';
+ } else if(!strncmp((char*)psz+4, "Mar", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '3';
+ } else if(!strncmp((char*)psz+4, "Apr", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '4';
+ } else if(!strncmp((char*)psz+4, "May", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '5';
+ } else if(!strncmp((char*)psz+4, "Jun", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '6';
+ } else if(!strncmp((char*)psz+4, "Jul", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '7';
+ } else if(!strncmp((char*)psz+4, "Aug", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '8';
+ } else if(!strncmp((char*)psz+4, "Sep", 3)) {
+ szDate[5] = '0';
+ szDate[6] = '9';
+ } else if(!strncmp((char*)psz+4, "Oct", 3)) {
+ szDate[5] = '1';
+ szDate[6] = '0';
+ } else if(!strncmp((char*)psz+4, "Nov", 3)) {
+ szDate[5] = '1';
+ szDate[6] = '1';
+ } else if(!strncmp((char*)psz+4, "Dec", 3)) {
+ szDate[5] = '1';
+ szDate[6] = '2';
+ }
+ szDate[7] = '-';
+ szDate[8] = psz[1];
+ szDate[9] = psz[2];
+ szDate[10] = '\0';
+ lenDate = 10;
+ } else {
+ dbgprintf("Custom_BindCDR: date part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* now time (pull both regular time and ms) */
+ if(psz[12] == ' ' && psz[15] == ':' && psz[18] == ':' && psz[21] == '.' && psz[25] == ' ') {
+ memcpy(szTime, (char*)psz+13, 8);
+ szTime[9] = '\0';
+ lenTime = 8;
+ memcpy(szMSec, (char*)psz+22, 3);
+ szMSec[4] = '\0';
+ lenMSec = 3;
+ } else {
+ dbgprintf("Custom_BindCDR: date part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* "client" */
+ psz = (uchar*) strstr((char*) getMSG(pMsg), "client ");
+ if(psz == NULL) {
+ dbgprintf("Custom_BindCDR: client part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ } else {
+ psz += sizeof("client ") - 1; /* skip "label" */
+ for( lenClient = 0
+ ; *psz && *psz != '#' && lenClient < sizeof(szClient) - 1
+ ; ++lenClient) {
+ szClient[lenClient] = *psz++;
+ }
+ szClient[lenClient] = '\0';
+ }
+
+ /* "view" */
+ psz = (uchar*) strstr((char*) getMSG(pMsg), "view ");
+ if(psz == NULL) {
+ dbgprintf("Custom_BindCDR: view part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ } else {
+ psz += sizeof("view ") - 1; /* skip "label" */
+ for( lenView = 0
+ ; *psz && *psz != ':' && lenView < sizeof(szView) - 1
+ ; ++lenView) {
+ szView[lenView] = *psz++;
+ }
+ szView[lenView] = '\0';
+ }
+
+ /* "query" - we must extract just the number, and in reverse! */
+ psz = (uchar*) strstr((char*) getMSG(pMsg), "query: ");
+ if(psz == NULL) {
+ dbgprintf("Custom_BindCDR: query part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ } else {
+ psz += sizeof("query: ") - 1; /* skip "label" */
+ /* first find end-of-strihttp://www.rsyslog.com/doc/omruleset.htmlng to process */
+ while(*psz && (isdigit(*psz) || *psz == '.')) {
+ psz++;
+ }
+ /* now shuffle data */
+ for( lenQuery = 0
+ ; *psz && *psz != ' ' && lenQuery < sizeof(szQuery) - 1
+ ; --psz) {
+ if(isdigit(*psz))
+ szQuery[lenQuery++] = *psz;
+ }
+ szQuery[lenQuery] = '\0';
+ }
+
+ /* "ip" */
+ psz = (uchar*) strstr((char*) getMSG(pMsg), "IN TXT + (");
+ if(psz == NULL) {
+ dbgprintf("Custom_BindCDR: ip part in msg missing\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ } else {
+ psz += sizeof("IN TXT + (") - 1; /* skip "label" */
+ for( lenIP = 0
+ ; *psz && *psz != ')' && lenIP < sizeof(szIP) - 1
+ ; ++lenIP) {
+ szIP[lenIP] = *psz++;
+ }
+ szIP[lenIP] = '\0';
+ }
+
+
+ /* --- strings extracted ---- */
+
+ /* now check if the IP is "allowed", in which case we should not
+ * insert into the database.
+ */
+ if(isAllowed(szIP)) {
+ DBGPRINTF("sm_cust_bindcdr: message from allowed IP, ignoring\n");
+ ABORT_FINALIZE(RS_RET_ERR);
+ }
+
+ /* calculate len, constants for spaces and similar fixed strings */
+ lenTotal = lenDate + lenTime + lenMSec + lenClient + lenView + lenQuery
+ + lenIP + 7 * 5 + sizeof(SQL_STMT) + sizeof(SQL_STMT_END) + 2;
+
+ /* now make sure buffer is large enough */
+ if(lenTotal >= *pLenBuf)
+ CHKiRet(ExtendBuf(ppBuf, pLenBuf, lenTotal));
+
+ /* and concatenate the resulting string */
+ memcpy(*ppBuf, SQL_STMT, sizeof(SQL_STMT) - 1);
+ iBuf = sizeof(SQL_STMT) - 1;
+
+ memcpy(*ppBuf + iBuf, szDate, lenDate);
+ iBuf += lenDate;
+ /* prviously: ADD_SQL_DELIM */
+ *(*ppBuf + iBuf) = ' ';
+ ++iBuf;
+
+ memcpy(*ppBuf + iBuf, szTime, lenTime);
+ iBuf += lenTime;
+ ADD_SQL_DELIM
+
+ /* we shall now discard this part
+ memcpy(*ppBuf + iBuf, szMSec, lenMSec);
+ iBuf += lenMSec;
+ ADD_SQL_DELIM
+ */
+
+ /* Note that this seem to be the IP to use */
+ memcpy(*ppBuf + iBuf, szClient, lenClient);
+ iBuf += lenClient;
+ ADD_SQL_DELIM
+
+ memcpy(*ppBuf + iBuf, szView, lenView);
+ iBuf += lenView;
+ ADD_SQL_DELIM
+
+ memcpy(*ppBuf + iBuf, szQuery, lenQuery);
+ iBuf += lenQuery;
+ /* this is now the last field, so we dont need: ADD_SQL_DELIM */
+
+ /* no longer to be included
+ memcpy(*ppBuf + iBuf, szIP, lenIP);
+ iBuf += lenIP;
+ */
+
+ /* end of SQL statement/trailer (NUL is contained in string!) */
+ memcpy(*ppBuf + iBuf, SQL_STMT_END, sizeof(SQL_STMT_END));
+ iBuf += sizeof(SQL_STMT_END);
+
+finalize_it:
+ENDstrgen
+
+
+BEGINmodExit
+ allowedip_t *pallow, *pdel;
+CODESTARTmodExit
+ for(pallow = root ; pallow != NULL ; ) {
+ pdel = pallow;
+ pallow = pallow->next;
+ free(pdel->pszIP);
+ free(pdel);
+ }
+
+ objRelease(errmsg, CORE_COMPONENT);
+ENDmodExit
+
+
+BEGINqueryEtryPt
+CODESTARTqueryEtryPt
+CODEqueryEtryPt_STD_SMOD_QUERIES
+ENDqueryEtryPt
+
+
+BEGINmodInit()
+CODESTARTmodInit
+ *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
+CODEmodInit_QueryRegCFSLineHdlr
+ CHKiRet(objUse(errmsg, CORE_COMPONENT));
+
+ root = NULL;
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"sgcustombindcdrallowedip", 0, eCmdHdlrGetWord,
+ addAllowedIP, NULL, STD_LOADABLE_MODULE_ID));
+ dbgprintf("rsyslog sm_cust_bindcdr called, compiled with version %s\n", VERSION);
+ENDmodInit