/* 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 <signal.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 "hashtable_itr.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 void removePidRatelimiter(lstn_t *pLstn, pid_t pid);
static void prunePidRatelimiters(lstn_t *pLstn);
static void enforcePidRatelimiterCap(lstn_t *pLstn);
static void releasePidRatelimiterCache(lstn_t *pLstn);

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 */
/*
 * Bound per-PID ratelimiter state so a stream of short-lived local senders
 * cannot grow the cache without limit for the lifetime of the daemon.
 */
#define MAX_DYNAMIC_RATELIMITERS 4096U
/* 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? */
    int ratelimitInterval; /* interval in seconds, 0 = off */
    int ratelimitBurst; /* max nbr of messages in interval */
    int ratelimitSeverity;
    uchar *pszRatelimitName;
    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;
    int ratelimitIntervalSysSock;
    int ratelimitBurstSysSock;
    int ratelimitSeveritySysSock;
    uchar *pszRatelimitNameSysSock;
    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},
                                           {"syssock.ratelimit.name", eCmdHdlrString, 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},
    {"ratelimit.name", eCmdHdlrString, 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 = -1;
    inst->ratelimitBurst = -1;
    inst->ratelimitSeverity = DFLT_ratelimitSeverity;
    inst->pszRatelimitName = NULL;
    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->pszRatelimitName != NULL || 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;
            free(inst->pszRatelimitName);
            inst->pszRatelimitName = NULL;
        }
    } 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->pszRatelimitName || 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;
    if (inst->pszRatelimitName != NULL) {
        CHKiRet(ratelimitNewFromConfig(&listeners[nfd].dflt_ratelimiter, runModConf->pConf,
                                       (char *)inst->pszRatelimitName, "imuxsock", NULL));
    } else {
        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) {
            releasePidRatelimiterCache(&listeners[0]);
        }
        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) {
            releasePidRatelimiterCache(&listeners[i]);
        }
        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];
    char procName[256];
    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) {
        prunePidRatelimiters(pLstn);
        enforcePidRatelimiterCap(pLstn);
        /* 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  */
        procName[0] = '\0';
        snprintf(pinfobuf, sizeof(pinfobuf), "pid: %lu", (unsigned long)cred->pid);
        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), sizeof(procName) - 1, f);
            if (len > 0) {
                procName[len] = '\0';
                snprintf(pinfobuf, sizeof(pinfobuf), "pid: %lu, name: %s", (unsigned long)cred->pid, procName);
            }
            fclose(f);
        }
        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;
}

static void removePidRatelimiter(lstn_t *pLstn, pid_t pid) {
    ratelimit_t *rl;

    assert(pLstn != NULL);
    if (pLstn == NULL || pLstn->ht == NULL) {
        return;
    }

    rl = hashtable_remove(pLstn->ht, &pid);
    if (rl != NULL) {
        ratelimitDestruct(rl);
        STATSCOUNTER_DEC(ctrNumRatelimiters, mutCtrNumRatelimiters);
    }
}

static void prunePidRatelimiters(lstn_t *pLstn) {
    struct hashtable_itr *itr;

    assert(pLstn != NULL);
    if (pLstn == NULL || pLstn->ht == NULL || hashtable_count(pLstn->ht) == 0) {
        return;
    }

    itr = hashtable_iterator(pLstn->ht);
    if (itr == NULL) {
        return;
    }

    while (itr->e != NULL) {
        const pid_t pid = *((pid_t *)hashtable_iterator_key(itr));
        ratelimit_t *rl = hashtable_iterator_value(itr);
        const int is_alive = (kill(pid, 0) == 0 || errno == EPERM);

        if (!is_alive) {
            hashtable_iterator_remove(itr);
            ratelimitDestruct(rl);
            STATSCOUNTER_DEC(ctrNumRatelimiters, mutCtrNumRatelimiters);
        } else if (!hashtable_iterator_advance(itr)) {
            break;
        }
    }

    free(itr);
}

static void enforcePidRatelimiterCap(lstn_t *pLstn) {
    struct hashtable_itr *itr;
    pid_t pid;

    assert(pLstn != NULL);
    if (pLstn == NULL || pLstn->ht == NULL || hashtable_count(pLstn->ht) < MAX_DYNAMIC_RATELIMITERS) {
        return;
    }

    itr = hashtable_iterator(pLstn->ht);
    if (itr == NULL || itr->e == NULL) {
        free(itr);
        return;
    }

    pid = *((pid_t *)hashtable_iterator_key(itr));
    free(itr);

    DBGPRINTF("imuxsock: evicting pid %lu ratelimiter to keep cache bounded\n", (unsigned long)pid);
    removePidRatelimiter(pLstn, pid);
}

