diff options
Diffstat (limited to 'liblog/logger.c')
| -rw-r--r-- | liblog/logger.c | 519 | 
1 files changed, 519 insertions, 0 deletions
| diff --git a/liblog/logger.c b/liblog/logger.c new file mode 100644 index 0000000..2fe40cc --- /dev/null +++ b/liblog/logger.c @@ -0,0 +1,519 @@ +/* +** Copyright 1998 - 2006 Double Precision, Inc. +** See COPYING for distribution information. +*/ + +#if	HAVE_CONFIG_H +#include	"config.h" +#endif +#include	<string.h> +#include	<stdio.h> +#include	<stdlib.h> +#include	<sys/types.h> +#include	<sys/wait.h> +#include	<signal.h> +#include	<errno.h> +#include	<fcntl.h> +#include	<pwd.h> +#include	<grp.h> +#if	HAVE_UNISTD_H +#include	<unistd.h> +#endif +#if	HAVE_SYSLOG_H +#include	<syslog.h> +#else +#undef	TESTMODE +#define	TESTMODE	1 +#endif +#include	"../liblock/config.h" +#include	"../liblock/liblock.h" +#include	"../numlib/numlib.h" + + +static int do_logger(const char *name, int facility, FILE *f) +{ +char	buf[512]; +char	*p; +int	c; + +#if	TESTMODE + +#else +	openlog(name, 0 + +#ifdef	LOG_NDELAY +			| LOG_NDELAY +#else +			| LOG_NOWAIT +#endif + +			, facility); +#endif +	if (chdir("/") < 0) +	{ +		perror("chdir(\"/\")"); +		exit(1); +	} + +	while (fgets(buf, sizeof(buf), f)) +	{ +		if ((p=strchr(buf, '\n')) != 0)	*p=0; +		else +			while ((c=getchar()) != EOF && c != '\n') +				; + +#if	TESTMODE +		fprintf(stderr, "%s: %s\n", name, buf); +#else +		c=LOG_INFO; +		if (strncmp(buf, "ERR:", 4) == 0) +		{ +			c=LOG_ERR; +			p=buf+4; +		} +		else if (strncmp(buf, "WARN:", 5) == 0) +		{ +			c=LOG_WARNING; +			p=buf+5; +		} +		else if (strncmp(buf, "ALERT:", 6) == 0) +		{ +			c=LOG_ALERT; +			p=buf+6; +		} +		else if (strncmp(buf, "CRIT:", 5) == 0) +		{ +			c=LOG_CRIT; +			p=buf+5; +		} +		else if (strncmp(buf, "DEBUG:", 6) == 0) +		{ +			c=LOG_DEBUG; +			p=buf+6; +		} +		else if (strncmp(buf, "INFO:", 5) == 0) +			p=buf+5; +		else	p=buf; + +		while (*p == ' ') +			++p; + +		syslog(c, "%s", p); +#endif +	} +	return (0); +} + +static const char *namearg=0; +static const char *pidarg=0; +static char *lockfilename=0; + +struct lognames +{ +	char *name; +	int value; +}; + +static struct lognames facilitynames[] = +{ +#ifdef LOG_AUTH +	{ "auth",	LOG_AUTH }, +#endif +#ifdef LOG_AUTHPRIV +	{ "authpriv",	LOG_AUTHPRIV }, +#endif +#ifdef LOG_CONSOLE +	{ "console",	LOG_CONSOLE }, +#endif +#ifdef LOG_CRON +	{ "cron",	LOG_CRON }, +#endif +#ifdef LOG_DAEMON +	{ "daemon",	LOG_DAEMON }, +#endif +#ifdef LOG_FTP +	{ "ftp",	LOG_FTP }, +#endif +#ifdef LOG_KERN +	{ "kern",	LOG_KERN }, +#endif +#ifdef LOG_LPR +	{ "lpr",	LOG_LPR }, +#endif +#ifdef LOG_MAIL +	{ "mail",	LOG_MAIL }, +#endif +#ifdef LOG_AUTHPRIV +	{ "news",	LOG_NEWS }, +#endif +#ifdef LOG_SECURITY +	{ "security",	LOG_SECURITY }, +#endif +#ifdef LOG_USER +	{ "user",	LOG_USER }, +#endif +#ifdef LOG_UUCP +	{ "uucp",	LOG_UUCP }, +#endif +#ifdef LOG_LOCAL0 +	{ "local0",	LOG_LOCAL0 }, +#endif +#ifdef LOG_LOCAL1 +	{ "local1",	LOG_LOCAL1 }, +#endif +#ifdef LOG_LOCAL2 +	{ "local2",	LOG_LOCAL2 }, +#endif +#ifdef LOG_LOCAL3 +	{ "local3",	LOG_LOCAL3 }, +#endif +#ifdef LOG_LOCAL4 +	{ "local4",	LOG_LOCAL4 }, +#endif +#ifdef LOG_LOCAL5 +	{ "local5",	LOG_LOCAL5 }, +#endif +#ifdef LOG_LOCAL6 +	{ "local6",	LOG_LOCAL6 }, +#endif +#ifdef LOG_LOCAL7 +	{ "local7",	LOG_LOCAL7 }, +#endif +	{ 0,		0 } +}; + +static int hup_restart = 0; +static int respawn = 0; +static int child_pid = -1; + +static RETSIGTYPE sighup(int n) +{ +	if (child_pid > 0) +	{ +		/* The child may respond to HUP by dying. If so it will +		   close its stderr and do_logger will terminate; at that +		   point we need to restart it */ +		hup_restart = 1; +		kill (child_pid, SIGHUP); +	} +	signal(SIGHUP, sighup); +#if	RETSIGTYPE != void +	return (1); +#endif +} + +static RETSIGTYPE sigalrm(int n) +{ +	if (child_pid > 0) kill(child_pid, SIGKILL); +#if	RETSIGTYPE != void +	return (1); +#endif +} + +static RETSIGTYPE sigterm(int n) +{ +	if (child_pid > 0) +	{ +		hup_restart = 0; +		respawn = 0; +		kill(child_pid, SIGTERM); +		signal(SIGALRM, sigalrm); /* kill after 8 secs */ +		alarm(8); +	} +	else exit(0); +#if	RETSIGTYPE != void +	return (1); +#endif +} + +static void checkfd(int actual, int exp) +{ +	if (actual == exp) return; +	fprintf(stderr, "Bad fd, got %d, expected %d\n", actual, exp); +	if (actual < 0) perror("error"); +	exit(1); +} + +static int isid(const char *p) +{ +	while (*p) +	{ +		if (*p < '0' || *p > '9')	return (0); +		++p; +	} +	return (1); +} + +static void setuidgid(const char *userarg, +		      const char *grouparg) +{ +	if (grouparg) +	{ +	gid_t gid = 0; +	struct group *gr; +	 +		if (isid(grouparg)) +			gid=atoi(grouparg); +		else if ((gr=getgrnam(grouparg)) == 0) +		{ +			fprintf(stderr, "Group not found: %s\n", grouparg); +			exit(1); +		} +		else	gid=gr->gr_gid; + +		libmail_changegroup(gid); +	} + +	if (userarg) +	{ +	uid_t	uid; + +		if (isid(userarg)) +		{ +			uid=atoi(userarg); +			libmail_changeuidgid(uid, getgid()); +		} +		else +		{ +		gid_t	g=getgid(), *gp=0; + +			if (grouparg)	gp= &g; +			libmail_changeusername(userarg, gp); +		} +	} +} + + +static void startchild(char **argv, const char *userarg, const char *grouparg) +{ +pid_t p; +int pipefd[2]; +int tmp; + +	signal(SIGTERM, sigterm); +	signal(SIGHUP, sighup); + +	/* Make sure the pipefds are at least 3 and 4. If we have an open +	   stderr then keep it around for debugging purposes. */ +	close(0); +	checkfd(open("/dev/null", O_RDWR), 0); +	close(1); +	checkfd(dup(0), 1); +	if ((tmp = dup(0)) > 2) close(tmp); + +	if (pipe(pipefd) < 0) +	{ +		perror("pipe"); +		exit(1); +	} +	p = fork(); +	if (p < 0) +	{ +		perror("fork"); +		exit(1); +	} +	if (p == 0) +	{ + +		close(pipefd[0]); +		close(2); +		checkfd(dup(pipefd[1]), 2); +		close(pipefd[1]); +		setuidgid(userarg, grouparg); +		execvp(argv[0], argv); +		perror("exec"); +		exit(1); +	} + +	// We can close stderr now + +	close(2); +	checkfd(dup(0), 2); + + +	close(pipefd[1]); +	close(0); +	checkfd(dup(pipefd[0]), 0); +	close(pipefd[0]); +	child_pid = p; +} +/* + * Note that we now support several modes of operation: + * + * (1) standalone logger, just collect messages on stdin and feed to syslog + *   courierlogger foo + *   courierlogger -name=foo + * + * (2) run a child process, collect its stderr messages and feed to syslog + *   courierlogger [-name=foo] foo arg1 arg2 + * + * (3) start a detached daemon with a child process + *   courierlogger [-name=foo] -pid=/var/run/foo.pid -start foo arg1 arg2 + * + * (4) stop or restart a detached daemon + *   courierlogger -pid=/var/run/foo.pid -stop      (or receive a SIGTERM) + *   courierlogger -pid=/var/run/foo.pid -restart   (or receive a SIGHUP) + */ +  +int main(int argc, char **argv) +{ +int facility = LOG_DEST; +int daemon = 0; +int lockfd = -1; +const char *userarg = 0; +const char *grouparg = 0; +int droproot=0; + +	if (argc == 2 && argv[1][0] != '-') +	{ +		/* backwards-compatibility mode */ +		close(1); +		close(2); +		checkfd(open("/dev/null", O_WRONLY), 1); +		checkfd(open("/dev/null", O_WRONLY), 2); +		do_logger(argv[1], facility, stdin); +		exit(0); +	} + +	if (argc <= 1) +	{ +		fprintf(stderr, + +			"Usage: courierlogger [-name=name] [-pid=pidfile] [-facility=type]\n" +			"       [-start|-stop|-restart] [cmd [args...]]\n" +		); +		exit(1); +	} + +	argv++, argc--; +	while (argc > 0 && argv[0][0]=='-') +	{ +		if (strncmp(argv[0],"-pid=",5) == 0 && argv[0][5]) +		{ +			pidarg=&argv[0][5]; +			lockfilename=malloc(strlen(pidarg)+sizeof(".lock")); +			if (!lockfilename) +			{ +				perror("malloc"); +				exit(1); +			} +			strcat(strcpy(lockfilename, pidarg), ".lock"); +		} +		else +		if (strncmp(argv[0],"-name=",6) == 0 && argv[0][6]) +			namearg=&argv[0][6]; +		else if (strncmp(argv[0],"-user=",6) == 0) +			userarg=argv[0]+6; +		else if (strncmp(argv[0],"-group=",7) == 0) +			grouparg=argv[0]+7; +		else if (strcmp(argv[0], "-droproot") == 0) +			droproot=1; +		else +		if (strncmp(argv[0],"-facility=",10) == 0) +		{ +		struct lognames *p = facilitynames; + +			while (p->name && strcmp(p->name, &argv[0][10])) +				p++; +			if (p->name == 0) +			{ +				fprintf(stderr, "Unknown facility name '%s'\n", +					&argv[0][10]); +				exit(1); +			} +			facility = p->value; +		} +		else +		if (strcmp(argv[0],"-start") == 0) +			daemon = 1; +		else +		if (strcmp(argv[0],"-stop") == 0) +			daemon = 2; +		else +		if (strcmp(argv[0],"-restart") == 0) +			daemon = 3; +		else +		if (strcmp(argv[0],"-respawn") == 0) +			respawn = 1; +		else +		{ +			fprintf(stderr, "Unknown option '%s'\n", argv[0]); +			exit(1); +		} +		argv++, argc--; +	} + +	if (daemon && !pidarg) +	{ +		fprintf(stderr, "-pid argument required\n"); +		exit(1); +	} + +	if (!daemon && pidarg) +		daemon = 1; /* -start implied */ + +	if (!namearg && daemon != 2 && daemon != 3) +	{ +		/* choose a default name based on the program we're running */ +		if (argc <= 0 || !argv[0] || !argv[0][0]) +		{ +			fprintf(stderr, "-name option required for standalone logger\n"); +			exit(1); +		} +		namearg = strrchr(argv[0],'/'); +		namearg = namearg ? namearg+1 : argv[0]; +	} + +	switch (daemon) +	{ +	case 1: /* start */ +		if (argc <= 0 || !argv[0] || !argv[0][0]) +		{ +			fprintf(stderr, "-start must be followed by a command to execute\n"); +			exit(1); +		} +		lockfd=ll_daemon_start(lockfilename); +		if (lockfd < 0) +		{ +			perror("ll_daemon_start"); +			exit(1); +		} +		startchild(argv, droproot ? userarg:NULL, +			   droproot ? grouparg:NULL); +		ll_daemon_started(pidarg, lockfd); +		break; +	case 2: /* stop */ +		exit(ll_daemon_stop(lockfilename, pidarg)); +	case 3: /* restart */ +		exit(ll_daemon_restart(lockfilename, pidarg)); +	default: /* run in foreground, with or without a child process */ + +		if (argc > 0) +			startchild(argv, droproot ? userarg:NULL, +				   droproot ? grouparg:NULL); +	} + +	setuidgid(userarg, grouparg); + +	while (1) +	{ +	int waitstat; +	pid_t p2; +	FILE *f = fdopen(0, "r"); + +		do_logger(namearg, facility, f); +		fclose(f); +		if (child_pid < 0) break; +		while ((p2=wait(&waitstat)) != child_pid && +			(p2 != -1 || errno != ECHILD)) +			; +		if (hup_restart) +			hup_restart = 0; +		else if (respawn) +			sleep (5); +		else +			break; +		startchild(argv, NULL, NULL); +	} +	exit(0); +} | 
