/* 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-2014 Rainer Gerhards and Adiscon GmbH.
 *
 * This file is part of rsyslog.
 *
 * Rsyslog is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Rsyslog is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Rsyslog.  If not, see <http://www.gnu.org/licenses/>.
 *
 * A copy of the GPL can be found in the file "COPYING" in this distribution.
 */
#include "config.h"
#include "rsyslog.h"
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/socket.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 "unlimited_select.h"
#include "sd-daemon.h"
#include "statsobj.h"
#include "datetime.h"
#include "hashtable.h"
#include "ratelimit.h"

MODULE_TYPE_INPUT
MODULE_TYPE_NOKEEP
MODULE_CNFNAME("imuxsock")

/* defines */
#define MAXFUNIX	50
#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

/* forward definitions */
static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal);

/* emulate struct ucred for platforms that do not have it */
#ifndef HAVE_SCM_CREDENTIALS
struct ucred { int pid; 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(errmsg)
DEFobjCurrIf(glbl)
DEFobjCurrIf(prop)
DEFobjCurrIf(net)
DEFobjCurrIf(parser)
DEFobjCurrIf(datetime)
DEFobjCurrIf(statsobj)


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 */
	int ratelimitInterval;
	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 */
} lstn_t;
static lstn_t listeners[MAXFUNIX];

static prop_t *pLocalHostIP = NULL;	/* there is only one global IP for all internally-generated messages */
static prop_t *pInputName = NULL;	/* our inputName currently is always "imudp", and this will hold it */
static int startIndexUxLocalSockets; /* process fd from that index on (used to
 				   * suppress local logging. rgerhards 2005-08-01
				   * read-only after startup
				   */
static int nfd = 1; /* number of Unix sockets open / read-only after startup */
static int sd_fds = 0;			/* number of systemd activated sockets */

/* config vars for legacy config system */
#define DFLT_bCreatePath 0
#define DFLT_ratelimitInterval 0
#define DFLT_ratelimitBurst 200
#define DFLT_ratelimitSeverity 1			/* do not rate-limit emergency messages */
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? */
	int ratelimitInterval;		/* interval in seconds, 0 = off */
	int ratelimitIntervalSysSock;
	int ratelimitBurst;		/* max nbr of messages in interval */
	int ratelimitBurstSysSock;
	int ratelimitSeverity;
	int ratelimitSeveritySysSock;
	int bAnnotate;			/* annotate trusted properties */
	int bAnnotateSysSock;		/* same, for system log socket */
	int bParseTrusted;		/* parse trusted properties */
} cs;

struct instanceConf_s {
	uchar *sockName;
	uchar *pLogHostName;		/* host name to use with this socket */
	sbool bUseFlowCtl;		/* use flow control or not (if yes, only LIGHT is used! */
	sbool bIgnoreTimestamp;		/* ignore timestamps present in the incoming message? */
	sbool bWritePid;		/* use credentials from recvmsg() and fixup PID in TAG */
	sbool bUseSysTimeStamp;		/* use timestamp from system (instead of from message) */
	int bCreatePath;		/* auto-create socket path? */
	int ratelimitInterval;		/* interval in seconds, 0 = off */
	int ratelimitBurst;		/* max nbr of messages in interval */
	int ratelimitSeverity;
	int bAnnotate;			/* annotate trusted properties */
	int bParseTrusted;		/* parse trusted properties */
	sbool bDiscardOwnMsgs;		/* discard messages that originated from our own pid? */
	sbool bUnlink;
	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;
	int bAnnotateSysSock;
	int bParseTrusted;
	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.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 },
	{ "usepidfromsystem", eCmdHdlrBinary, 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
	};

/* we do not use this, because we do not bind to a ruleset so far
 * enable when this is changed: #include "im-helper.h" */ /* must be included AFTER the type definitions! */

static int bLegacyCnfModGlobalsPermitted;/* are legacy module-global config parameters permitted? */