static void releasePidRatelimiterCache(lstn_t *pLstn) {
    unsigned int i;
    unsigned int count;

    assert(pLstn != NULL);
    if (pLstn == NULL || pLstn->ht == NULL) {
        return;
    }

    count = hashtable_count(pLstn->ht);
    hashtable_destroy(pLstn->ht, 1);
    pLstn->ht = NULL;
    for (i = 0; i < count; ++i) {
        STATSCOUNTER_DEC(ctrNumRatelimiters, mutCtrNumRatelimiters);
    }
}


/* 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);
    CHKiRet(ratelimitAddMsg(ratelimiter, NULL, pMsg));
    STATSCOUNTER_INC(ctrSubmit, mutCtrSubmit);
finalize_it:
    if (iRet == RS_RET_DISCARDMSG) {
        STATSCOUNTER_INC(ctrLostRatelimit, mutCtrLostRatelimit);
    } else 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->pszRatelimitNameSysSock != NULL || 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->pszRatelimitNameSysSock || 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;
        if (runModConf->pszRatelimitNameSysSock != NULL) {
            CHKiRet(ratelimitNewFromConfig(&listeners[0].dflt_ratelimiter, runModConf->pConf,
                                           (char *)runModConf->pszRatelimitNameSysSock, "imuxsock", NULL));
        } else {
            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 = -1;
    pModConf->ratelimitBurstSysSock = -1;
    pModConf->ratelimitSeveritySysSock = DFLT_ratelimitSeverity;
    pModConf->pszRatelimitNameSysSock = NULL;
    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 = (int)pvals[i].val.d.n;
        } else if (!strcmp(modpblk.descr[i].name, "syssock.ratelimit.burst")) {
            loadModConf->ratelimitBurstSysSock = (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 if (!strcmp(modpblk.descr[i].name, "syssock.ratelimit.name")) {
            loadModConf->pszRatelimitNameSysSock = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL);
        } else {
            dbgprintf(
                "imuxsock: program error, non-handled "
                "param '%s' in beginCnfLoad\n",
                modpblk.descr[i].name);
        }
    }

    if (loadModConf->pszRatelimitNameSysSock != NULL) {
        if (loadModConf->ratelimitIntervalSysSock != -1 || loadModConf->ratelimitBurstSysSock != -1) {
            LogError(0, RS_RET_INVALID_PARAMS,
                     "imuxsock: syssock.ratelimit.name is mutually exclusive with "
                     "syssock.ratelimit.interval and syssock.ratelimit.burst - using syssock.ratelimit.name");
        }
    } else {
        if (loadModConf->ratelimitIntervalSysSock == -1) loadModConf->ratelimitIntervalSysSock = DFLT_ratelimitInterval;
        if (loadModConf->ratelimitBurstSysSock == -1) loadModConf->ratelimitBurstSysSock = DFLT_ratelimitBurst;
    }

    /* 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 = (int)pvals[i].val.d.n;
        } else if (!strcmp(inppblk.descr[i].name, "ratelimit.burst")) {
            inst->ratelimitBurst = (int)pvals[i].val.d.n;
        } else if (!strcmp(inppblk.descr[i].name, "ratelimit.severity")) {
            inst->ratelimitSeverity = (int)pvals[i].val.d.n;
        } else if (!strcmp(inppblk.descr[i].name, "ratelimit.name")) {
            inst->pszRatelimitName = (uchar *)es_str2cstr(pvals[i].val.d.estr, NULL);
        } else {
            dbgprintf(
                "imuxsock: program error, non-handled "
                "param '%s'\n",
                inppblk.descr[i].name);
        }
    }

    if (inst->pszRatelimitName != NULL) {
        if (inst->ratelimitInterval != -1 || inst->ratelimitBurst != -1) {
            LogError(0, RS_RET_INVALID_PARAMS,
                     "imuxsock: ratelimit.name is mutually exclusive with "
                     "ratelimit.interval and ratelimit.burst - using ratelimit.name");
        }
    } else {
        if (inst->ratelimitInterval == -1) inst->ratelimitInterval = DFLT_ratelimitInterval;
        if (inst->ratelimitBurst == -1) inst->ratelimitBurst = DFLT_ratelimitBurst;
    }

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);
    free(pModConf->pszRatelimitNameSysSock);
    for (inst = pModConf->root; inst != NULL;) {
        free(inst->sockName);
        free(inst->pszBindRuleset);
        free(inst->pLogHostName);
        free(inst->pszRatelimitName);
        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
