diff options
Diffstat (limited to '')
-rw-r--r-- | plugins/imuxsock/imuxsock.c | 1767 |
1 files changed, 1767 insertions, 0 deletions
diff --git a/plugins/imuxsock/imuxsock.c b/plugins/imuxsock/imuxsock.c new file mode 100644 index 0000000..1ae550e --- /dev/null +++ b/plugins/imuxsock/imuxsock.c @@ -0,0 +1,1767 @@ +/* imuxsock.c + * This is the implementation of the Unix sockets input module. + * + * NOTE: read comments in module-template.h to understand how this file + * works! + * + * File begun on 2007-12-20 by RGerhards (extracted from syslogd.c) + * + * Copyright 2007-2019 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. + */ +#ifdef __sun +#define _XPG4_2 +#endif +#include "config.h" +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <poll.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/socket.h> +#ifdef HAVE_LIBSYSTEMD +# include <systemd/sd-daemon.h> +#endif +#if defined(__FreeBSD__) + #include <sys/param.h> +#endif +#include "rsyslog.h" +#include "dirty.h" +#include "cfsysline.h" +#include "unicode-helper.h" +#include "module-template.h" +#include "srUtils.h" +#include "errmsg.h" +#include "net.h" +#include "glbl.h" +#include "msg.h" +#include "parser.h" +#include "prop.h" +#include "debug.h" +#include "ruleset.h" +#include "unlimited_select.h" +#include "statsobj.h" +#include "datetime.h" +#include "hashtable.h" +#include "ratelimit.h" + + +MODULE_TYPE_INPUT +MODULE_TYPE_NOKEEP +MODULE_CNFNAME("imuxsock") + +/* defines */ +#ifndef _PATH_LOG +#ifdef BSD +#define _PATH_LOG "/var/run/log" +#else +#define _PATH_LOG "/dev/log" +#endif +#endif +#ifndef SYSTEMD_JOURNAL +#define SYSTEMD_JOURNAL "/run/systemd/journal" +#endif +#ifndef SYSTEMD_PATH_LOG +#define SYSTEMD_PATH_LOG SYSTEMD_JOURNAL "/syslog" +#endif +#define UNSET -1 /* to indicate a value has not been configured */ + +/* forward definitions */ +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + +#if defined(_AIX) +#define ucred ucred_t +#endif +/* emulate struct ucred for platforms that do not have it */ +#ifndef HAVE_SCM_CREDENTIALS +struct ucred { int pid; uid_t uid; gid_t gid; }; +#endif + +/* handle some defines missing on more than one platform */ +#ifndef SUN_LEN +#define SUN_LEN(su) \ + (sizeof(*(su)) - sizeof((su)->sun_path) + strlen((su)->sun_path)) +#endif +/* Module static data */ +DEF_IMOD_STATIC_DATA +DEFobjCurrIf(glbl) +DEFobjCurrIf(prop) +DEFobjCurrIf(net) +DEFobjCurrIf(parser) +DEFobjCurrIf(datetime) +DEFobjCurrIf(statsobj) +DEFobjCurrIf(ruleset) + + +statsobj_t *modStats; +STATSCOUNTER_DEF(ctrSubmit, mutCtrSubmit) +STATSCOUNTER_DEF(ctrLostRatelimit, mutCtrLostRatelimit) +STATSCOUNTER_DEF(ctrNumRatelimiters, mutCtrNumRatelimiters) + + +/* 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); +} + + +/* 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 */ + unsigned int ratelimitInterval; + unsigned int ratelimitBurst; + ratelimit_t *dflt_ratelimiter;/*ratelimiter to apply if none else is to be used */ + 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 bAnnotate; /* annotate events with trusted properties */ + sbool bParseTrusted; /* parse trusted properties */ + sbool bWritePid; /* write original PID into tag */ + sbool bDiscardOwnMsgs; /* discard messages that originated from ourselves */ + sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */ + sbool bUnlink; /* unlink&re-create socket at start and end of processing */ + sbool bUseSpecialParser;/* use "canned" log socket parser instead of parser chain? */ + ruleset_t *pRuleset; +} lstn_t; +static lstn_t *listeners; + +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 "imuxsock", 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 nfd = 1; /* number of active unix sockets (socket 0 is always reserved for the system + socket, even if it is not enabled. */ +static int sd_fds = 0; /* number of systemd activated sockets */ + +#if (defined(__FreeBSD__) && (__FreeBSD_version >= 1200061)) + #define DFLT_bUseSpecialParser 0 +#else + #define DFLT_bUseSpecialParser 1 +#endif +#define DFLT_bCreatePath 0 +#define DFLT_ratelimitInterval 0 +#define DFLT_ratelimitBurst 200 +#define DFLT_ratelimitSeverity 1 /* do not rate-limit emergency messages */ +/* config vars for the legacy config system */ +static struct configSettings_s { + int bOmitLocalLogging; + uchar *pLogSockName; + uchar *pLogHostName; /* host name to use with this socket */ + int bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used!) */ + int bUseFlowCtlSysSock; + int bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */ + int bIgnoreTimestampSysSock; + int bUseSysTimeStamp; /* use timestamp from system (rather than from message) */ + int bUseSysTimeStampSysSock; /* same, for system log socket */ + int bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */ + int bWritePidSysSock; /* use credentials from recvmsg() and fixup PID in TAG */ + int bCreatePath; /* auto-create socket path? */ + unsigned int ratelimitInterval; /* interval in seconds, 0 = off */ + unsigned int ratelimitIntervalSysSock; + unsigned int ratelimitBurst; /* max nbr of messages in interval */ + unsigned int ratelimitBurstSysSock; + int ratelimitSeverity; + int ratelimitSeveritySysSock; + int bAnnotate; /* annotate trusted properties */ + int bAnnotateSysSock; /* same, for system log socket */ + int bParseTrusted; /* parse trusted properties */ +} cs; + +/* config vars for the v2 config system (rsyslog v6+) */ +struct instanceConf_s { + uchar *sockName; + uchar *pLogHostName; /* host name to use with this socket */ + sbool bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */ + sbool bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */ + sbool bWritePid; /* use credentials from recvmsg() and fixup PID in TAG */ + sbool bUseSysTimeStamp; /* use timestamp from system (instead of from message) */ + int bCreatePath; /* auto-create socket path? */ + unsigned int ratelimitInterval; /* interval in seconds, 0 = off */ + unsigned int ratelimitBurst; /* max nbr of messages in interval */ + int ratelimitSeverity; + int bAnnotate; /* annotate trusted properties */ + int bParseTrusted; /* parse trusted properties */ + sbool bDiscardOwnMsgs; /* discard messages that originated from our own pid? */ + sbool bUnlink; + sbool bUseSpecialParser; + sbool bParseHost; + uchar *pszBindRuleset; /* name of ruleset to bind to */ + ruleset_t *pBindRuleset; /* ruleset to bind listener to (use system default if unspecified) */ + struct instanceConf_s *next; +}; + +struct modConfData_s { + rsconf_t *pConf; /* our overall config object */ + instanceConf_t *root, *tail; + uchar *pLogSockName; + unsigned int ratelimitIntervalSysSock; + unsigned int ratelimitBurstSysSock; + int ratelimitSeveritySysSock; + int bAnnotateSysSock; + int bParseTrusted; + int bUseSpecialParser; + int bParseHost; + sbool bIgnoreTimestamp; /* ignore timestamps present in the incoming message? */ + sbool bUseFlowCtl; /* use flow control or not (if yes, only LIGHT is used! */ + sbool bOmitLocalLogging; + sbool bWritePidSysSock; + sbool bUseSysTimeStamp; + sbool bDiscardOwnMsgs; + sbool configSetViaV2Method; + sbool bUnlink; +}; +static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */ +static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current load process */ + +/* module-global parameters */ +static struct cnfparamdescr modpdescr[] = { + { "syssock.use", eCmdHdlrBinary, 0 }, + { "syssock.name", eCmdHdlrGetWord, 0 }, + { "syssock.unlink", eCmdHdlrBinary, 0 }, + { "syssock.ignoretimestamp", eCmdHdlrBinary, 0 }, + { "syssock.ignoreownmessages", eCmdHdlrBinary, 0 }, + { "syssock.flowcontrol", eCmdHdlrBinary, 0 }, + { "syssock.usesystimestamp", eCmdHdlrBinary, 0 }, + { "syssock.annotate", eCmdHdlrBinary, 0 }, + { "syssock.parsetrusted", eCmdHdlrBinary, 0 }, + { "syssock.usespecialparser", eCmdHdlrBinary, 0 }, + { "syssock.parsehostname", eCmdHdlrBinary, 0 }, + { "syssock.usepidfromsystem", eCmdHdlrBinary, 0 }, + { "syssock.ratelimit.interval", eCmdHdlrInt, 0 }, + { "syssock.ratelimit.burst", eCmdHdlrInt, 0 }, + { "syssock.ratelimit.severity", eCmdHdlrInt, 0 } +}; +static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, + sizeof(modpdescr)/sizeof(struct cnfparamdescr), + modpdescr + }; + +/* input instance parameters */ +static struct cnfparamdescr inppdescr[] = { + { "socket", eCmdHdlrString, CNFPARAM_REQUIRED }, /* legacy: addunixlistensocket */ + { "unlink", eCmdHdlrBinary, 0 }, + { "createpath", eCmdHdlrBinary, 0 }, + { "parsetrusted", eCmdHdlrBinary, 0 }, + { "ignoreownmessages", eCmdHdlrBinary, 0 }, + { "hostname", eCmdHdlrString, 0 }, + { "ignoretimestamp", eCmdHdlrBinary, 0 }, + { "flowcontrol", eCmdHdlrBinary, 0 }, + { "usesystimestamp", eCmdHdlrBinary, 0 }, + { "annotate", eCmdHdlrBinary, 0 }, + { "usespecialparser", eCmdHdlrBinary, 0 }, + { "parsehostname", eCmdHdlrBinary, 0 }, + { "usepidfromsystem", eCmdHdlrBinary, 0 }, + { "ruleset", eCmdHdlrString, 0 }, + { "ratelimit.interval", eCmdHdlrInt, 0 }, + { "ratelimit.burst", eCmdHdlrInt, 0 }, + { "ratelimit.severity", eCmdHdlrInt, 0 } +}; +static struct cnfparamblk inppblk = + { CNFPARAMBLK_VERSION, + sizeof(inppdescr)/sizeof(struct cnfparamdescr), + inppdescr + }; + +#include "im-helper.h" /* must be included AFTER the type definitions! */ + +static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */ + + +/* create input instance, set default parameters, and + * add it to the list of instances. + */ +static rsRetVal +createInstance(instanceConf_t **pinst) +{ + instanceConf_t *inst; + DEFiRet; + CHKmalloc(inst = malloc(sizeof(instanceConf_t))); + inst->sockName = NULL; + inst->pLogHostName = NULL; + inst->pszBindRuleset = NULL; + inst->pBindRuleset = NULL; + inst->ratelimitInterval = DFLT_ratelimitInterval; + inst->ratelimitBurst = DFLT_ratelimitBurst; + inst->ratelimitSeverity = DFLT_ratelimitSeverity; + inst->bUseFlowCtl = 0; + inst->bUseSpecialParser = DFLT_bUseSpecialParser; + inst->bParseHost = UNSET; + inst->bIgnoreTimestamp = 1; + inst->bCreatePath = DFLT_bCreatePath; + inst->bUseSysTimeStamp = 1; + inst->bWritePid = 0; + inst->bAnnotate = 0; + inst->bParseTrusted = 0; + inst->bDiscardOwnMsgs = loadModConf->pConf->globals.bProcessInternalMessages; + inst->bUnlink = 1; + inst->next = NULL; + + /* node created, let's add to config */ + if(loadModConf->tail == NULL) { + loadModConf->tail = loadModConf->root = inst; + } else { + loadModConf->tail->next = inst; + loadModConf->tail = inst; + } + + *pinst = inst; +finalize_it: + RETiRet; +} + + +/* This function is called when a new listen socket instance shall be added to + * the current config object via the legacy config system. It just shuffles + * all parameters to the listener in-memory instance. + * rgerhards, 2011-05-12 + */ +static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal) +{ + instanceConf_t *inst; + DEFiRet; + + if(pNewVal == NULL || pNewVal[0] == '\0') { + LogError(0, RS_RET_SOCKNAME_MISSING , "imuxsock: socket name must be specified, " + "but is not - listener not created\n"); + if(pNewVal != NULL) + free(pNewVal); + ABORT_FINALIZE(RS_RET_SOCKNAME_MISSING); + } + + CHKiRet(createInstance(&inst)); + inst->sockName = pNewVal; + inst->ratelimitInterval = cs.ratelimitInterval; + inst->pLogHostName = cs.pLogHostName; + inst->ratelimitBurst = cs.ratelimitBurst; + inst->ratelimitSeverity = cs.ratelimitSeverity; + inst->bUseFlowCtl = cs.bUseFlowCtl; + inst->bIgnoreTimestamp = cs.bIgnoreTimestamp; + inst->bCreatePath = cs.bCreatePath; + inst->bUseSysTimeStamp = cs.bUseSysTimeStamp; + inst->bWritePid = cs.bWritePid; + inst->bAnnotate = cs.bAnnotate; + inst->bParseTrusted = cs.bParseTrusted; + inst->bParseHost = UNSET; + inst->next = NULL; + + /* reset hostname for next socket */ + cs.pLogHostName = NULL; + +finalize_it: + RETiRet; +} + + +/* add an additional listen socket. + * added capability to specify hostname for socket -- rgerhards, 2008-08-01 + */ +static rsRetVal +addListner(instanceConf_t *inst) +{ + DEFiRet; + + if(inst->bParseHost == UNSET) { + if(*inst->sockName == ':') { + listeners[nfd].bParseHost = 1; + } else { + listeners[nfd].bParseHost = 0; + } + } else { + listeners[nfd].bParseHost = inst->bParseHost; + } + if(inst->pLogHostName == NULL) { + listeners[nfd].hostName = NULL; + } else { + CHKiRet(prop.Construct(&(listeners[nfd].hostName))); + CHKiRet(prop.SetString(listeners[nfd].hostName, inst->pLogHostName, ustrlen(inst->pLogHostName))); + CHKiRet(prop.ConstructFinalize(listeners[nfd].hostName)); + } + if(inst->ratelimitInterval > 0) { + if((listeners[nfd].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, + (void(*)(void*))ratelimitDestruct)) == NULL) { + /* in this case, we simply turn off rate-limiting */ + DBGPRINTF("imuxsock: turning off rate limiting because we could not " + "create hash table\n"); + inst->ratelimitInterval = 0; + } + } else { + listeners[nfd].ht = NULL; + } + listeners[nfd].ratelimitInterval = inst->ratelimitInterval; + listeners[nfd].ratelimitBurst = inst->ratelimitBurst; + listeners[nfd].ratelimitSev = inst->ratelimitSeverity; + listeners[nfd].flowCtl = inst->bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY; + listeners[nfd].flags = inst->bIgnoreTimestamp ? IGNDATE : NOFLAG; + listeners[nfd].bCreatePath = inst->bCreatePath; + listeners[nfd].sockName = ustrdup(inst->sockName); + listeners[nfd].bUseCreds = (inst->bDiscardOwnMsgs || inst->bWritePid || inst->ratelimitInterval + || inst->bAnnotate || inst->bUseSysTimeStamp) ? 1 : 0; + listeners[nfd].bAnnotate = inst->bAnnotate; + listeners[nfd].bParseTrusted = inst->bParseTrusted; + listeners[nfd].bDiscardOwnMsgs = inst->bDiscardOwnMsgs; + listeners[nfd].bUnlink = inst->bUnlink; + listeners[nfd].bWritePid = inst->bWritePid; + listeners[nfd].bUseSysTimeStamp = inst->bUseSysTimeStamp; + listeners[nfd].bUseSpecialParser = inst->bUseSpecialParser; + listeners[nfd].pRuleset = inst->pBindRuleset; + CHKiRet(ratelimitNew(&listeners[nfd].dflt_ratelimiter, "imuxsock", NULL)); + ratelimitSetLinuxLike(listeners[nfd].dflt_ratelimiter, + listeners[nfd].ratelimitInterval, + listeners[nfd].ratelimitBurst); + ratelimitSetSeverity(listeners[nfd].dflt_ratelimiter, + listeners[nfd].ratelimitSev); + nfd++; + +finalize_it: + RETiRet; +} + + +static rsRetVal discardLogSockets(void) +{ + int i; + + /* Check whether the system socket is in use */ + if(startIndexUxLocalSockets == 0) { + /* Clean up rate limiting data for the system socket */ + if(listeners[0].ht != NULL) { + hashtable_destroy(listeners[0].ht, 1); /* 1 => free all values automatically */ + } + ratelimitDestruct(listeners[0].dflt_ratelimiter); + } + + /* Clean up all other sockets */ + 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(listeners[i].ht != NULL) { + hashtable_destroy(listeners[i].ht, 1); /* 1 => free all values automatically */ + } + ratelimitDestruct(listeners[i].dflt_ratelimiter); + } + + return RS_RET_OK; +} + + +/* used to create a log socket if NOT passed in via systemd. + */ +/* note: the linux SUN_LEN macro uses a sizeof based on a NULL pointer. This + * triggers UBSan warning. As such, we turn that warning off for the fuction. + * As it is OS-provided, there is no way to solve it ourselves. The problem + * may also exist on other platforms, we have just noticed it on Linux. + */ +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wunknown-attributes" +#endif +static rsRetVal +#if defined(__clang__) +__attribute__((no_sanitize("undefined"))) +#endif +createLogSocket(lstn_t *pLstn) +{ + struct sockaddr_un sunx; + DEFiRet; + + if(pLstn->bUnlink) + unlink((char*)pLstn->sockName); + memset(&sunx, 0, sizeof(sunx)); + sunx.sun_family = AF_UNIX; + 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)); + sunx.sun_path[sizeof(sunx.sun_path)-1] = '\0'; + pLstn->fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if(pLstn->fd < 0 ) { + ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); + } + if(bind(pLstn->fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx)) < 0) { + ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); + } + if(chmod((char*)pLstn->sockName, 0666) < 0) { + ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); + } +finalize_it: + if(iRet != RS_RET_OK) { + LogError(errno, iRet, "cannot create '%s'", pLstn->sockName); + if(pLstn->fd != -1) { + close(pLstn->fd); + pLstn->fd = -1; + } + } + RETiRet; +} + + +static rsRetVal +openLogSocket(lstn_t *pLstn) +{ + DEFiRet; +# ifdef HAVE_SCM_CREDENTIALS + int one; +# endif /* HAVE_SCM_CREDENTIALS */ + + if(pLstn->sockName[0] == '\0') + return -1; + + pLstn->fd = -1; + +#ifdef HAVE_LIBSYSTEMD + if (sd_fds > 0) { + /* Check if the current socket is a systemd activated one. + * If so, just use it. + */ + int fd; + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + sd_fds; fd++) { + if( sd_is_socket_unix(fd, SOCK_DGRAM, -1, (const char*) pLstn->sockName, 0) == 1) { + /* ok, it matches -- just use as is */ + pLstn->fd = fd; + + LogMsg(0, NO_ERRCODE, LOG_INFO, + "imuxsock: Acquired UNIX socket '%s' (fd %d) from systemd.\n", + pLstn->sockName, pLstn->fd); + break; + } + /* + * otherwise it either didn't match *this* socket and + * we just continue to check the next one or there was + * an error and we will create a new socket below. + */ + } + } +#endif + + if (pLstn->fd == -1) { + CHKiRet(createLogSocket(pLstn)); + assert(pLstn->fd != -1); /* else createLogSocket() should have failed! */ + } + +# ifdef HAVE_SCM_CREDENTIALS + if(pLstn->bUseCreds) { + one = 1; + if(setsockopt(pLstn->fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t) sizeof(one)) != 0) { + LogError(errno, NO_ERRCODE, "set SO_PASSCRED failed on '%s'", pLstn->sockName); + pLstn->bUseCreds = 0; + } +// TODO: move to its own #if + if(setsockopt(pLstn->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) != 0) { + LogError(errno, NO_ERRCODE, "set SO_TIMESTAMP failed on '%s'", pLstn->sockName); + } + } +# else /* HAVE_SCM_CREDENTIALS */ + pLstn->bUseCreds = 0; + pLstn->bAnnotate = 0; +# endif /* HAVE_SCM_CREDENTIALS */ + +finalize_it: + if(iRet != RS_RET_OK) { + if(pLstn->fd != -1) { + close(pLstn->fd); + pLstn->fd = -1; + } + } + + RETiRet; +} + + +/* find ratelimiter to use for this message. Currently, we use the + * pid, but may change to cgroup later (probably via a config switch). + * Returns NULL if not found or rate-limiting not activated for this + * listener (the latter being a performance enhancement). + */ +static rsRetVal +findRatelimiter(lstn_t *pLstn, struct ucred *cred, ratelimit_t **prl) +{ + ratelimit_t *rl = NULL; + int r; + pid_t *keybuf; + char pinfobuf[512]; + DEFiRet; + + if(cred == NULL) + FINALIZE; +#if 0 // TODO: check deactivated? + if(pLstn->ratelimitInterval == 0) { + *prl = NULL; + FINALIZE; + } +#endif + if(pLstn->ht == NULL) { + *prl = NULL; + FINALIZE; + } + + rl = hashtable_search(pLstn->ht, &cred->pid); + if(rl == NULL) { + /* we need to add a new ratelimiter, process not seen before! */ + DBGPRINTF("imuxsock: no ratelimiter for pid %lu, creating one\n", + (unsigned long) cred->pid); + STATSCOUNTER_INC(ctrNumRatelimiters, mutCtrNumRatelimiters); + /* read process name from system */ + char procName[256]; /* enough for any sane process name */ + snprintf(procName, sizeof(procName), "/proc/%lu/cmdline", (unsigned long) cred->pid); + FILE *f = fopen(procName, "r"); + if (f) { + size_t len; + len = fread(procName, sizeof(char), 256, f); + if (len > 0) { + snprintf(pinfobuf, sizeof(pinfobuf), "pid: %lu, name: %s", + (unsigned long) cred->pid, procName); + } + fclose(f); + } + else { + snprintf(pinfobuf, sizeof(pinfobuf), "pid: %lu", + (unsigned long) cred->pid); + } + pinfobuf[sizeof(pinfobuf)-1] = '\0'; /* to be on safe side */ + CHKiRet(ratelimitNew(&rl, "imuxsock", pinfobuf)); + ratelimitSetLinuxLike(rl, pLstn->ratelimitInterval, pLstn->ratelimitBurst); + ratelimitSetSeverity(rl, pLstn->ratelimitSev); + CHKmalloc(keybuf = malloc(sizeof(pid_t))); + *keybuf = cred->pid; + r = hashtable_insert(pLstn->ht, keybuf, rl); + if(r == 0) + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + + *prl = rl; + rl = NULL; + +finalize_it: + if(rl != NULL) + ratelimitDestruct(rl); + if(*prl == NULL) + *prl = pLstn->dflt_ratelimiter; + RETiRet; +} + + +/* patch correct pid into tag. bufTAG MUST be CONF_TAG_MAXSIZE long! + */ +static 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; +} + + +/* Get an "trusted property" from the system. Returns an empty string if the + * property can not be obtained. Inspired by similiar functionality inside + * journald. Currently works with Linux /proc filesystem, only. + */ +static rsRetVal +getTrustedProp(struct ucred *cred, const char *propName, uchar *buf, size_t lenBuf, int *lenProp) +{ + int fd; + int i; + int lenRead; + char namebuf[1024]; + DEFiRet; + + if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/%s", (long unsigned) cred->pid, + propName) >= (int) sizeof(namebuf)) { + ABORT_FINALIZE(RS_RET_ERR); + } + + if((fd = open(namebuf, O_RDONLY)) == -1) { + DBGPRINTF("error reading '%s'\n", namebuf); + ABORT_FINALIZE(RS_RET_ERR); + } + if((lenRead = read(fd, buf, lenBuf - 1)) == -1) { + DBGPRINTF("error reading file data for '%s'\n", namebuf); + close(fd); + ABORT_FINALIZE(RS_RET_ERR); + } + + /* we strip after the first \n */ + for(i = 0 ; i < lenRead ; ++i) { + if(buf[i] == '\n') + break; + else if(iscntrl(buf[i])) + buf[i] = ' '; + } + buf[i] = '\0'; + *lenProp = i; + + close(fd); + +finalize_it: + RETiRet; +} + + +/* read the exe trusted property path (so far, /proc fs only) + */ +static rsRetVal +getTrustedExe(struct ucred *cred, uchar *buf, size_t lenBuf, int* lenProp) +{ + int lenRead; + char namebuf[1024]; + DEFiRet; + + if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/exe", (long unsigned) cred->pid) + >= (int) sizeof(namebuf)) { + ABORT_FINALIZE(RS_RET_ERR); + } + + if((lenRead = readlink(namebuf, (char*)buf, lenBuf - 1)) == -1) { + DBGPRINTF("error reading link '%s'\n", namebuf); + ABORT_FINALIZE(RS_RET_ERR); + } + + buf[lenRead] = '\0'; + *lenProp = lenRead; + +finalize_it: + RETiRet; +} + + +/* copy a trusted property in escaped mode. That is, the property can contain + * any character and so it must be properly quoted AND escaped. + * It is assumed the output buffer is large enough. Returns the number of + * characters added. + */ +static int +copyescaped(uchar *dstbuf, uchar *inbuf, int inlen) +{ + int iDst, iSrc; + + *dstbuf = '"'; + for(iDst=1, iSrc=0 ; iSrc < inlen ; ++iDst, ++iSrc) { + if(inbuf[iSrc] == '"' || inbuf[iSrc] == '\\') { + dstbuf[iDst++] = '\\'; + } + dstbuf[iDst] = inbuf[iSrc]; + } + dstbuf[iDst++] = '"'; + return iDst; +} + + +/* submit received message to the queue engine + * We now parse the message according to expected format so that we + * can also mangle it if necessary. + */ +static rsRetVal +SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct timeval *ts) +{ + smsg_t *pMsg = NULL; + int lenMsg; + int offs; + int i; + uchar *parse; + syslog_pri_t pri; + uchar bufParseTAG[CONF_TAG_MAXSIZE]; + struct syslogTime st; + time_t tt; + ratelimit_t *ratelimiter = NULL; + struct syslogTime dummyTS; + DEFiRet; + + if(pLstn->bDiscardOwnMsgs && cred != NULL && cred->pid == glblGetOurPid()) { + DBGPRINTF("imuxsock: discarding message from our own pid\n"); + FINALIZE; + } + + /* TODO: handle format errors?? */ + /* we need to parse the pri first, because we need the severity for + * rate-limiting as well. + */ + parse = pRcv; + lenMsg = lenRcv; + offs = 1; /* '<' */ + + parse++; + pri = 0; + while(offs < lenMsg && isdigit(*parse)) { + pri = pri * 10 + *parse - '0'; + ++parse; + ++offs; + } + + findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */ + + if(ts == NULL) { + datetime.getCurrTime(&st, &tt, TIME_IN_LOCALTIME); + } else { + datetime.timeval2syslogTime(ts, &st, TIME_IN_LOCALTIME); + tt = ts->tv_sec; + } + +#if 0 // TODO: think about stats counters (or wait for request...?) + if(ratelimiter != NULL && !withinRatelimit(ratelimiter, tt, cred->pid)) { + STATSCOUNTER_INC(ctrLostRatelimit, mutCtrLostRatelimit); + FINALIZE; + } +#endif + + /* we now create our own message object and submit it to the queue */ + CHKiRet(msgConstructWithTime(&pMsg, &st, tt)); + + /* created trusted properties */ + if(cred != NULL && pLstn->bAnnotate) { + uchar propBuf[1024]; + int lenProp; + + if (pLstn->bParseTrusted) { + struct json_object *json, *jval; + +#define CHKjson(operation, toBeFreed) \ + if((operation) == NULL) { \ + json_object_put(toBeFreed); \ + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); \ + } + + CHKmalloc(json = json_object_new_object()); + /* create value string, create field, and add it */ + CHKjson(jval = json_object_new_int(cred->pid), json); + json_object_object_add(json, "pid", jval); + CHKjson(jval = json_object_new_int(cred->uid), json); + json_object_object_add(json, "uid", jval); + CHKjson(jval = json_object_new_int(cred->gid), json); + json_object_object_add(json, "gid", jval); + if(getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + CHKjson(jval = json_object_new_string((char*)propBuf), json); + json_object_object_add(json, "appname", jval); + } + if(getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + CHKjson(jval = json_object_new_string((char*)propBuf), json); + json_object_object_add(json, "exe", jval); + } + if(getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + CHKjson(jval = json_object_new_string((char*)propBuf), json); + json_object_object_add(json, "cmd", jval); + } +#undef CHKjson + + /* as per lumberjack spec, these properties need to go into + * the CEE root. + */ + msgAddJSON(pMsg, (uchar*)"!", json, 0, 0); + + MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv); + } else { + uchar msgbuf[8192]; + uchar *pmsgbuf = msgbuf; + int toffs; /* offset for trusted properties */ + + if((unsigned) (lenRcv + 4096) >= sizeof(msgbuf)) { + CHKmalloc(pmsgbuf = malloc(lenRcv+4096)); + } + + memcpy(pmsgbuf, pRcv, lenRcv); + memcpy(pmsgbuf+lenRcv, " @[", 3); + toffs = lenRcv + 3; /* next free location */ + lenProp = snprintf((char*)propBuf, sizeof(propBuf), "_PID=%lu _UID=%lu _GID=%lu", + (long unsigned) cred->pid, (long unsigned) cred->uid, + (long unsigned) cred->gid); + memcpy(pmsgbuf+toffs, propBuf, lenProp); + toffs = toffs + lenProp; + + if(getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + memcpy(pmsgbuf+toffs, " _COMM=", 7); + memcpy(pmsgbuf+toffs+7, propBuf, lenProp); + toffs = toffs + 7 + lenProp; + } + if(getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + memcpy(pmsgbuf+toffs, " _EXE=", 6); + memcpy(pmsgbuf+toffs+6, propBuf, lenProp); + toffs = toffs + 6 + lenProp; + } + if(getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) { + memcpy(pmsgbuf+toffs, " _CMDLINE=", 10); + toffs = toffs + 10 + + copyescaped(pmsgbuf+toffs+10, propBuf, lenProp); + } + + /* finalize string */ + pmsgbuf[toffs] = ']'; + pmsgbuf[toffs+1] = '\0'; + + MsgSetRawMsg(pMsg, (char*)pmsgbuf, toffs + 1); + if (pmsgbuf != msgbuf) { + free(pmsgbuf); + } + } + } else { + /* just add the unmodified message */ + MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv); + } + + MsgSetFlowControlType(pMsg, pLstn->flowCtl); + MsgSetInputName(pMsg, pInputName); + if(pLstn->bParseHost) { + pMsg->msgFlags = pLstn->flags | PARSE_HOSTNAME; + } else { + pMsg->msgFlags = pLstn->flags; + } + + if(pLstn->bUseSpecialParser) { + /* this is the legacy "log socket" parser which was written on the assumption + * that the log socket format would be fixed. While many folks said so, it + * seems to be different in practice, and this is why we now have choices... + * rgerhards, 2015-03-03 + */ + parser.SanitizeMsg(pMsg); + lenMsg = pMsg->iLenRawMsg - offs; /* SanitizeMsg() may have changed the size */ + msgSetPRI(pMsg, pri); + MsgSetAfterPRIOffs(pMsg, offs); + + parse++; lenMsg--; /* '>' */ + if(ts == NULL) { + if((pLstn->flags & IGNDATE)) { + /* in this case, we still need to find out if we have a valid + * datestamp or not .. and advance the parse pointer accordingly. + */ + if (datetime.ParseTIMESTAMP3339(&dummyTS, &parse, &lenMsg) != RS_RET_OK) { + datetime.ParseTIMESTAMP3164(&dummyTS, &parse, &lenMsg, + NO_PARSE3164_TZSTRING, NO_PERMIT_YEAR_AFTER_TIME); + } + } else { + if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK && + datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg, + NO_PARSE3164_TZSTRING, NO_PERMIT_YEAR_AFTER_TIME) != RS_RET_OK) { + DBGPRINTF("we have a problem, invalid timestamp in msg!\n"); + } + } + } else { /* if we pulled the time from the system, we need to update the message text */ + uchar *tmpParse = parse; /* just to check correctness of TS */ + if(datetime.ParseTIMESTAMP3339(&dummyTS, &tmpParse, &lenMsg) == RS_RET_OK || + datetime.ParseTIMESTAMP3164(&dummyTS, &tmpParse, &lenMsg, NO_PARSE3164_TZSTRING, + NO_PERMIT_YEAR_AFTER_TIME) == RS_RET_OK) { + /* We modify the message only if it contained a valid timestamp, + otherwise we do not touch it at all. */ + datetime.formatTimestamp3164(&st, (char*)parse, 0); + parse[15] = ' '; /* re-write \0 from fromatTimestamp3164 by SP */ + /* update "counters" to reflect processed timestamp */ + parse += 16; + } + } + + /* pull tag */ + + i = 0; + while(lenMsg > 0 && *parse != ' ' && i < CONF_TAG_MAXSIZE - 1) { + bufParseTAG[i++] = *parse++; + --lenMsg; + } + bufParseTAG[i] = '\0'; /* terminate string */ + if(pLstn->bWritePid) + fixPID(bufParseTAG, &i, cred); + MsgSetTAG(pMsg, bufParseTAG, i); + MsgSetMSGoffs(pMsg, pMsg->iLenRawMsg - lenMsg); + } else { /* we are configured to use regular parser chain */ + pMsg->msgFlags |= NEEDS_PARSING; + } + + MsgSetRcvFrom(pMsg, pLstn->hostName == NULL ? glbl.GetLocalHostNameProp() : pLstn->hostName); + CHKiRet(MsgSetRcvFromIP(pMsg, pLocalHostIP)); + MsgSetRuleset(pMsg, pLstn->pRuleset); + ratelimitAddMsg(ratelimiter, NULL, pMsg); + STATSCOUNTER_INC(ctrSubmit, mutCtrSubmit); +finalize_it: + if(iRet != RS_RET_OK) { + if(pMsg != NULL) + msgDestruct(&pMsg); + } + RETiRet; +} + + +/* This function receives data from a socket indicated to be ready + * to receive and submits the message received for processing. + * rgerhards, 2007-12-20 + * Interface changed so that this function is passed the array index + * of the socket which is to be processed. This eases access to the + * growing number of properties. -- rgerhards, 2008-08-01 + */ +static rsRetVal readSocket(lstn_t *pLstn) +{ + DEFiRet; + int iRcvd; + int iMaxLine; + struct msghdr msgh; + struct iovec msgiov; + struct ucred cred; + struct timeval ts; + int cred_set = 0; + int ts_set = 0; + uchar bufRcv[4096+1]; + uchar *pRcv = NULL; /* receive buffer */ +# ifdef HAVE_SCM_CREDENTIALS + /* aux is a union rather than a direct char array to force alignment with cmsghdr */ + union { + char buf[128]; + struct cmsghdr cm; + } aux; +# endif + + assert(pLstn->fd >= 0); + + iMaxLine = glbl.GetMaxLine(runConf); + + /* we optimize performance: if iMaxLine is below 4K (which it is in almost all + * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory + * is used. We could use alloca() to achive a similar aspect, but there are so + * many issues with alloca() that I do not want to take that route. + * rgerhards, 2008-09-02 + */ + if((size_t) iMaxLine < sizeof(bufRcv) - 1) { + pRcv = bufRcv; + } else { + CHKmalloc(pRcv = (uchar*) malloc(iMaxLine + 1)); + } + + memset(&msgh, 0, sizeof(msgh)); + memset(&msgiov, 0, sizeof(msgiov)); +# ifdef HAVE_SCM_CREDENTIALS + if(pLstn->bUseCreds) { + memset(&aux, 0, sizeof(aux)); + msgh.msg_control = &aux; + msgh.msg_controllen = sizeof(aux); + } +# endif + msgiov.iov_base = (char*)pRcv; + msgiov.iov_len = iMaxLine; + msgh.msg_iov = &msgiov; + msgh.msg_iovlen = 1; +/* AIXPORT : MSG_DONTWAIT not supported */ +#if defined (_AIX) +#define MSG_DONTWAIT MSG_NONBLOCK +#endif + iRcvd = recvmsg(pLstn->fd, &msgh, MSG_DONTWAIT); + + DBGPRINTF("Message from UNIX socket: #%d, size %d\n", pLstn->fd, (int) iRcvd); + if(iRcvd > 0) { +# if defined(HAVE_SCM_CREDENTIALS) || defined(HAVE_SO_TIMESTAMP) + if(pLstn->bUseCreds) { + struct cmsghdr *cm; + for(cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) { +# ifdef HAVE_SCM_CREDENTIALS + if( pLstn->bUseCreds + && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) { + memcpy(&cred, CMSG_DATA(cm), sizeof(cred)); + cred_set = 1; + } +# endif /* HAVE_SCM_CREDENTIALS */ +# if HAVE_SO_TIMESTAMP + if( pLstn->bUseSysTimeStamp + && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) { + memcpy(&ts, CMSG_DATA(cm), sizeof(ts)); + ts_set = 1; + } +# endif /* HAVE_SO_TIMESTAMP */ + } + } +# endif /* defined(HAVE_SCM_CREDENTIALS) || defined(HAVE_SO_TIMESTAMP) */ + CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, (cred_set ? &cred : NULL), (ts_set ? &ts : NULL))); + } else if(iRcvd < 0 && errno != EINTR && errno != EAGAIN) { + char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + DBGPRINTF("UNIX socket error: %d = %s.\n", errno, errStr); + LogError(errno, NO_ERRCODE, "imuxsock: recvfrom UNIX"); + } + +finalize_it: + if(pRcv != NULL && (size_t) iMaxLine >= sizeof(bufRcv) - 1) + free(pRcv); + + RETiRet; +} + + +/* activate current listeners */ +static rsRetVal +activateListeners(void) +{ + int actSocks; + int i; + DEFiRet; + + /* Initialize the system socket only if it's in use */ + if(startIndexUxLocalSockets == 0) { + /* first apply some config settings */ + listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG); + if(runModConf->pLogSockName != NULL) { + listeners[0].sockName = runModConf->pLogSockName; + } +#ifdef HAVE_LIBSYSTEMD + else if(sd_booted()) { + struct stat st; + if(stat(SYSTEMD_PATH_LOG, &st) != -1 && S_ISSOCK(st.st_mode)) { + listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG; + } + } +#endif + if(runModConf->ratelimitIntervalSysSock > 0) { + if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) { + /* in this case, we simply turn of rate-limiting */ + LogError(0, NO_ERRCODE, "imuxsock: turning off rate limiting because " + "we could not create hash table\n"); + runModConf->ratelimitIntervalSysSock = 0; + } + } else { + listeners[0].ht = NULL; + } + listeners[0].fd = -1; + listeners[0].pRuleset = NULL; + listeners[0].hostName = NULL; + listeners[0].bParseHost = 0; + listeners[0].bCreatePath = 0; + listeners[0].ratelimitInterval = runModConf->ratelimitIntervalSysSock; + listeners[0].ratelimitBurst = runModConf->ratelimitBurstSysSock; + listeners[0].ratelimitSev = runModConf->ratelimitSeveritySysSock; + listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock + || runModConf->bAnnotateSysSock || runModConf->bDiscardOwnMsgs + || runModConf->bUseSysTimeStamp) ? 1 : 0; + listeners[0].bWritePid = runModConf->bWritePidSysSock; + listeners[0].bAnnotate = runModConf->bAnnotateSysSock; + listeners[0].bParseTrusted = runModConf->bParseTrusted; + listeners[0].bParseHost = runModConf->bParseHost; + listeners[0].bUseSpecialParser = runModConf->bUseSpecialParser; + listeners[0].bDiscardOwnMsgs = runModConf->bDiscardOwnMsgs; + listeners[0].bUnlink = runModConf->bUnlink; + listeners[0].bUseSysTimeStamp = runModConf->bUseSysTimeStamp; + listeners[0].flags = runModConf->bIgnoreTimestamp ? IGNDATE : NOFLAG; + listeners[0].flowCtl = runModConf->bUseFlowCtl ? eFLOWCTL_LIGHT_DELAY : eFLOWCTL_NO_DELAY; + CHKiRet(ratelimitNew(&listeners[0].dflt_ratelimiter, "imuxsock", NULL)); + ratelimitSetLinuxLike(listeners[0].dflt_ratelimiter, + listeners[0].ratelimitInterval, + listeners[0].ratelimitBurst); + ratelimitSetSeverity(listeners[0].dflt_ratelimiter,listeners[0].ratelimitSev); + } + +#ifdef HAVE_LIBSYSTEMD + sd_fds = sd_listen_fds(0); + if(sd_fds < 0) { + LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket"); + ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX); + } +#endif + + /* initialize and return if will run or not */ + actSocks = 0; + for (i = startIndexUxLocalSockets ; i < nfd ; i++) { + if(openLogSocket(&(listeners[i])) == RS_RET_OK) { + ++actSocks; + DBGPRINTF("imuxsock: Opened UNIX socket '%s' (fd %d).\n", + listeners[i].sockName, listeners[i].fd); + } + } + + if(actSocks == 0) { + LogError(0, RS_RET_ERR, "imuxsock does not run because we could not " + "acquire any socket\n"); + ABORT_FINALIZE(RS_RET_ERR); + } + +finalize_it: + RETiRet; +} + + + +BEGINbeginCnfLoad +CODESTARTbeginCnfLoad + loadModConf = pModConf; + pModConf->pConf = pConf; + /* init our settings */ + pModConf->pLogSockName = NULL; + pModConf->bOmitLocalLogging = 0; + pModConf->bIgnoreTimestamp = 1; + pModConf->bUseFlowCtl = 0; + pModConf->bUseSysTimeStamp = 1; + pModConf->bWritePidSysSock = 0; + pModConf->bAnnotateSysSock = 0; + pModConf->bParseTrusted = 0; + pModConf->bParseHost = UNSET; + pModConf->bUseSpecialParser = DFLT_bUseSpecialParser; + /* if we do not process internal messages, we will see messages + * from ourselves, and so we need to permit this. + */ + pModConf->bDiscardOwnMsgs = pConf->globals.bProcessInternalMessages; + pModConf->bUnlink = 1; + pModConf->ratelimitIntervalSysSock = DFLT_ratelimitInterval; + pModConf->ratelimitBurstSysSock = DFLT_ratelimitBurst; + pModConf->ratelimitSeveritySysSock = DFLT_ratelimitSeverity; + bLegacyCnfModGlobalsPermitted = 1; + /* reset legacy config vars */ + resetConfigVariables(NULL, NULL); +ENDbeginCnfLoad + + +BEGINsetModCnf + struct cnfparamvals *pvals = NULL; + int i; +CODESTARTsetModCnf + pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { + LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing module " + "config parameters [module(...)]"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("module (global) param blk for imuxsock:\n"); + cnfparamsPrint(&modpblk, pvals); + } + + for(i = 0 ; i < modpblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(modpblk.descr[i].name, "syssock.use")) { + loadModConf->bOmitLocalLogging = ((int) pvals[i].val.d.n) ? 0 : 1; + } else if(!strcmp(modpblk.descr[i].name, "syssock.name")) { + loadModConf->pLogSockName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(modpblk.descr[i].name, "syssock.ignoretimestamp")) { + loadModConf->bIgnoreTimestamp = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.ignoreownmessages")) { + loadModConf->bDiscardOwnMsgs = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.unlink")) { + loadModConf->bUnlink = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.flowcontrol")) { + loadModConf->bUseFlowCtl = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.usesystimestamp")) { + loadModConf->bUseSysTimeStamp = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.annotate")) { + loadModConf->bAnnotateSysSock = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.parsetrusted")) { + loadModConf->bParseTrusted = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.parsehostname")) { + loadModConf->bParseHost = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.usespecialparser")) { + loadModConf->bUseSpecialParser = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.usepidfromsystem")) { + loadModConf->bWritePidSysSock = (int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.interval")) { + loadModConf->ratelimitIntervalSysSock = (unsigned int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.burst")) { + loadModConf->ratelimitBurstSysSock = (unsigned int) pvals[i].val.d.n; + } else if(!strcmp(modpblk.descr[i].name, "syssock.ratelimit.severity")) { + loadModConf->ratelimitSeveritySysSock = (int) pvals[i].val.d.n; + } else { + dbgprintf("imuxsock: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); + } + } + + /* disable legacy module-global config directives */ + bLegacyCnfModGlobalsPermitted = 0; + loadModConf->configSetViaV2Method = 1; + +finalize_it: + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &modpblk); +ENDsetModCnf + + +BEGINnewInpInst + struct cnfparamvals *pvals; + instanceConf_t *inst; + int i; +CODESTARTnewInpInst + DBGPRINTF("newInpInst (imuxsock)\n"); + + pvals = nvlstGetParams(lst, &inppblk, NULL); + if(pvals == NULL) { + LogError(0, RS_RET_MISSING_CNFPARAMS, + "imuxsock: required parameter are missing\n"); + ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS); + } + + if(Debug) { + dbgprintf("input param blk in imuxsock:\n"); + cnfparamsPrint(&inppblk, pvals); + } + + CHKiRet(createInstance(&inst)); + + for(i = 0 ; i < inppblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(inppblk.descr[i].name, "socket")) { + inst->sockName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "createpath")) { + inst->bCreatePath = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "parsetrusted")) { + inst->bParseTrusted = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "ignoreownmessages")) { + inst->bDiscardOwnMsgs = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "unlink")) { + inst->bUnlink = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "hostname")) { + inst->pLogHostName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "ignoretimestamp")) { + inst->bIgnoreTimestamp = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "flowcontrol")) { + inst->bUseFlowCtl = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "usesystimestamp")) { + inst->bUseSysTimeStamp = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "annotate")) { + inst->bAnnotate = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "usepidfromsystem")) { + inst->bWritePid = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "parsehostname")) { + inst->bParseHost = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "usespecialparser")) { + inst->bUseSpecialParser = (int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "ruleset")) { + inst->pszBindRuleset = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(inppblk.descr[i].name, "ratelimit.interval")) { + inst->ratelimitInterval = (unsigned int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "ratelimit.burst")) { + inst->ratelimitBurst = (unsigned int) pvals[i].val.d.n; + } else if(!strcmp(inppblk.descr[i].name, "ratelimit.severity")) { + inst->ratelimitSeverity = (int) pvals[i].val.d.n; + } else { + dbgprintf("imuxsock: program error, non-handled " + "param '%s'\n", inppblk.descr[i].name); + } + } +finalize_it: +CODE_STD_FINALIZERnewInpInst + cnfparamvalsDestruct(pvals, &inppblk); +ENDnewInpInst + + +BEGINendCnfLoad +CODESTARTendCnfLoad + if(!loadModConf->configSetViaV2Method) { + /* persist module-specific settings from legacy config system */ + /* these are used to initialize the system log socket (listeners[0]) */ + loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging; + loadModConf->pLogSockName = cs.pLogSockName; + loadModConf->bIgnoreTimestamp = cs.bIgnoreTimestampSysSock; + loadModConf->bUseSysTimeStamp = cs.bUseSysTimeStampSysSock; + loadModConf->bUseFlowCtl = cs.bUseFlowCtlSysSock; + loadModConf->bAnnotateSysSock = cs.bAnnotateSysSock; + loadModConf->bWritePidSysSock = cs.bWritePidSysSock; + loadModConf->bParseTrusted = cs.bParseTrusted; + loadModConf->ratelimitIntervalSysSock = cs.ratelimitIntervalSysSock; + loadModConf->ratelimitBurstSysSock = cs.ratelimitBurstSysSock; + loadModConf->ratelimitSeveritySysSock = cs.ratelimitSeveritySysSock; + } + + loadModConf = NULL; /* done loading */ + /* free legacy config vars */ + free(cs.pLogHostName); + cs.pLogSockName = NULL; + cs.pLogHostName = NULL; +ENDendCnfLoad + + +/* function to generate error message if framework does not find requested ruleset */ +static void +std_checkRuleset_genErrMsg(__attribute__((unused)) modConfData_t *modConf, instanceConf_t *inst) +{ + LogError(0, NO_ERRCODE, "imuxsock: ruleset '%s' for socket %s not found - " + "using default ruleset instead", inst->pszBindRuleset, + inst->sockName); +} +BEGINcheckCnf + instanceConf_t *inst; +CODESTARTcheckCnf + for(inst = pModConf->root ; inst != NULL ; inst = inst->next) { + std_checkRuleset(pModConf, inst); + } +ENDcheckCnf + + +BEGINactivateCnfPrePrivDrop + instanceConf_t *inst; + int nLstn; + int i; +CODESTARTactivateCnfPrePrivDrop + runModConf = pModConf; +# ifdef OS_SOLARIS + /* under solaris, we must NEVER process the local log socket, because + * it is implemented there differently. If we used it, we would actually + * delete it and render the system partly unusable. So don't do that. + * rgerhards, 2010-03-26 + */ + startIndexUxLocalSockets = 1; +# else + startIndexUxLocalSockets = runModConf->bOmitLocalLogging ? 1 : 0; +# endif + /* we first calculate the number of listeners so that we can + * appropriately size the listener array. Note that we will + * always allocate memory for the system log socket. + */ + nLstn = 0; + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + ++nLstn; + } + if(nLstn > 0 || startIndexUxLocalSockets == 0) { + DBGPRINTF("imuxsock: allocating memory for %d listeners\n", nLstn); + lstn_t *const listeners_new = realloc(listeners, (1+nLstn)*sizeof(lstn_t)); + CHKmalloc(listeners_new); + listeners = listeners_new; + for(i = 1 ; i < nLstn ; ++i) { + listeners[i].sockName = NULL; + listeners[i].fd = -1; + } + for(inst = runModConf->root ; inst != NULL ; inst = inst->next) { + addListner(inst); + } + CHKiRet(activateListeners()); + } +finalize_it: +ENDactivateCnfPrePrivDrop + + +BEGINactivateCnf +CODESTARTactivateCnf +ENDactivateCnf + + +BEGINfreeCnf + instanceConf_t *inst, *del; +CODESTARTfreeCnf + free(pModConf->pLogSockName); + for(inst = pModConf->root ; inst != NULL ; ) { + free(inst->sockName); + free(inst->pszBindRuleset); + free(inst->pLogHostName); + del = inst; + inst = inst->next; + free(del); + } +ENDfreeCnf + + +/* This function is called to gather input. */ +BEGINrunInput + int nfds; + int i; +CODESTARTrunInput + struct pollfd *const pollfds = calloc(nfd, sizeof(struct pollfd)); + CHKmalloc(pollfds); + if(startIndexUxLocalSockets == 1 && nfd == 1) { + /* No sockets were configured, no reason to run. */ + ABORT_FINALIZE(RS_RET_OK); + } + if(startIndexUxLocalSockets == 1) { + pollfds[0].fd = -1; + } + for (i = startIndexUxLocalSockets; i < nfd; i++) { + pollfds[i].fd = listeners[i].fd; + pollfds[i].events = POLLIN; + } + + /* this is an endless loop - it is terminated when the thread is + * signalled to do so. + */ + while(1) { + DBGPRINTF("--------imuxsock calling poll() on %d fds\n", nfd); + + nfds = poll(pollfds, nfd, -1); + if(glbl.GetGlobalInputTermState() == 1) + break; /* terminate input! */ + + if(nfds < 0) { + if(errno == EINTR) { + DBGPRINTF("imuxsock: EINTR occurred\n"); + } else { + LogMsg(errno, RS_RET_POLL_ERR, LOG_WARNING, "imuxsock: poll " + "system call failed, may cause further troubles"); + } + nfds = 0; + } + + for (i = startIndexUxLocalSockets ; i < nfd && nfds > 0; i++) { + if(glbl.GetGlobalInputTermState() == 1) + ABORT_FINALIZE(RS_RET_FORCE_TERM); /* terminate input! */ + if(pollfds[i].revents & POLLIN) { + readSocket(&(listeners[i])); + --nfds; /* indicate we have processed one */ + } + } + } + +finalize_it: + free(pollfds); +ENDrunInput + + +BEGINwillRun +CODESTARTwillRun +ENDwillRun + + +BEGINafterRun + int i; +CODESTARTafterRun + /* do cleanup here */ + if(startIndexUxLocalSockets == 1 && nfd == 1) { + /* No sockets were configured, no cleanup needed. */ + return RS_RET_OK; + } + + /* Close the UNIX sockets. */ + for (i = 0; i < nfd; i++) + if (listeners[i].fd != -1) + close(listeners[i].fd); + + /* Clean-up files. */ + for(i = startIndexUxLocalSockets; i < nfd; i++) + if (listeners[i].sockName && listeners[i].fd != -1) { + /* If systemd passed us a socket it is systemd's job to clean it up. + * Do not unlink it -- we will get same socket (node) from systemd + * e.g. on restart again. + */ + if (sd_fds > 0 +# ifdef HAVE_LIBSYSTEMD + && listeners[i].fd >= SD_LISTEN_FDS_START && + listeners[i].fd < SD_LISTEN_FDS_START + sd_fds +# endif + ) + continue; + + if(listeners[i].bUnlink) { + DBGPRINTF("imuxsock: unlinking unix socket file[%d] %s\n", i, listeners[i].sockName); + unlink((char*) listeners[i].sockName); + } + } + + discardLogSockets(); + nfd = 1; +ENDafterRun + + +BEGINmodExit +CODESTARTmodExit + free(listeners); + if(pInputName != NULL) + prop.Destruct(&pInputName); + + statsobj.Destruct(&modStats); + + objRelease(parser, CORE_COMPONENT); + objRelease(glbl, CORE_COMPONENT); + objRelease(prop, CORE_COMPONENT); + objRelease(statsobj, CORE_COMPONENT); + objRelease(datetime, CORE_COMPONENT); + objRelease(ruleset, CORE_COMPONENT); +ENDmodExit + + +BEGINisCompatibleWithFeature +CODESTARTisCompatibleWithFeature + if(eFeat == sFEATURENonCancelInputTermination) + iRet = RS_RET_OK; +ENDisCompatibleWithFeature + + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_IMOD_QUERIES +CODEqueryEtryPt_STD_CONF2_QUERIES +CODEqueryEtryPt_STD_CONF2_setModCnf_QUERIES +CODEqueryEtryPt_STD_CONF2_PREPRIVDROP_QUERIES +CODEqueryEtryPt_STD_CONF2_IMOD_QUERIES +CODEqueryEtryPt_IsCompatibleWithFeature_IF_OMOD_QUERIES +ENDqueryEtryPt + +static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal) +{ + free(cs.pLogSockName); + cs.pLogSockName = NULL; + free(cs.pLogHostName); + cs.bOmitLocalLogging = 0; + cs.pLogHostName = NULL; + cs.bIgnoreTimestamp = 1; + cs.bIgnoreTimestampSysSock = 1; + cs.bUseFlowCtl = 0; + cs.bUseFlowCtlSysSock = 0; + cs.bUseSysTimeStamp = 1; + cs.bUseSysTimeStampSysSock = 1; + cs.bWritePid = 0; + cs.bWritePidSysSock = 0; + cs.bAnnotate = 0; + cs.bAnnotateSysSock = 0; + cs.bParseTrusted = 0; + cs.bCreatePath = DFLT_bCreatePath; + cs.ratelimitInterval = DFLT_ratelimitInterval; + cs.ratelimitIntervalSysSock = DFLT_ratelimitInterval; + cs.ratelimitBurst = DFLT_ratelimitBurst; + cs.ratelimitBurstSysSock = DFLT_ratelimitBurst; + cs.ratelimitSeverity = DFLT_ratelimitSeverity; + cs.ratelimitSeveritySysSock = DFLT_ratelimitSeverity; + + return RS_RET_OK; +} + + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + CHKiRet(objUse(glbl, CORE_COMPONENT)); + CHKiRet(objUse(net, CORE_COMPONENT)); + CHKiRet(objUse(prop, CORE_COMPONENT)); + CHKiRet(objUse(statsobj, CORE_COMPONENT)); + CHKiRet(objUse(datetime, CORE_COMPONENT)); + CHKiRet(objUse(parser, CORE_COMPONENT)); + CHKiRet(objUse(ruleset, CORE_COMPONENT)); + + DBGPRINTF("imuxsock version %s initializing\n", PACKAGE_VERSION); + + /* init legacy config vars */ + cs.pLogSockName = NULL; + cs.pLogHostName = NULL; /* host name to use with this socket */ + + /* we need to create the inputName property (only once during our lifetime) */ + CHKiRet(prop.Construct(&pInputName)); + CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1)); + CHKiRet(prop.ConstructFinalize(pInputName)); + + /* right now, glbl does not permit per-instance IP address notation. As long as this + * is the case, it is OK to query the HostIP once here at this location. HOWEVER, the + * whole concept is not 100% clean and needs to be addressed on a higher layer. + * TODO / rgerhards, 2012-04-11 + */ + pLocalHostIP = glbl.GetLocalHostIP(); + + /* register config file handlers */ + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketignoremsgtimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bIgnoreTimestamp, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensockethostname", 0, eCmdHdlrGetWord, + NULL, &cs.pLogHostName, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketflowcontrol", 0, eCmdHdlrBinary, + NULL, &cs.bUseFlowCtl, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketannotate", 0, eCmdHdlrBinary, + NULL, &cs.bAnnotate, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketcreatepath", 0, eCmdHdlrBinary, + NULL, &cs.bCreatePath, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusesystimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bUseSysTimeStamp, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"addunixlistensocket", 0, eCmdHdlrGetWord, + addInstance, NULL, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"inputunixlistensocketusepidfromsystem", 0, eCmdHdlrBinary, + NULL, &cs.bWritePid, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitinterval", 0, eCmdHdlrInt, + NULL, &cs.ratelimitInterval, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitburst", 0, eCmdHdlrInt, + NULL, &cs.ratelimitBurst, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imuxsockratelimitseverity", 0, eCmdHdlrInt, + NULL, &cs.ratelimitSeverity, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"resetconfigvariables", 1, eCmdHdlrCustomHandler, + resetConfigVariables, NULL, STD_LOADABLE_MODULE_ID)); + /* the following one is a (dirty) trick: the system log socket is not added via + * an "addUnixListenSocket" config format. As such, it's properties can not be modified + * via $InputUnixListenSocket*". So we need to add a special directive + * for that. We should revisit all of that once we have the new config format... + * rgerhards, 2008-03-06 + */ + CHKiRet(regCfSysLineHdlr2((uchar *)"omitlocallogging", 0, eCmdHdlrBinary, + NULL, &cs.bOmitLocalLogging, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketname", 0, eCmdHdlrGetWord, + NULL, &cs.pLogSockName, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketignoremsgtimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bIgnoreTimestampSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketflowcontrol", 0, eCmdHdlrBinary, + NULL, &cs.bUseFlowCtlSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogusesystimestamp", 0, eCmdHdlrBinary, + NULL, &cs.bUseSysTimeStampSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogsocketannotate", 0, eCmdHdlrBinary, + NULL, &cs.bAnnotateSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogparsetrusted", 0, eCmdHdlrBinary, + NULL, &cs.bParseTrusted, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogusepidfromsystem", 0, eCmdHdlrBinary, + NULL, &cs.bWritePidSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitinterval", 0, eCmdHdlrInt, + NULL, &cs.ratelimitIntervalSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitburst", 0, eCmdHdlrInt, + NULL, &cs.ratelimitBurstSysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + CHKiRet(regCfSysLineHdlr2((uchar *)"systemlogratelimitseverity", 0, eCmdHdlrInt, + NULL, &cs.ratelimitSeveritySysSock, STD_LOADABLE_MODULE_ID, &bLegacyCnfModGlobalsPermitted)); + + /* support statistics gathering */ + CHKiRet(statsobj.Construct(&modStats)); + CHKiRet(statsobj.SetName(modStats, UCHAR_CONSTANT("imuxsock"))); + CHKiRet(statsobj.SetOrigin(modStats, UCHAR_CONSTANT("imuxsock"))); + STATSCOUNTER_INIT(ctrSubmit, mutCtrSubmit); + CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("submitted"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &ctrSubmit)); + STATSCOUNTER_INIT(ctrLostRatelimit, mutCtrLostRatelimit); + CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.discarded"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &ctrLostRatelimit)); + STATSCOUNTER_INIT(ctrNumRatelimiters, mutCtrNumRatelimiters); + CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.numratelimiters"), + ctrType_IntCtr, CTR_FLAG_RESETTABLE, &ctrNumRatelimiters)); + CHKiRet(statsobj.ConstructFinalize(modStats)); + +ENDmodInit |