summaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/imdiag/imdiag.c24
-rw-r--r--plugins/imfile/imfile.c38
-rw-r--r--plugins/imgssapi/imgssapi.c17
-rw-r--r--plugins/imklog/bsd.c3
-rw-r--r--plugins/imklog/imklog.c1
-rw-r--r--plugins/imklog/ksym.c3
-rw-r--r--plugins/imklog/ksym_mod.c3
-rw-r--r--plugins/immark/immark.c28
-rw-r--r--plugins/impstats/Makefile.am6
-rw-r--r--plugins/impstats/impstats.c228
-rw-r--r--plugins/imptcp/imptcp.c1
-rw-r--r--plugins/imsolaris/imsolaris.c26
-rw-r--r--plugins/imtcp/imtcp.c17
-rw-r--r--plugins/imtemplate/imtemplate.c41
-rw-r--r--plugins/imudp/imudp.c270
-rw-r--r--plugins/imuxsock/Makefile.am4
-rw-r--r--plugins/imuxsock/imuxsock.c739
-rw-r--r--plugins/omdbalerting/Makefile.am8
-rw-r--r--plugins/omdbalerting/omdbalerting.c144
-rw-r--r--plugins/omgssapi/omgssapi.c8
-rw-r--r--plugins/omhdfs/Makefile.am6
-rw-r--r--plugins/omhdfs/omhdfs.c475
-rw-r--r--plugins/ommail/ommail.c6
-rw-r--r--plugins/ompgsql/ompgsql.c39
-rw-r--r--plugins/omrelp/omrelp.c3
-rw-r--r--plugins/omruleset/Makefile.am8
-rw-r--r--plugins/omruleset/omruleset.c231
-rw-r--r--plugins/omsnmp/omsnmp.c2
-rw-r--r--plugins/omtesting/omtesting.c159
-rw-r--r--plugins/omudpspoof/Makefile.am8
-rw-r--r--plugins/omudpspoof/omudpspoof.c502
-rw-r--r--plugins/pmlastmsg/Makefile.am8
-rw-r--r--plugins/pmlastmsg/pmlastmsg.c175
-rw-r--r--plugins/pmrfc3164sd/Makefile.am8
-rw-r--r--plugins/pmrfc3164sd/pmrfc3164sd.c343
35 files changed, 3281 insertions, 301 deletions
diff --git a/plugins/imdiag/imdiag.c b/plugins/imdiag/imdiag.c
index bf972191..81b357ef 100644
--- a/plugins/imdiag/imdiag.c
+++ b/plugins/imdiag/imdiag.c
@@ -213,7 +213,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 +245,7 @@ injectMsg(uchar *pszCmd, tcps_sess_t *pSess)
doInjectMsg(i + iFrom);
}
- CHKiRet(sendResponse(pSess, "messages injected\n"));
+ CHKiRet(sendResponse(pSess, "%d messages injected\n", nMsgs));
finalize_it:
RETiRet;
@@ -259,12 +258,23 @@ static rsRetVal
waitMainQEmpty(tcps_sess_t *pSess)
{
int iMsgQueueSize;
+ int iPrint = 0;
DEFiRet;
CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
while(iMsgQueueSize > 0) {
+ /* DEV DEBUG ONLY if(iPrint++ % 500)
+ printf("imdiag: main msg queue size: %d\n", iMsgQueueSize);
+ */
+ if(iPrint++ % 500 == 0)
+ dbgprintf("imdiag sleeping, wait mainq drain, curr size %d\n", iMsgQueueSize);
srSleep(0,2); /* wait a little bit */
CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ if(iMsgQueueSize == 0) {
+ /* verify that queue is still empty (else it could just be a race!) */
+ srSleep(1,5); /* wait a little bit */
+ CHKiRet(diagGetMainMsgQSize(&iMsgQueueSize));
+ }
}
CHKiRet(sendResponse(pSess, "mainqueue empty\n"));
@@ -291,12 +301,13 @@ 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)));
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));
@@ -443,10 +454,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 91493bb1..8681ac8c 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,6 +47,7 @@
#include "datetime.h"
#include "unicode-helper.h"
#include "prop.h"
+#include "stringbuf.h"
MODULE_TYPE_INPUT /* must be present for input modules, do not remove */
@@ -113,7 +114,6 @@ 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;
CHKiRet(submitMsg(pMsg));
finalize_it:
RETiRet;
@@ -223,7 +223,7 @@ static rsRetVal pollFile(fileInfo_t *pThis, int *pbHadFileData)
}
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
@@ -286,28 +286,22 @@ 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 *
- * ------------------------------------------------------------------------------------------ */
+ while(1) {
- do {
- bHadFileData = 0;
- for(i = 0 ; i < iFilPtr ; ++i) {
- pollFile(&files[i], &bHadFileData);
- }
- } while(iFilPtr > 1 && bHadFileData == 1); /* waring: do...while()! */
+ do {
+ bHadFileData = 0;
+ for(i = 0 ; i < iFilPtr ; ++i) {
+ pollFile(&files[i], &bHadFileData);
+ }
+ } while(iFilPtr > 1 && bHadFileData == 1); /* 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
- */
- srSleep(iPollInterval, 10);
+ /* Note: the additional 10ns wait is vitally important. It guards rsyslog against totally
+ * hogging the CPU if the users selects a polling interval of 0 seconds. It doesn't hurt any
+ * other valid scenario. So do not remove. -- rgerhards, 2008-02-14
+ */
+ srSleep(iPollInterval, 10);
- /* ------------------------------------------------------------------------------------------ *
- * DO NOT TOUCH the following code - it will soon be part of the module generation macros! */
}
/*NOTREACHED*/
@@ -359,7 +353,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));
diff --git a/plugins/imgssapi/imgssapi.c b/plugins/imgssapi/imgssapi.c
index 1aad6622..dd3d67e3 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,6 +57,7 @@
#include "errmsg.h"
#include "netstrm.h"
#include "glbl.h"
+#include "debug.h"
#include "unlimited_select.h"
@@ -177,10 +179,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;
@@ -331,6 +333,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));
}
@@ -408,7 +411,7 @@ 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);
@@ -682,9 +685,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..c59ce04f 100644
--- a/plugins/imklog/imklog.c
+++ b/plugins/imklog/imklog.c
@@ -111,7 +111,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/immark/immark.c b/plugins/immark/immark.c
index 8504f872..5d48369e 100644
--- a/plugins/immark/immark.c
+++ b/plugins/immark/immark.c
@@ -42,6 +42,8 @@
#include "module-template.h"
#include "errmsg.h"
#include "msg.h"
+#include "srUtils.h"
+#include "glbl.h"
MODULE_TYPE_INPUT
@@ -50,8 +52,16 @@ MODULE_TYPE_INPUT
/* 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 +81,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 +113,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 +127,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..1312a4e8
--- /dev/null
+++ b/plugins/impstats/impstats.c
@@ -0,0 +1,228 @@
+/* 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
+
+/* 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));
+ CHKiRet(omsdRegCFSLineHdlr((uchar *)"pstatsinterval", 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/imptcp.c b/plugins/imptcp/imptcp.c
index 2afb340f..6449ad62 100644
--- a/plugins/imptcp/imptcp.c
+++ b/plugins/imptcp/imptcp.c
@@ -505,7 +505,6 @@ doSubmitMsg(ptcpsess_t *pThis, struct syslogTime *stTime, time_t ttGenTime, mult
MsgSetInputName(pMsg, pThis->pSrv->pInputName);
MsgSetFlowControlType(pMsg, eFLOWCTL_LIGHT_DELAY);
pMsg->msgFlags = NEEDS_PARSING | PARSE_HOSTNAME;
- pMsg->bParseHOSTNAME = 1;
MsgSetRcvFrom(pMsg, pThis->peerName);
CHKiRet(MsgSetRcvFromIP(pMsg, pThis->peerIP));
MsgSetRuleset(pMsg, pThis->pSrv->pRuleset);
diff --git a/plugins/imsolaris/imsolaris.c b/plugins/imsolaris/imsolaris.c
index 6b07ba2b..f801833b 100644
--- a/plugins/imsolaris/imsolaris.c
+++ b/plugins/imsolaris/imsolaris.c
@@ -205,7 +205,6 @@ readLog(int fd, uchar *pRcv, int iMaxLine)
MsgSetHOSTNAME(pMsg, glbl.GetLocalHostName(), ustrlen(glbl.GetLocalHostName()));
pMsg->iFacility = LOG_FAC(hdr.pri);
pMsg->iSeverity = LOG_PRI(hdr.pri);
- pMsg->bParseHOSTNAME = 0;
pMsg->msgFlags = NEEDS_PARSING | NO_PRI_IN_RAW;
CHKiRet(submitMsg(pMsg));
}
@@ -224,7 +223,7 @@ finalize_it:
* rgerhards, 2010-04-19
*/
static inline rsRetVal
-getMsgs(int timeout)
+getMsgs(thrdInfo_t *pThrd, int timeout)
{
DEFiRet;
int nfds;
@@ -247,12 +246,14 @@ getMsgs(int timeout)
CHKmalloc(pRcv = (uchar*) malloc(sizeof(uchar) * (iMaxLine + 1)));
}
- do {
+ 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 */
- /* v5-TODO: here we must check if we should terminante! */
+ if(pThrd->bShallStop == TRUE) {
+ break;
+ }
if(nfds == 0) {
if(timeout == 0) {
@@ -290,9 +291,7 @@ getMsgs(int timeout)
readLog(sun_Pfd.fd, pRcv, iMaxLine);
}
- } while(1); /* TODO: in v5, we must check the termination predicate */
-
- /* Note: in v4, this code is never reached (our thread will be cancelled) */
+ }
finalize_it:
if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1)
@@ -311,7 +310,7 @@ CODESTARTrunInput
*/
DBGPRINTF("imsolaris: doing startup poll before openeing door()\n");
- CHKiRet(getMsgs(0));
+ 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
@@ -319,8 +318,9 @@ CODESTARTrunInput
*/
sun_open_door();
DBGPRINTF("imsolaris: starting regular poll loop\n");
- iRet = getMsgs(-1); /* this is the primary poll loop, infinite timeout */
+ iRet = getMsgs(pThrd, -1); /* this is the primary poll loop, infinite timeout */
+ DBGPRINTF("imsolaris: terminating (bShallStop=%d)\n", pThrd->bShallStop);
finalize_it:
RETiRet;
ENDrunInput
@@ -359,9 +359,17 @@ CODESTARTmodExit
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,
diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index d122e976..0cfae057 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -86,6 +86,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 +98,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 +172,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 +199,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 +256,13 @@ CODESTARTafterRun
ENDafterRun
+BEGINisCompatibleWithFeature
+CODESTARTisCompatibleWithFeature
+ if(eFeat == sFEATURENonCancelInputTermination)
+ iRet = RS_RET_OK;
+ENDisCompatibleWithFeature
+
+
BEGINmodExit
CODESTARTmodExit
if(pOurTcpsrv != NULL)
@@ -281,6 +290,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 +303,7 @@ resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unus
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
+CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES
ENDqueryEtryPt
@@ -326,6 +337,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..e5e43025 100644
--- a/plugins/imtemplate/imtemplate.c
+++ b/plugins/imtemplate/imtemplate.c
@@ -77,6 +77,7 @@
#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 */
@@ -137,7 +138,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 +232,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 +291,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/imudp.c b/plugins/imudp/imudp.c
index d76f3544..ce0856be 100644
--- a/plugins/imudp/imudp.c
+++ b/plugins/imudp/imudp.c
@@ -32,6 +32,9 @@
#include <errno.h>
#include <unistd.h>
#include <netdb.h>
+#if HAVE_SYS_EPOLL_H
+# include <sys/epoll.h>
+#endif
#include "rsyslog.h"
#include "dirty.h"
#include "net.h"
@@ -44,8 +47,8 @@
#include "parser.h"
#include "datetime.h"
#include "prop.h"
+#include "ruleset.h"
#include "unicode-helper.h"
-#include "unlimited_select.h"
MODULE_TYPE_INPUT
@@ -58,7 +61,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"
@@ -66,13 +71,14 @@ 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 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 */
@@ -91,6 +97,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
@@ -111,26 +118,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;
}
}
}
@@ -142,7 +162,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)
@@ -163,7 +182,6 @@ finalize_it:
free(pszName); /* no longer needed */
RETiRet;
}
-#endif
/* This function is a helper to runInput. I have extracted it
@@ -181,8 +199,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;
@@ -196,8 +214,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) {
@@ -206,7 +227,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)
@@ -214,37 +235,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);
}
@@ -252,11 +275,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));
}
}
@@ -271,45 +295,99 @@ finalize_it:
}
-/* 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).
+/* 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
- int maxfds;
+#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;
- uchar fromHost[NI_MAXHOST];
- uchar fromHostIP[NI_MAXHOST];
- uchar fromHostFQDN[NI_MAXHOST];
-#ifdef USE_UNLIMITED_SELECT
- fd_set *pReadfds = malloc(glbl.GetFdSetSize());
-#else
- fd_set readfds;
- fd_set *pReadfds = &readfds;
-#endif
+ struct epoll_event *udpEPollEvt = NULL;
+ struct epoll_event currEvt[NUM_EPOLL_EVENTS];
+ char errStr[1024];
-CODESTARTrunInput
/* start "name caching" algo by making sure the previous system indicator
* is invalidated.
*/
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.
+
+ 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);
+# else
+ DBGPRINTF("imudp uses epoll_create()\n");
+ efd = epoll_create(NUM_EPOLL_EVENTS);
+# endif
+ 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;
+
+ /* start "name caching" algo by making sure the previous system indicator
+ * is invalidated.
+ */
+ bIsPermitted = 0;
+ memset(&frominetPrev, 0, sizeof(frominetPrev));
+ DBGPRINTF("imudp uses select()\n");
+
while(1) {
/* Add the Unix Domain Sockets to the list of read
* descriptors.
@@ -318,40 +396,51 @@ CODESTARTrunInput
* is given without -a, we do not need to listen at all..
*/
maxfds = 0;
- FD_ZERO (pReadfds);
+ FD_ZERO(&readfds);
/* Add the UDP listen sockets to the list of read descriptors. */
for (i = 0; i < *udpLstnSocks; i++) {
if (udpLstnSocks[i+1] != -1) {
if(Debug)
net.debugListenInfo(udpLstnSocks[i+1], "UDP");
- FD_SET(udpLstnSocks[i+1], pReadfds);
+ FD_SET(udpLstnSocks[i+1], &readfds);
if(udpLstnSocks[i+1]>maxfds) maxfds=udpLstnSocks[i+1];
}
}
if(Debug) {
dbgprintf("--------imUDP calling select, active file descriptors (max %d): ", maxfds);
for (nfds = 0; nfds <= maxfds; ++nfds)
- if ( FD_ISSET(nfds, pReadfds) )
+ if(FD_ISSET(nfds, &readfds))
dbgprintf("%d ", nfds);
dbgprintf("\n");
}
/* wait for io to become ready */
- nfds = select(maxfds+1, (fd_set *) pReadfds, NULL, NULL, NULL);
+ 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], pReadfds)) {
- processSocket(udpLstnSocks[i+1], &frominetPrev, &bIsPermitted,
- fromHost, fromHostFQDN, fromHostIP);
+ if(FD_ISSET(udpLstnSocks[i+1], &readfds)) {
+ 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() */
}
- freeFdSet(pReadfds);
- 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
@@ -364,6 +453,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)
@@ -371,9 +461,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
@@ -385,6 +473,8 @@ CODESTARTafterRun
if(udpLstnSocks != NULL) {
net.closeUDPListenSockets(udpLstnSocks);
udpLstnSocks = NULL;
+ free(udpRulesets);
+ udpRulesets = NULL;
}
if(pRcvBuf != NULL) {
free(pRcvBuf);
@@ -402,13 +492,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)
@@ -417,10 +516,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;
}
@@ -434,13 +529,12 @@ 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,
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 daa3bb47..0eee1122 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"
@@ -45,12 +47,17 @@
#include "glbl.h"
#include "msg.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
/* defines */
-#define MAXFUNIX 20
+#define MAXFUNIX 50
#ifndef _PATH_LOG
#ifdef BSD
#define _PATH_LOG "/var/run/log"
@@ -59,6 +66,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
@@ -70,29 +81,156 @@ DEF_IMOD_STATIC_DATA
DEFobjCurrIf(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
+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 int funixCreateSockPath[MAXFUNIX] = { 0, }; /* auto-creation of socket directory? */
-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 bSysSockFromSystemd = 0; /* Did we receive the system socket from systemd? */
/* 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? */
-#define DFLT_bCreateSockPath 0
-static int bCreateSockPath = DFLT_bCreateSockPath; /* auto-create socket path? */
+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
@@ -102,7 +240,7 @@ static int bCreateSockPath = DFLT_bCreateSockPath; /* auto-create socket path? *
static rsRetVal setSystemLogTimestampIgnore(void __attribute__((unused)) *pVal, int iNewVal)
{
DEFiRet;
- funixFlags[0] = iNewVal ? IGNDATE : NOFLAG;
+ listeners[0].flags = iNewVal ? IGNDATE : NOFLAG;
RETiRet;
}
@@ -111,7 +249,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;
}
@@ -123,78 +261,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;
- funixCreateSockPath[nfunix] = bCreateSockPath;
- 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(listeners[i].hostName != NULL) {
+ prop.Destruct(&(listeners[i].hostName));
}
- if(funixHName[i] != NULL) {
- free(funixHName[i]);
- funixHName[i] = NULL;
+ 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, int bCreatePath)
+/* 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;
- if(bCreatePath) {
- makeFileParentDirs((uchar*)path, strlen(path), 0755, -1, -1, 0);
+ 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);
}
- (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);
+finalize_it:
+ RETiRet;
+}
+
+
+static inline rsRetVal
+openLogSocket(lstn_t *pLstn)
+{
+ DEFiRet;
+ int one;
+
+ if(pLstn->sockName[0] == '\0')
return -1;
+
+ if (ustrcmp(pLstn->sockName, UCHAR_CONSTANT(_PATH_LOG)) == 0) {
+ bSysSockFromSystemd = 0; /* set default */
+ int r;
+
+ /* System log socket code. Check whether an FD was passed in from systemd. If
+ * so, it's the /dev/log socket, so use it. */
+
+ r = sd_listen_fds(0);
+ if (r < 0) {
+ errmsg.LogError(-r, NO_ERRCODE, "Failed to acquire systemd socket");
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+
+ if (r > 1) {
+ errmsg.LogError(EINVAL, NO_ERRCODE, "Wrong number of systemd sockets passed");
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+
+ if (r == 1) {
+ pLstn->fd = SD_LISTEN_FDS_START;
+ r = sd_is_socket_unix(pLstn->fd, SOCK_DGRAM, -1, _PATH_LOG, 0);
+ if (r < 0) {
+ errmsg.LogError(-r, NO_ERRCODE, "Failed to verify systemd socket type");
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+
+ if (!r) {
+ errmsg.LogError(EINVAL, NO_ERRCODE, "Passed systemd socket of wrong type");
+ ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
+ }
+ bSysSockFromSystemd = 1; /* indicate we got the socket from systemd */
+ } else {
+ CHKiRet(createLogSocket(pLstn));
+ }
+ } else {
+ 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;
}
- return fd;
+
+ 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.
+ */
+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;
+
+ 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, ratelimitInterval, pLstn->ratelimitBurst);
+ r = hashtable_insert(pLstn->ht, keybuf, rl);
+ if(r == 0)
+ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY);
+ }
+
+ *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 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;
+
+ parse++; lenMsg--; /* '<' */
+ pri = 0;
+ while(lenMsg && isdigit(*parse)) {
+ pri = pri * 10 + *parse - '0';
+ ++parse;
+ --lenMsg;
+ }
+ 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);
+ MsgSetInputName(pMsg, pInputName);
+ MsgSetFlowControlType(pMsg, pLstn->flowCtl);
+
+ pMsg->iFacility = facil;
+ pMsg->iSeverity = sever;
+ MsgSetAfterPRIOffs(pMsg, lenRcv - lenMsg);
+
+ 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, lenRcv - 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;
}
@@ -205,15 +602,20 @@ static int create_unix_socket(const char *path, int bCreatePath)
* 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;
+ struct cmsghdr *cm;
+ 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();
@@ -226,21 +628,45 @@ 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(pLstn->bUseCreds) {
+ memset(&aux, 0, sizeof(aux));
+ msgh.msg_control = aux;
+ msgh.msg_controllen = sizeof(aux);
+ }
+ 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:
@@ -279,10 +705,11 @@ CODESTARTrunInput
maxfds = 0;
FD_ZERO (pReadfds);
/* Copy master connections */
- for (i = startIndexUxLocalSockets; i < nfunix; i++) {
- if (funix[i] != -1) {
- FD_SET(funix[i], pReadfds);
- 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;
}
}
@@ -296,15 +723,20 @@ CODESTARTrunInput
/* wait for io to become ready */
nfds = select(maxfds+1, (fd_set *) pReadfds, NULL, NULL, NULL);
-
- for (i = 0; i < nfunix && nfds > 0; i++) {
- if ((fd = funix[i]) != -1 && FD_ISSET(fd, pReadfds)) {
- readSocket(fd, i);
+ 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
@@ -313,6 +745,7 @@ ENDrunInput
BEGINwillRun
CODESTARTwillRun
register int i;
+ int actSocks;
/* first apply some config settings */
# ifdef OS_SOLARIS
@@ -326,12 +759,33 @@ CODESTARTwillRun
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;
/* initialize and return if will run or not */
- for (i = startIndexUxLocalSockets ; i < nfunix ; i++) {
- if ((funix[i] = create_unix_socket((char*) funixn[i], funixCreateSockPath[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("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) */
@@ -348,40 +802,58 @@ 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 = startIndexUxLocalSockets; 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. If systemd passed us a socket it is
+ * systemd's job to clean it up.*/
+ if(bSysSockFromSystemd) {
+ DBGPRINTF("imuxsock: got system socket from systemd, not unlinking it\n");
+ i = 1;
+ } else
+ i = startIndexUxLocalSockets;
+ for(; i < nfd; i++)
+ if (listeners[i].sockName && listeners[i].fd != -1) {
+ 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(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)
@@ -396,11 +868,19 @@ static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __a
pLogHostName = NULL;
}
- discardFunixn();
- nfunix = 1;
+ discardLogSockets();
+ nfd = 1;
bIgnoreTimestamp = 1;
bUseFlowCtl = 0;
- bCreateSockPath = DFLT_bCreateSockPath;
+ 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;
}
@@ -414,15 +894,36 @@ 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));
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));
@@ -435,9 +936,17 @@ CODEmodInit_QueryRegCFSLineHdlr
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary,
NULL, &bUseFlowCtl, STD_LOADABLE_MODULE_ID));
CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary,
- NULL, &bCreateSockPath, STD_LOADABLE_MODULE_ID));
+ 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
@@ -450,6 +959,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/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..2e04391c
--- /dev/null
+++ b/plugins/omdbalerting/omdbalerting.c
@@ -0,0 +1,144 @@
+/* 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
+
+/* 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..605e5ed9 100644
--- a/plugins/omgssapi/omgssapi.c
+++ b/plugins/omgssapi/omgssapi.c
@@ -193,7 +193,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 +409,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 +563,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..9705b7fd
--- /dev/null
+++ b/plugins/omhdfs/omhdfs.c
@@ -0,0 +1,475 @@
+/* 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
+
+/* 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/ommail/ommail.c b/plugins/ommail/ommail.c
index 3a7669c9..324e1a77 100644
--- a/plugins/ommail/ommail.c
+++ b/plugins/ommail/ommail.c
@@ -50,6 +50,7 @@
#include "cfsysline.h"
#include "module-template.h"
#include "errmsg.h"
+#include "datetime.h"
#include "glbl.h"
MODULE_TYPE_OUTPUT
@@ -59,6 +60,7 @@ MODULE_TYPE_OUTPUT
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 +480,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 +671,7 @@ CODESTARTmodExit
freeConfigVariables();
/* release what we no longer need */
+ objRelease(datetime, CORE_COMPONENT);
objRelease(glbl, CORE_COMPONENT);
objRelease(errmsg, CORE_COMPONENT);
ENDmodExit
@@ -698,6 +701,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/ompgsql/ompgsql.c b/plugins/ompgsql/ompgsql.c
index eb774835..ffdcc532 100644
--- a/plugins/ompgsql/ompgsql.c
+++ b/plugins/ompgsql/ompgsql.c
@@ -65,6 +65,8 @@ typedef struct _instanceData {
} instanceData;
+static rsRetVal writePgSQL(uchar *psz, instanceData *pData);
+
BEGINcreateInstance
CODESTARTcreateInstance
ENDcreateInstance
@@ -189,7 +191,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 +230,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 +345,7 @@ ENDmodExit
BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_OMOD_QUERIES
+CODEqueryEtryPt_TXIF_OMOD_QUERIES /* we support the transactional interface! */
ENDqueryEtryPt
@@ -322,6 +354,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/omrelp/omrelp.c b/plugins/omrelp/omrelp.c
index d5ef8b4f..349e45aa 100644
--- a/plugins/omrelp/omrelp.c
+++ b/plugins/omrelp/omrelp.c
@@ -43,6 +43,7 @@
#include "module-template.h"
#include "glbl.h"
#include "errmsg.h"
+#include "debug.h"
MODULE_TYPE_OUTPUT
@@ -260,7 +261,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..0e0fc13b
--- /dev/null
+++ b/plugins/omruleset/omruleset.c
@@ -0,0 +1,231 @@
+/* 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
+
+/* 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..b973b09d 100644
--- a/plugins/omsnmp/omsnmp.c
+++ b/plugins/omsnmp/omsnmp.c
@@ -222,7 +222,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/omtesting/omtesting.c b/plugins/omtesting/omtesting.c
index 411bcf88..c474bb41 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,12 +46,15 @@
#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
@@ -59,9 +62,18 @@ MODULE_TYPE_OUTPUT
*/
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 +97,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 +214,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 +236,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 +254,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 +310,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..3ead5447
--- /dev/null
+++ b/plugins/omudpspoof/omudpspoof.c
@@ -0,0 +1,502 @@
+/* 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
+
+/* 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/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..275f1c1f
--- /dev/null
+++ b/plugins/pmlastmsg/pmlastmsg.c
@@ -0,0 +1,175 @@
+/* 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
+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..5598c025
--- /dev/null
+++ b/plugins/pmrfc3164sd/pmrfc3164sd.c
@@ -0,0 +1,343 @@
+/* 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
+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:
+ */