/* create input instance, set default paramters, 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->ratelimitInterval = DFLT_ratelimitInterval;
	inst->ratelimitBurst = DFLT_ratelimitBurst;
	inst->ratelimitSeverity = DFLT_ratelimitSeverity;
	inst->bUseFlowCtl = 0;
	inst->bIgnoreTimestamp = 1;
	inst->bCreatePath = DFLT_bCreatePath;
	inst->bUseSysTimeStamp = 1;
	inst->bWritePid = 0;
	inst->bAnnotate = 0;
	inst->bParseTrusted = 0;
	inst->bDiscardOwnMsgs = 1;
	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 instace shall be added to 
 * the current config object via the legacy config system. It just shuffles
 * all parameters to the listener in-memory instance.
 * rgerhards, 2011-05-12
 */
static rsRetVal addInstance(void __attribute__((unused)) *pVal, uchar *pNewVal)
{
	instanceConf_t *inst;
	DEFiRet;

	if(pNewVal == NULL || pNewVal[0] == '\0') {
		errmsg.LogError(0, RS_RET_SOCKNAME_MISSING , "imuxsock: socket name must be specified, "
			        "but is not - listener not created\n");
		if(pNewVal != NULL)
			free(pNewVal);
		ABORT_FINALIZE(RS_RET_SOCKNAME_MISSING);
	}

	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->next = NULL;

	/* some legacy conf processing */
	free(cs.pLogHostName); /* reset hostname for next socket */
	cs.pLogHostName = NULL;

finalize_it:
	RETiRet;
}


/* add an additional listen socket. Socket names are added
 * until the array is filled up. It is never reset, only at
 * module unload.
 * TODO: we should change the array to a list so that we
 * can support any number of listen socket names.
 * rgerhards, 2007-12-20
 * added capability to specify hostname for socket -- rgerhards, 2008-08-01
 */
static rsRetVal
addListner(instanceConf_t *inst)
{
	DEFiRet;

	if(nfd < MAXFUNIX) {
		if(*inst->sockName == ':') {
			listeners[nfd].bParseHost = 1;
		} else {
			listeners[nfd].bParseHost = 0;
		}
		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;
			}
		}
		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) ? 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;
		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++;
	} else {
		errmsg.LogError(0, NO_ERRCODE, "Out of unix socket name descriptors, ignoring %s\n",
			 inst->sockName);
	}

finalize_it:
	RETiRet;
}


/* discard/Destruct all log sockets except for "socket" 0. Data for it comes from
 * the constant memory pool - and if not, it is freeed via some other pointer.
 */
static rsRetVal discardLogSockets(void)
{
	int i;

        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. 
 */
static inline rsRetVal
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));
	pLstn->fd = socket(AF_UNIX, SOCK_DGRAM, 0);
	if(pLstn->fd < 0 || bind(pLstn->fd, (struct sockaddr *) &sunx, SUN_LEN(&sunx)) < 0 ||
	    chmod((char*)pLstn->sockName, 0666) < 0) {
		errmsg.LogError(errno, NO_ERRCODE, "cannot create '%s'", pLstn->sockName);
		DBGPRINTF("cannot create %s (%d).\n", pLstn->sockName, errno);
		if(pLstn->fd != -1)
			close(pLstn->fd);
		pLstn->fd = -1;
		ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
	}
finalize_it:
	RETiRet;
}


static inline rsRetVal
openLogSocket(lstn_t *pLstn)
{
	DEFiRet;
#	if HAVE_SCM_CREDENTIALS
	int one;
#	endif /* HAVE_SCM_CREDENTIALS */

	if(pLstn->sockName[0] == '\0')
		return -1;

	pLstn->fd = -1;

	if (sd_fds > 0) {
               /* Check if the current socket is a systemd activated one.
	        * If so, just use it.
		*/
		int fd;

		for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + sd_fds; fd++) {
			if( sd_is_socket_unix(fd, SOCK_DGRAM, -1, (const char*) pLstn->sockName, 0) == 1) {
				/* ok, it matches -- just use as is */
				pLstn->fd = fd;

				DBGPRINTF("imuxsock: Acquired UNIX socket '%s' (fd %d) from systemd.\n",
					pLstn->sockName, pLstn->fd);
				break;
			}
			/*
			 * otherwise it either didn't matched *this* socket and
			 * we just continue to check the next one or there were
			 * an error and we will create a new socket bellow.
			 */
		}
	}

	if (pLstn->fd == -1) {
		CHKiRet(createLogSocket(pLstn));
	}

#	if HAVE_SCM_CREDENTIALS
	if(pLstn->bUseCreds) {
		one = 1;
		if(setsockopt(pLstn->fd, SOL_SOCKET, SO_PASSCRED, &one, (socklen_t) sizeof(one)) != 0) {
			errmsg.LogError(errno, NO_ERRCODE, "set SO_PASSCRED failed on '%s'", pLstn->sockName);
			pLstn->bUseCreds = 0;
		}
// TODO: move to its own #if
		if(setsockopt(pLstn->fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) != 0) {
			errmsg.LogError(errno, NO_ERRCODE, "set SO_TIMESTAMP failed on '%s'", pLstn->sockName);
		}
	}
#	else /* HAVE_SCM_CREDENTIALS */
	pLstn->bUseCreds = 0;
	pLstn->bAnnotate = 0;
#	endif /* HAVE_SCM_CREDENTIALS */

finalize_it:
	if(iRet != RS_RET_OK) {
		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 inline rsRetVal
findRatelimiter(lstn_t *pLstn, struct ucred *cred, ratelimit_t **prl)
{
	ratelimit_t *rl;
	int r;
	pid_t *keybuf;
	char pidbuf[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) {
		/* 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);
		snprintf(pidbuf, sizeof(pidbuf), "pid %lu",
			(unsigned long) cred->pid);
		pidbuf[sizeof(pidbuf)-1] = '\0'; /* to be on safe side */
		CHKiRet(ratelimitNew(&rl, "imuxsock", pidbuf));
		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;

finalize_it:
	if(*prl == NULL)
		*prl = pLstn->dflt_ratelimiter;
	RETiRet;
}


/* patch correct pid into tag. bufTAG MUST be CONF_TAG_MAXSIZE long!
 */
static inline void
fixPID(uchar *bufTAG, int *lenTag, struct ucred *cred)
{
	int i;
	char bufPID[16];
	int lenPID;

	if(cred == NULL)
		return;
	
	lenPID = snprintf(bufPID, sizeof(bufPID), "[%lu]:", (unsigned long) cred->pid);

	for(i = *lenTag ; i >= 0  && bufTAG[i] != '[' ; --i)
		/*JUST SKIP*/;

	if(i < 0)
		i = *lenTag - 1; /* go right at end of TAG, pid was not present (-1 for ':') */
	
	if(i + lenPID > CONF_TAG_MAXSIZE)
		return; /* do not touch, as things would break */

	memcpy(bufTAG + i, bufPID, lenPID);
	*lenTag = i + lenPID;
}


/* Get an "trusted property" from the system. Returns an empty string if the
 * property can not be obtained. Inspired by similiar functionality inside
 * journald. Currently works with Linux /proc filesystem, only.
 */
static rsRetVal
getTrustedProp(struct ucred *cred, char *propName, uchar *buf, size_t lenBuf, int *lenProp)
{
	int fd;
	int i;
	int lenRead;
	char namebuf[1024];
	DEFiRet;

	if(snprintf(namebuf, sizeof(namebuf), "/proc/%lu/%s", (long unsigned) cred->pid,
		propName) >= (int) sizeof(namebuf)) {
		ABORT_FINALIZE(RS_RET_ERR);
	}

	if((fd = open(namebuf, O_RDONLY)) == -1) {
		DBGPRINTF("error reading '%s'\n", namebuf);
		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 inline int
copyescaped(uchar *dstbuf, uchar *inbuf, int inlen)
{
	int iDst, iSrc;

	*dstbuf = '"';
	for(iDst=1, iSrc=0 ; iSrc < inlen ; ++iDst, ++iSrc) {
		if(inbuf[iSrc] == '"' || inbuf[iSrc] == '\\') {
			dstbuf[iDst++] = '\\';
		}
		dstbuf[iDst] = inbuf[iSrc];
	}
	dstbuf[iDst++] = '"';
	return iDst;
}


/* submit received message to the queue engine
 * We now parse the message according to expected format so that we
 * can also mangle it if necessary.
 */
static inline rsRetVal
SubmitMsg(uchar *pRcv, int lenRcv, lstn_t *pLstn, struct ucred *cred, struct timeval *ts)
{
	msg_t *pMsg;
	int lenMsg;
	int offs;
	int i;
	uchar *parse;
	int pri;
	int facil;
	int sever;
	uchar bufParseTAG[CONF_TAG_MAXSIZE];
	struct syslogTime st;
	time_t tt;
	int lenProp;
	ratelimit_t *ratelimiter = NULL;
	uchar propBuf[1024];
	uchar msgbuf[8192];
	uchar *pmsgbuf;
	int toffs; /* offset for trusted properties */
	struct syslogTime dummyTS;
	struct json_object *json = NULL, *jval;
	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;
	} 
	facil = pri2fac(pri);
	sever = pri2sev(pri);

	findRatelimiter(pLstn, cred, &ratelimiter); /* ignore error, better so than others... */

	if(ts == NULL) {
		datetime.getCurrTime(&st, &tt);
	} else {
		datetime.timeval2syslogTime(ts, &st);
		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

	/* created trusted properties */
	if(cred != NULL && pLstn->bAnnotate) {
		if((unsigned) (lenRcv + 4096) < sizeof(msgbuf)) {
			pmsgbuf = msgbuf;
		} else {
			CHKmalloc(pmsgbuf = malloc(lenRcv+4096));
		}

		if (pLstn->bParseTrusted) {
			json = json_object_new_object();
			/* create value string, create field, and add it */
			jval = json_object_new_int(cred->pid);
			json_object_object_add(json, "pid", jval);
			jval = json_object_new_int(cred->uid);
			json_object_object_add(json, "uid", jval);
			jval = json_object_new_int(cred->gid);
			json_object_object_add(json, "gid", jval);
			if(getTrustedProp(cred, "comm", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) {
				jval = json_object_new_string((char*)propBuf);
				json_object_object_add(json, "appname", jval);
			}
			if(getTrustedExe(cred, propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) {
				jval = json_object_new_string((char*)propBuf);
				json_object_object_add(json, "exe", jval);
			}
			if(getTrustedProp(cred, "cmdline", propBuf, sizeof(propBuf), &lenProp) == RS_RET_OK) {
				jval = json_object_new_string((char*)propBuf);
				json_object_object_add(json, "cmd", jval);
			}
		} else {
			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';

			pRcv = pmsgbuf;
			lenRcv = toffs + 1;
		}
	}

	/* we now create our own message object and submit it to the queue */
	CHKiRet(msgConstructWithTime(&pMsg, &st, tt));
	MsgSetRawMsg(pMsg, (char*)pRcv, lenRcv);
	parser.SanitizeMsg(pMsg);
	lenMsg = pMsg->iLenRawMsg - offs; /* SanitizeMsg() may have changed the size */
	MsgSetInputName(pMsg, pInputName);
	MsgSetFlowControlType(pMsg, pLstn->flowCtl);

	pMsg->iFacility = facil;
	pMsg->iSeverity = sever;
	MsgSetAfterPRIOffs(pMsg, offs);

	parse++; lenMsg--; /* '>' */

	if(json != NULL) {
		/* as per lumberjack spec, these properties need to go into
		 * the CEE root.
		 */
		msgAddJSON(pMsg, (uchar*)"!", json);
	}

	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);
			}
		} else {
			if(datetime.ParseTIMESTAMP3339(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK &&
			   datetime.ParseTIMESTAMP3164(&(pMsg->tTIMESTAMP), &parse, &lenMsg) != RS_RET_OK) {
				DBGPRINTF("we have a problem, invalid timestamp in msg!\n");
			}
		}
	} else { /* if we pulled the time from the system, we need to update the message text */
		uchar *tmpParse = parse; /* just to check correctness of TS */
		if(datetime.ParseTIMESTAMP3339(&dummyTS, &tmpParse, &lenMsg) == RS_RET_OK ||
		   datetime.ParseTIMESTAMP3164(&dummyTS, &tmpParse, &lenMsg) == RS_RET_OK) {
			/* We modify the message only if it contained a valid timestamp,
			 * otherwise we do not touch it at all. */
			datetime.formatTimestamp3164(&st, (char*)parse, 0);
			parse[15] = ' '; /* re-write \0 from fromatTimestamp3164 by SP */
			/* update "counters" to reflect processed timestamp */
			parse += 16;
		}
	}

	/* 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);

	if(pLstn->bParseHost) {
		pMsg->msgFlags  = pLstn->flags | PARSE_HOSTNAME;
	} else {
		pMsg->msgFlags  = pLstn->flags;
	}

	MsgSetRcvFrom(pMsg, pLstn->hostName == NULL ? glbl.GetLocalHostNameProp() : pLstn->hostName);
	CHKiRet(MsgSetRcvFromIP(pMsg, pLocalHostIP));
	ratelimitAddMsg(ratelimiter, NULL, pMsg);
	STATSCOUNTER_INC(ctrSubmit, mutCtrSubmit);
finalize_it:
	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 cmsghdr *cm;
	struct ucred *cred;
	struct timeval *ts;
	uchar bufRcv[4096+1];
	uchar *pRcv = NULL; /* receive buffer */
#	if HAVE_SCM_CREDENTIALS
	char aux[128];
#	endif

	assert(pLstn->fd >= 0);

	iMaxLine = glbl.GetMaxLine();

	/* we optimize performance: if iMaxLine is below 4K (which it is in almost all
	 * cases, we use a fixed buffer on the stack. Only if it is higher, heap memory
	 * is used. We could use alloca() to achive a similar aspect, but there are so
	 * many issues with alloca() that I do not want to take that route.
	 * rgerhards, 2008-09-02
	 */
	if((size_t) iMaxLine < sizeof(bufRcv) - 1) {
		pRcv = bufRcv;
	} else {
		CHKmalloc(pRcv = (uchar*) MALLOC(sizeof(uchar) * (iMaxLine + 1)));
	}

	memset(&msgh, 0, sizeof(msgh));
	memset(&msgiov, 0, sizeof(msgiov));
#	if HAVE_SCM_CREDENTIALS
	if(pLstn->bUseCreds) {
		memset(&aux, 0, sizeof(aux));
		msgh.msg_control = aux;
		msgh.msg_controllen = sizeof(aux);
	}
#	endif
	msgiov.iov_base = pRcv;
	msgiov.iov_len = iMaxLine;
	msgh.msg_iov = &msgiov;
	msgh.msg_iovlen = 1;
	iRcvd = recvmsg(pLstn->fd, &msgh, MSG_DONTWAIT);
 
	DBGPRINTF("Message from UNIX socket: #%d\n", pLstn->fd);
	if(iRcvd > 0) {
		cred = NULL;
		ts = NULL;
		if(pLstn->bUseCreds || pLstn->bUseSysTimeStamp) {
			for(cm = CMSG_FIRSTHDR(&msgh); cm; cm = CMSG_NXTHDR(&msgh, cm)) {
#				if HAVE_SCM_CREDENTIALS
				if(   pLstn->bUseCreds
				   && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_CREDENTIALS) {
					cred = (struct ucred*) CMSG_DATA(cm);
				}
#				endif /* HAVE_SCM_CREDENTIALS */
#				if HAVE_SO_TIMESTAMP
				if(   pLstn->bUseSysTimeStamp 
				   && cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SO_TIMESTAMP) {
					ts = (struct timeval *)CMSG_DATA(cm);
				}
#				endif /* HAVE_SO_TIMESTAMP */
			}
		}
		CHKiRet(SubmitMsg(pRcv, iRcvd, pLstn, cred, ts));
	} 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);
		errmsg.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 inline rsRetVal
activateListeners()
{
	register int i;
	int actSocks;
	DEFiRet;

	/* first apply some config settings */
#	ifdef OS_SOLARIS
		/* under solaris, we must NEVER process the local log socket, because
		 * it is implemented there differently. If we used it, we would actually
		 * delete it and render the system partly unusable. So don't do that.
		 * rgerhards, 2010-03-26
		 */
		startIndexUxLocalSockets = 1;
#	else
		startIndexUxLocalSockets = runModConf->bOmitLocalLogging ? 1 : 0;
#	endif
	if(runModConf->pLogSockName != NULL)
		listeners[0].sockName = runModConf->pLogSockName;
	else if(sd_booted()) {
		struct stat st;
		if(stat(SYSTEMD_PATH_LOG, &st) != -1 && S_ISSOCK(st.st_mode)) {
			listeners[0].sockName = (uchar*) SYSTEMD_PATH_LOG;
		}
	}
	if(runModConf->ratelimitIntervalSysSock > 0) {
		if((listeners[0].ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL)) == NULL) {
			/* in this case, we simply turn of rate-limiting */
			errmsg.LogError(0, NO_ERRCODE, "imuxsock: turning off rate limiting because we could not "
				  "create hash table\n");
			runModConf->ratelimitIntervalSysSock = 0;
		}
	}
	listeners[0].ratelimitInterval = runModConf->ratelimitIntervalSysSock;
	listeners[0].ratelimitBurst = runModConf->ratelimitBurstSysSock;
	listeners[0].ratelimitSev = runModConf->ratelimitSeveritySysSock;
	listeners[0].bUseCreds = (runModConf->bWritePidSysSock || runModConf->ratelimitIntervalSysSock || runModConf->bAnnotateSysSock || runModConf->bDiscardOwnMsgs) ? 1 : 0;
	listeners[0].bWritePid = runModConf->bWritePidSysSock;
	listeners[0].bAnnotate = runModConf->bAnnotateSysSock;
	listeners[0].bParseTrusted = runModConf->bParseTrusted;
	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);

	sd_fds = sd_listen_fds(0);
	if(sd_fds < 0) {
		errmsg.LogError(-sd_fds, NO_ERRCODE, "imuxsock: Failed to acquire systemd socket");
		ABORT_FINALIZE(RS_RET_ERR_CRE_AFUX);
	}

	/* initialize and return if will run or not */
	actSocks = 0;
	for (i = startIndexUxLocalSockets ; i < nfd ; i++) {
		if(openLogSocket(&(listeners[i])) == RS_RET_OK) {
			++actSocks;
			DBGPRINTF("imuxsock: Opened UNIX socket '%s' (fd %d).\n",
				  listeners[i].sockName, listeners[i].fd);
		}
	}

	if(actSocks == 0) {
		errmsg.LogError(0, NO_ERRCODE, "imuxsock does not run because we could not aquire any socket\n");
		ABORT_FINALIZE(RS_RET_ERR);
	}

finalize_it:
	RETiRet;
}



BEGINbeginCnfLoad
CODESTARTbeginCnfLoad
	loadModConf = pModConf;
	pModConf->pConf = pConf;
	/* 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->bDiscardOwnMsgs = 1;
	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) {
		errmsg.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.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 {
			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) {
		errmsg.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(modpblk.descr[i].name, "createpath")) {
			inst->bCreatePath = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "parsetrusted")) {
			inst->bParseTrusted = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "ignoreownmessages")) {
			inst->bDiscardOwnMsgs = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "unlink")) {
			inst->bUnlink = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "hostname")) {
			inst->pLogHostName = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
		} else if(!strcmp(modpblk.descr[i].name, "ignoretimestamp")) {
			inst->bIgnoreTimestamp = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "flowcontrol")) {
			inst->bUseFlowCtl = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "usesystimestamp")) {
			inst->bUseSysTimeStamp = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "annotate")) {
			inst->bAnnotate = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "usepidfromsystem")) {
			inst->bWritePid = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "ratelimit.interval")) {
			inst->ratelimitInterval = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.descr[i].name, "ratelimit.burst")) {
			inst->ratelimitBurst = (int) pvals[i].val.d.n;
		} else if(!strcmp(modpblk.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 */
		loadModConf->bOmitLocalLogging = cs.bOmitLocalLogging;
		loadModConf->pLogSockName = cs.pLogSockName;
		loadModConf->bIgnoreTimestamp = cs.bIgnoreTimestampSysSock;
		loadModConf->bUseFlowCtl = cs.bUseFlowCtlSysSock;
		loadModConf->bAnnotateSysSock = cs.bAnnotateSysSock;
		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


BEGINcheckCnf
CODESTARTcheckCnf
ENDcheckCnf


BEGINactivateCnfPrePrivDrop
	instanceConf_t *inst;
CODESTARTactivateCnfPrePrivDrop
	runModConf = pModConf;
	for(inst = runModConf->root ; inst != NULL ; inst = inst->next) {
		addListner(inst);
	}
	CHKiRet(activateListeners());
finalize_it:
ENDactivateCnfPrePrivDrop


BEGINactivateCnf
CODESTARTactivateCnf
ENDactivateCnf


BEGINfreeCnf
	instanceConf_t *inst, *del;
CODESTARTfreeCnf
	free(pModConf->pLogSockName);
	for(inst = pModConf->root ; inst != NULL ; ) {
		free(inst->sockName);
		free(inst->pLogHostName);
		del = inst;
		inst = inst->next;
		free(del);
	}
ENDfreeCnf


/* This function is called to gather input. */
BEGINrunInput
	int maxfds;
	int nfds;
	int i;
	int fd;
#ifdef USE_UNLIMITED_SELECT
        fd_set  *pReadfds = malloc(glbl.GetFdSetSize());
#else
        fd_set  readfds;
        fd_set *pReadfds = &readfds;
#endif

CODESTARTrunInput
	/* this is an endless loop - it is terminated when the thread is
	 * signalled to do so. This, however, is handled by the framework,
	 * right into the sleep below.
	 */
	while(1) {
		/* Add the Unix Domain Sockets to the list of read
		 * descriptors.
		 * rgerhards 2005-08-01: we must now check if there are
		 * any local sockets to listen to at all. If the -o option
		 * is given without -a, we do not need to listen at all..
		 */
	        maxfds = 0;
	        FD_ZERO (pReadfds);
		/* Copy master connections */
		for (i = startIndexUxLocalSockets; i < nfd; i++) {
			if (listeners[i].fd!= -1) {
				FD_SET(listeners[i].fd, pReadfds);
				if(listeners[i].fd > maxfds)
					maxfds=listeners[i].fd;
			}
		}

		if(Debug) {
			dbgprintf("--------imuxsock calling select, active file descriptors (max %d): ", maxfds);
			for (nfds= 0; nfds <= maxfds; ++nfds)
				if ( FD_ISSET(nfds, pReadfds) )
					dbgprintf("%d ", nfds);
			dbgprintf("\n");
		}

		/* wait for io to become ready */
		nfds = select(maxfds+1, (fd_set *) pReadfds, NULL, NULL, NULL);
		if(glbl.GetGlobalInputTermState() == 1)
			break; /* terminate input! */

		for (i = 0; i < nfd && nfds > 0; i++) {
			if(glbl.GetGlobalInputTermState() == 1)
				ABORT_FINALIZE(RS_RET_FORCE_TERM); /* terminate input! */
			if ((fd = listeners[i].fd) != -1 && FD_ISSET(fd, pReadfds)) {
				readSocket(&(listeners[i]));
				--nfds; /* indicate we have processed one */
			}
		}
	}

finalize_it:
	freeFdSet(pReadfds);
	RETiRet;
ENDrunInput


BEGINwillRun
CODESTARTwillRun
ENDwillRun


BEGINafterRun
	int i;
CODESTARTafterRun
	/* do cleanup here */
	/* 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 &&
			    listeners[i].fd >= SD_LISTEN_FDS_START &&
			    listeners[i].fd <  SD_LISTEN_FDS_START + sd_fds)
				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
	if(pInputName != NULL)
		prop.Destruct(&pInputName);

	statsobj.Destruct(&modStats);

	objRelease(parser, CORE_COMPONENT);
	objRelease(glbl, CORE_COMPONENT);
	objRelease(errmsg, CORE_COMPONENT);
	objRelease(prop, CORE_COMPONENT);
	objRelease(statsobj, CORE_COMPONENT);
	objRelease(datetime, CORE_COMPONENT);
ENDmodExit


BEGINisCompatibleWithFeature
CODESTARTisCompatibleWithFeature
	if(eFeat == sFEATURENonCancelInputTermination)
		iRet = RS_RET_OK;
ENDisCompatibleWithFeature


BEGINqueryEtryPt
CODESTARTqueryEtryPt
CODEqueryEtryPt_STD_IMOD_QUERIES
CODEqueryEtryPt_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()
	int i;
CODESTARTmodInit
	*ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
CODEmodInit_QueryRegCFSLineHdlr
	CHKiRet(objUse(errmsg, CORE_COMPONENT));
	CHKiRet(objUse(glbl, CORE_COMPONENT));
	CHKiRet(objUse(net, CORE_COMPONENT));
	CHKiRet(objUse(prop, CORE_COMPONENT));
	CHKiRet(objUse(statsobj, CORE_COMPONENT));
	CHKiRet(objUse(datetime, CORE_COMPONENT));
	CHKiRet(objUse(parser, CORE_COMPONENT));

	DBGPRINTF("imuxsock version %s initializing\n", PACKAGE_VERSION);

	/* init legacy config vars */
	cs.pLogSockName = NULL;
	cs.pLogHostName = NULL;	/* host name to use with this socket */

	/* we need to create the inputName property (only once during our lifetime) */
	CHKiRet(prop.Construct(&pInputName));
	CHKiRet(prop.SetString(pInputName, UCHAR_CONSTANT("imuxsock"), sizeof("imuxsock") - 1));
	CHKiRet(prop.ConstructFinalize(pInputName));

	/* right now, glbl does not permit per-instance IP address notation. As long as this
	 * is the case, it is OK to query the HostIP once here at this location. HOWEVER, the
	 * whole concept is not 100% clean and needs to be addressed on a higher layer.
	 * TODO / rgerhards, 2012-04-11
	 */
	pLocalHostIP = glbl.GetLocalHostIP();

	/* init system log socket settings */
	listeners[0].flags = IGNDATE;
	listeners[0].sockName = UCHAR_CONSTANT(_PATH_LOG);
	listeners[0].hostName = NULL;
	listeners[0].flowCtl = eFLOWCTL_NO_DELAY;
	listeners[0].fd = -1;
	listeners[0].bParseHost = 0;
	listeners[0].bUseCreds = 0;
	listeners[0].bAnnotate = 0;
	listeners[0].bParseTrusted = 0;
	listeners[0].bDiscardOwnMsgs = 1;
	listeners[0].bUnlink = 1;
	listeners[0].bCreatePath = 0;
	listeners[0].bUseSysTimeStamp = 1;
	if((listeners[0].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 for system socket "
			  "because we could not create hash table\n");
		listeners[0].ratelimitInterval = 0;
	}

	/* initialize socket names */
	for(i = 1 ; i < MAXFUNIX ; ++i) {
		listeners[i].sockName = NULL;
		listeners[i].fd  = -1;
	}

	/* 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")));
	STATSCOUNTER_INIT(ctrSubmit, mutCtrSubmit);
	CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("submitted"),
		ctrType_IntCtr, &ctrSubmit));
	STATSCOUNTER_INIT(ctrLostRatelimit, mutCtrLostRatelimit);
	CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.discarded"),
		ctrType_IntCtr, &ctrLostRatelimit));
	STATSCOUNTER_INIT(ctrNumRatelimiters, mutCtrNumRatelimiters);
	CHKiRet(statsobj.AddCounter(modStats, UCHAR_CONSTANT("ratelimit.numratelimiters"),
		ctrType_IntCtr, &ctrNumRatelimiters));
	CHKiRet(statsobj.ConstructFinalize(modStats));

ENDmodInit
/* vim:set ai:
 */
