Index: usr.sbin/syslogd/Makefile
===================================================================
RCS file: /cvsroot/src/usr.sbin/syslogd/Makefile,v
retrieving revision 1.18
diff -u -p -r1.18 Makefile
--- usr.sbin/syslogd/Makefile	18 Sep 2002 03:54:37 -0000	1.18
+++ usr.sbin/syslogd/Makefile	3 May 2010 14:49:55 -0000
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.18 2002/09/18 03:54:37 lukem Exp $
+#	$NetBSD: Makefile,v 1.19 2005/01/10 02:58:59 lukem Exp $
 #	from: @(#)Makefile	8.1 (Berkeley) 6/6/93
 
 .include <bsd.own.mk>
@@ -10,10 +10,13 @@ DPADD+=${LIBUTIL}
 LDADD+=-lutil
 #make symlink to old socket location for transitional period
 SYMLINKS=	/var/run/log /dev/log
-CPPFLAGS+=-DINET6
 .PATH.c: ${NETBSDSRCDIR}/usr.bin/who
 CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP
 
+.if (${USE_INET6} != "no")
+CPPFLAGS+=-DINET6
+.endif
+
 CPPFLAGS+=-DLIBWRAP
 LDADD+=	-lwrap
 DPADD+=	${LIBWRAP}
Index: usr.sbin/syslogd/syslog.conf.5
===================================================================
RCS file: /cvsroot/src/usr.sbin/syslogd/syslog.conf.5,v
retrieving revision 1.11
diff -u -p -r1.11 syslog.conf.5
--- usr.sbin/syslogd/syslog.conf.5	7 Aug 2003 11:25:44 -0000	1.11
+++ usr.sbin/syslogd/syslog.conf.5	3 May 2010 14:49:56 -0000
@@ -1,4 +1,4 @@
-.\"	$NetBSD: syslog.conf.5,v 1.11 2003/08/07 11:25:44 agc Exp $
+.\"	$NetBSD: syslog.conf.5,v 1.13 2004/11/19 18:48:43 wiz Exp $
 .\"
 .\" Copyright (c) 1990, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"     from: @(#)syslog.conf.5	8.1 (Berkeley) 6/9/93
 .\"
-.Dd June 9, 1993
+.Dd November 18, 2004
 .Dt SYSLOG.CONF 5
 .Os
 .Sh NAME
@@ -43,7 +43,11 @@ The
 file is the configuration file for the
 .Xr syslogd 8
 program.
-It consists of lines with two fields: the
+It consists of blocks of lines separated by
+.Em program
+and
+.Em hostname
+specifications, with each line containing two fields: the
 .Em selector
 field which specifies the types of messages and priorities to which the
 line applies, and an
@@ -64,6 +68,8 @@ are encoded as a
 .Em facility ,
 a period
 .Pq Sq \&. ,
+an optional set of comparison flags
+.Pq Bo ! Bc Bq \*[Lt]=\*[Gt] ,
 and a
 .Em level ,
 with no intervening white-space.
@@ -88,6 +94,28 @@ and
 library routines.
 .Pp
 The
+.Em comparison flags
+may be used to specify exactly what levels are logged.
+If unspecified, the default comparison is
+.Sq \*[Gt]=
+.Pq greater than or equal to ,
+or, if the
+.Fl U
+option is passed to
+.Xr syslogd 8 ,
+.Sq =
+.Pq equal to .
+Comparison flags beginning with
+.So ! Sc
+will have their logical sense inverted.
+Thus,
+.Sq !=info
+means all levels except info and
+.Sq !notice
+has the same meaning as
+.Sq \*[Lt]notice .
+.Pp
+The
 .Em level
 describes the severity of the message, and is a keyword from the
 following ordered list (higher to lower): emerg, alert, crit, err,
@@ -99,6 +127,84 @@ values specified to the
 .Xr syslog 3
 library routine.
 .Pp
+Each block of lines is separated from the previous block by a
+.Em program
+or
+.Em hostname
+specification.
+A block will only log messages corresponding to the most recent
+.Em program
+and
+.Em hostname
+specifications given.
+Consider the case of a block that selects
+.Ql pppd
+as the
+.Em program ,
+directly followed by a block that selects messages from the
+.Em hostname
+.Ql dialhost .
+The second block will log only messages from the
+.Xr pppd 8
+program from the host
+.Sq dialhost .
+.Pp
+A
+.Em program
+specification of the form
+.Ql #!+prog1,prog2
+or
+.Ql !+prog1,prog2
+will cause subsequent blocks to be applied to messages logged by the
+specified programs.
+A
+.Em program
+specification of the form
+.Ql #!-prog1,prog2
+or
+.Ql !-prog1,prog2
+will cause subsequent blocks to be applied to messages logged by programs
+other than the ones specified.
+A
+.Em program
+specification of the form
+.Ql #!prog1,prog2
+or
+.Ql !prog1,prog2
+is equivalent to
+.Ql !+prog1,prog2 .
+Program selectors may also match kernel-generated messages.
+For example, a program specification of
+.Ql !+subsys
+will match kernel-generated messages of the form
+.Ql subsys: here is a message .
+The special specification
+.Ql !*
+will cause subsequent blocks to apply to all programs.
+.Pp
+A
+.Em hostname
+specification of the form
+.Ql #+host1,host2
+or
+.Ql +host1,host2
+will cause subsequent blocks to be applied to messages received from
+the specified hosts.
+A
+.Em hostname
+specification of the form
+.Ql #-host1,host2
+or
+.Ql -host1,host2
+will cause subsequent blocks to be applied to messages from hosts other
+than the ones specified.
+If the hostname is given as
+.Ql @ ,
+the local hostname will be used.
+The special specification
+.Ql +*
+will cause subsequent blocks to apply to all hosts.
+.Pp
 See
 .Xr syslog 3
 for a further descriptions of both the
@@ -106,12 +212,22 @@ for a further descriptions of both the
 and
 .Em level
 keywords and their significance.
+It is preferred that selections be made based on
+.Em facility
+rather than
+.Em program ,
+since the latter can vary in a networked environment.
+However, there are cases where a
+.Em facility
+may be too broadly defined.
 .Pp
 If a received message matches the specified
-.Em facility
-and is of the specified
+.Em facility ,
+and the specified
 .Em level
-.Em (or a higher level) ,
+comparison is true,
+and the first word in the message after the date matches the
+.Em program ,
 the action specified in the
 .Em action
 field will be taken.
@@ -165,11 +281,25 @@ The
 field of each line specifies the action to be taken when the
 .Em selector
 field selects a message.
-There are four forms:
+There are five forms:
 .Bl -bullet
 .It
 A pathname (beginning with a leading slash).
 Selected messages are appended to the file.
+.Pp
+To ensure that kernel messages are written to disk promptly,
+.Xr syslogd 8
+calls
+.Xr fsync 2
+after writing messages from the kernel.
+Other messages are not synced explcitly.
+You may disable syncing of files specified to receive kernel messages
+by prefixing the pathname with a minus sign
+.Ql - .
+Note that use of this option may cause the loss of log information in
+the event of a system crash immediately following the write attempt.
+However, using this option may prove to be useful if your system's
+kernel is logging many messages.
 .It
 A hostname (preceded by an at
 .Pq Sq @
@@ -184,6 +314,52 @@ if they are logged in.
 .It
 An asterisk.
 Selected messages are written to all logged-in users.
+.It
+A vertical bar
+.Pq Sq |
+followed by a command to which to pipe the selected messages.
+The command string is passed to
+.Pa /bin/sh
+for evaluation, so the usual shell metacharacters or input/output
+redirection can occur.
+(Note that redirecting
+.Xr stdio 3
+buffered output from the invoked command can cause additional delays,
+or even lost output data in case a logging subprocess exits with a
+signal.)
+The command itself runs with
+.Em stdout
+and
+.Em stderr
+redirected to
+.Pa /dev/null .
+Upon receipt of a
+.Dv SIGHUP ,
+.Xr syslogd 8
+will close the pipe to the process.
+If the process does not exit voluntarily, it will be sent a
+.Dv SIGTERM
+signal after a grace period of up to 60 seconds.
+.Pp
+The command will only be started once data arrives that should be
+piped to it.
+If the command exits, it will be restarted as necessary.
+.Pp
+If it is desired that the subprocess should receive exactly one line of
+input, this can be achieved by exiting after reading and processing the
+single line.
+A wrapper script can be used to achieve this effect, if necessary.
+Note that this method can be very resource-intensive if many log messages
+are being piped through the filter.
+.Pp
+Unless the command is a full pipeline, it may be useful to
+start the command with
+.Em exec
+so that the invoking shell process does not wait for the command to
+complete.
+Note that the command is started with the UID of the
+.Xr syslogd 8
+process, normally the superuser.
 .El
 .Pp
 Blank lines and lines whose first non-blank character is a hash
@@ -209,23 +385,50 @@ A configuration file might appear as fol
 # Don't log private authentication messages!
 *.info;mail.none;authpriv.none		/var/log/messages
 
+# Log daemon messages at debug level only
+daemon.=debug				/var/log/daemon.debug
+
 # The authpriv file has restricted access.
-authpriv.*						/var/log/secure
+authpriv.*				/var/log/secure
 
 # Log all the mail messages in one place.
-mail.*							/var/log/maillog
+mail.*					/var/log/maillog
 
 # Everybody gets emergency messages, plus log them on another
 # machine.
-*.emerg							*
-*.emerg							@arpa.berkeley.edu
+*.emerg					*
+*.emerg					@arpa.berkeley.edu
 
 # Root and Eric get alert and higher messages.
-*.alert							root,eric
+*.alert					root,eric
 
 # Save mail and news errors of level err and higher in a
 # special file.
-mail,news.err						/var/log/spoolerr
+mail,news.err				/var/log/spoolerr
+
+# Pipe all authentication messages to a filter.
+auth.*					|exec /usr/local/sbin/authfilter
+
+# Log kernel messages to a separate file without syncing each message.
+kern.*					-/var/log/kernlog
+
+# Save ftpd transactions along with mail and news.
+!ftpd
+*.*					/var/log/spoolerr
+
+# Send all error messages from a RAID array through a filter.
+!raid0
+kern.err				|exec /usr/local/sbin/raidfilter
+
+# Save pppd messages from dialhost to a separate file.
+!pppd
++dialhost
+*.*					/var/log/dialhost-pppd
+
+# Save non-local log messages from all programs to a separate file.
+!*
+-@
+*.*					/var/log/foreign
 .Ed
 .Sh SEE ALSO
 .Xr syslog 3 ,
Index: usr.sbin/syslogd/syslogd.8
===================================================================
RCS file: /cvsroot/src/usr.sbin/syslogd/syslogd.8,v
retrieving revision 1.33
diff -u -p -r1.33 syslogd.8
--- usr.sbin/syslogd/syslogd.8	17 Oct 2003 01:39:25 -0000	1.33
+++ usr.sbin/syslogd/syslogd.8	3 May 2010 14:49:56 -0000
@@ -1,4 +1,4 @@
-.\"	$NetBSD: syslogd.8,v 1.33 2003/10/17 01:39:25 lukem Exp $
+.\"	$NetBSD: syslogd.8,v 1.42 2006/09/15 20:32:59 christos Exp $
 .\"
 .\" Copyright (c) 1983, 1986, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"     from: @(#)syslogd.8	8.1 (Berkeley) 6/6/93
 .\"
-.Dd October 17, 2003
+.Dd September 15, 2006
 .Dt SYSLOGD 8
 .Os
 .Sh NAME
@@ -37,7 +37,10 @@
 .Nd log systems messages
 .Sh SYNOPSIS
 .Nm
-.Op Fl dnrs
+.Op Fl dnrSsTUv
+.Bk -words
+.Op Fl b Ar bind_address
+.Ek
 .Bk -words
 .Op Fl f Ar config_file
 .Ek
@@ -68,6 +71,10 @@ reads and logs messages to the system co
 machines and/or users as specified by its configuration file.
 The options are as follows:
 .Bl -tag -width 15n
+.It Fl b Ar bind_address
+Specify one specific IP address or hostname to bind to.
+If a hostname is specified, the IPv4 or IPv6 address
+which corresponds to it is used.
 .It Fl d
 Enable debugging to the standard output,
 and do not disassociate from the controlling terminal.
@@ -102,6 +109,8 @@ is used.
 Disable the compression of repeated instances of the same line
 into a single line of the form
 .Dq last message repeated N times .
+.It Fl S
+Sync kernel messages to disk immediately.
 .It Fl s
 Select
 .Dq secure
@@ -121,10 +130,32 @@ and similar attacks.
 to
 .Ar chroot_dir
 after the sockets and log files have been opened.
+.It Fl T
+Always use the local time and date for messages received from the
+network, instead of the timestamp field supplied in the message
+by the remote host.
+This is useful if some of the originating hosts can't keep time
+properly or are unable to generate a correct timestamp.
 .It Fl u Ar user
 Set UID to
 .Ar user
 after the sockets and log files have been opened.
+.It Fl U
+Unique priority logging.
+Only log messages at the priority specified by the selector in the
+configuration file.
+Without this option, messages at the specified priority or higher are
+logged.
+This option changes the default priority comparison from
+.Sq \*[Gt]=
+to
+.Sq = .
+.It Fl v
+Verbose logging.
+If specified once, the numeric facility and priority are logged with
+each locally-written message.
+If specified more than once, the names of the facility and priority are
+logged with each locally-written message.
 .El
 .Pp
 .Nm
@@ -170,6 +201,13 @@ The logged message includes the date, ti
 the log socket).
 Commonly, the program name and the process id is included.
 .Pp
+The date and time are taken from the received message.
+If the format of the timestamp field is incorrect, time obtained from
+the local host is used instead.
+This can be overridden by the
+.Fl T
+flag.
+.Pp
 Accesses from UDP socket can be filtered by libwrap configuration files, like
 .Pa /etc/hosts.deny .
 Specify
@@ -190,6 +228,17 @@ decimal number in angle braces, for exam
 This priority code should map into the priorities defined in the
 include file
 .Aq Pa sys/syslog.h .
+See RFC 3164 for detailed description of the message format.
+.Pp
+Messages from the local kernel that are not tagged with a priority code
+receive the default facility
+.Dv LOG_KERN
+and priority
+.Dv LOG_NOTICE .
+All other untagged messages receive the default facility
+.Dv LOG_USER
+and priority
+.Dv LOT_NOTICE .
 .Sh FILES
 .Bl -tag -width /var/run/syslogd.pid -compact
 .It Pa /etc/syslog.conf
@@ -210,6 +259,12 @@ The kernel log device.
 .Xr services 5 ,
 .Xr syslog.conf 5 ,
 .Xr newsyslog 8
+.Rs
+.%R RFC
+.%N 3164
+.%D August 2001
+.%T The BSD syslog Protocol
+.Re
 .Sh HISTORY
 The
 .Nm
Index: usr.sbin/syslogd/syslogd.c
===================================================================
RCS file: /cvsroot/src/usr.sbin/syslogd/syslogd.c,v
retrieving revision 1.65.4.1
diff -u -p -r1.65.4.1 syslogd.c
--- usr.sbin/syslogd/syslogd.c	30 Jan 2005 13:29:26 -0000	1.65.4.1
+++ usr.sbin/syslogd/syslogd.c	3 May 2010 14:49:57 -0000
@@ -1,4 +1,4 @@
-/*	$NetBSD: syslogd.c,v 1.65.4.1 2005/01/30 13:29:26 he Exp $	*/
+/*	$NetBSD: syslogd.c,v 1.84 2006/11/13 20:24:00 christos Exp $	*/
 
 /*
  * Copyright (c) 1983, 1988, 1993, 1994
@@ -39,7 +39,7 @@ __COPYRIGHT("@(#) Copyright (c) 1983, 19
 #if 0
 static char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";
 #else
-__RCSID("$NetBSD: syslogd.c,v 1.65.4.1 2005/01/30 13:29:26 he Exp $");
+__RCSID("$NetBSD: syslogd.c,v 1.84 2006/11/13 20:24:00 christos Exp $");
 #endif
 #endif /* not lint */
 
@@ -63,12 +63,16 @@ __RCSID("$NetBSD: syslogd.c,v 1.65.4.1 2
  * Author: Eric Allman
  * extensive changes by Ralph Campbell
  * more extensive changes by Eric Allman (again)
+ * Extension to log by program name as well as facility and priority
+ *   by Peter da Silva.
+ * -U and -v by Harlan Stenn.
+ * Priority comparison code by Harlan Stenn.
  */
 
 #define	MAXLINE		1024		/* maximum line length */
 #define	MAXSVLINE	120		/* maximum saved line length */
 #define DEFUPRI		(LOG_USER|LOG_NOTICE)
-#define DEFSPRI		(LOG_KERN|LOG_CRIT)
+#define DEFSPRI		(LOG_KERN|LOG_NOTICE)
 #define TIMERINTVL	30		/* interval for checking flush, mark */
 #define TTYMSGTIME	1		/* timeout passed to ttymsg */
 
@@ -78,16 +82,18 @@ __RCSID("$NetBSD: syslogd.c,v 1.65.4.1 2
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
+#include <sys/queue.h>
+#include <sys/event.h>
 
 #include <netinet/in.h>
 
+#include <assert.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
 #include <locale.h>
 #include <netdb.h>
-#include <poll.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdarg.h>
@@ -127,10 +133,13 @@ char	ctty[] = _PATH_CONSOLE;
 #define SYNC_FILE	0x002	/* do fsync on file after printing */
 #define ADDDATE		0x004	/* add a date to the message */
 #define MARK		0x008	/* this message is a mark */
+#define	ISKERNEL	0x010	/* kernel generated message */
 
 /*
  * This structure represents the files that will have log
  * copies printed.
+ * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY,
+ * or if f_type is F_PIPE and f_pid > 0.
  */
 
 struct filed {
@@ -138,25 +147,56 @@ struct filed {
 	short	f_type;			/* entry type, see below */
 	short	f_file;			/* file descriptor */
 	time_t	f_time;			/* time this was last written */
+	char	*f_host;		/* host from which to record */
 	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
+	u_char	f_pcmp[LOG_NFACILITIES+1];	/* compare priority */
+#define	PRI_LT	0x1
+#define	PRI_EQ	0x2
+#define	PRI_GT	0x4
+	char	*f_program;		/* program this applies to */
 	union {
 		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
 		struct {
-			char	f_hname[MAXHOSTNAMELEN+1];
+			char	f_hname[MAXHOSTNAMELEN];
 			struct	addrinfo *f_addr;
 		} f_forw;		/* forwarding address */
 		char	f_fname[MAXPATHLEN];
+		struct {
+			char	f_pname[MAXPATHLEN];
+			pid_t	f_pid;
+		} f_pipe;
 	} f_un;
 	char	f_prevline[MAXSVLINE];		/* last message logged */
 	char	f_lasttime[16];			/* time of last occurrence */
-	char	f_prevhost[MAXHOSTNAMELEN+1];	/* host from which recd. */
+	char	f_prevhost[MAXHOSTNAMELEN];	/* host from which recd. */
 	int	f_prevpri;			/* pri of f_prevline */
 	int	f_prevlen;			/* length of f_prevline */
 	int	f_prevcount;			/* repetition cnt of prevline */
 	int	f_repeatcount;			/* number of "repeated" msgs */
+	int	f_lasterror;			/* last error on writev() */
+	int	f_flags;			/* file-specific flags */
+#define	FFLAG_SYNC	0x01
 };
 
 /*
+ * Queue of about-to-be-dead processes we should watch out for.
+ */
+TAILQ_HEAD(, deadq_entry) deadq_head = TAILQ_HEAD_INITIALIZER(deadq_head);
+
+typedef struct deadq_entry {
+	pid_t				dq_pid;
+	int				dq_timeout;
+	TAILQ_ENTRY(deadq_entry)	dq_entries;
+} *dq_t;
+
+/*
+ * The timeout to apply to processes waiting on the dead queue.  Unit
+ * of measure is "mark intervals", i.e. 20 minutes by default.
+ * Processes on the dead queue will be terminated after that time.
+ */
+#define	DQ_TIMO_INIT	2
+
+/*
  * Intervals at which we flush out "message repeated" messages,
  * in seconds after previous message is logged.  After each flush,
  * we move to the next interval until we reach the largest.
@@ -176,10 +216,11 @@ int	repeatinterval[] = { 30, 120, 600 };
 #define F_FORW		4		/* remote machine */
 #define F_USERS		5		/* list of users */
 #define F_WALL		6		/* everyone logged on */
+#define	F_PIPE		7		/* pipe to program */
 
-char	*TypeNames[7] = {
+char	*TypeNames[8] = {
 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
-	"FORW",		"USERS",	"WALL"
+	"FORW",		"USERS",	"WALL",		"PIPE"
 };
 
 struct	filed *Files;
@@ -187,10 +228,13 @@ struct	filed consfile;
 
 int	Debug;			/* debug flag */
 int	daemonized = 0;		/* we are not daemonized yet */
-char	LocalHostName[MAXHOSTNAMELEN+1];	/* our hostname */
+char	LocalHostName[MAXHOSTNAMELEN];	/* our hostname */
+char	oldLocalHostName[MAXHOSTNAMELEN];/* previous hostname */
 char	*LocalDomain;		/* our local domain name */
+size_t	LocalDomainLen;		/* length of LocalDomain */
 int	*finet = NULL;		/* Internet datagram sockets */
-int	Initialized = 0;	/* set when we have initialized ourselves */
+int	Initialized;		/* set when we have initialized ourselves */
+int	ShuttingDown;		/* set when we die() */
 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
 int	MarkSeq = 0;		/* mark sequence number */
 int	SecureMode = 0;		/* listen only on unix domain socks */
@@ -198,39 +242,67 @@ int	UseNameService = 1;	/* make domain n
 int	NumForwards = 0;	/* number of forwarding actions in conf file */
 char	**LogPaths;		/* array of pathnames to read messages from */
 int	NoRepeat = 0;		/* disable "repeated"; log always */
-volatile sig_atomic_t gothup = 0; /* got SIGHUP */
+int	RemoteAddDate = 0;	/* always add date to messages from network */
+int	SyncKernel = 0;		/* write kernel messages synchronously */
+int	UniquePriority = 0;	/* only log specified priority */
+int	LogFacPri = 0;		/* put facility and priority in log messages: */
+				/* 0=no, 1=numeric, 2=names */
 
-void	cfline(char *, struct filed *);
+void	cfline(char *, struct filed *, char *, char *);
 char   *cvthname(struct sockaddr_storage *);
+void	deadq_enter(pid_t, const char *);
+int	deadq_remove(pid_t);
 int	decode(const char *, CODE *);
-void	die(int);
-void	domark(int);
+void	die(struct kevent *);	/* SIGTERM kevent dispatch routine */
+void	domark(struct kevent *);/* timer kevent dispatch routine */
 void	fprintlog(struct filed *, int, char *);
 int	getmsgbufsize(void);
-int*	socksetup(int);
-void	init(void);
+int*	socksetup(int, const char *);
+void	init(struct kevent *);	/* SIGHUP kevent dispatch routine */
 void	logerror(const char *, ...);
 void	logmsg(int, char *, char *, int);
-void	printline(char *, char *);
+void	log_deadchild(pid_t, int, const char *);
+int	matches_spec(const char *, const char *,
+		     char *(*)(const char *, const char *));
+void	printline(char *, char *, int);
 void	printsys(char *);
-void	sighup(int);
-void	reapchild(int);
+int	p_open(char *, pid_t *);
+void	trim_localdomain(char *);
+void	reapchild(struct kevent *); /* SIGCHLD kevent dispatch routine */
 void	usage(void);
-void	wallmsg(struct filed *, struct iovec *);
+void	wallmsg(struct filed *, struct iovec *, size_t);
 int	main(int, char *[]);
 void	logpath_add(char ***, int *, int *, char *);
 void	logpath_fileadd(char ***, int *, int *, char *);
 
+static int fkq;
+
+static struct kevent *allocevchange(void);
+static int wait_for_events(struct kevent *, size_t);
+
+static void dispatch_read_klog(struct kevent *);
+static void dispatch_read_finet(struct kevent *);
+static void dispatch_read_funix(struct kevent *);
+
+/*
+ * Global line buffer.  Since we only process one event at a time,
+ * a global one will do.
+ */
+static char *linebuf;
+static size_t linebufsize;
+static const char *bindhostname = NULL;
+
+#define	A_CNT(x)	(sizeof((x)) / sizeof((x)[0]))
+
 int
 main(int argc, char *argv[])
 {
-	int ch, *funix, i, j, fklog, len, linesize;
-	int *nfinetix, nfklogix, nfunixbaseix, nfds;
+	int ch, *funix, j, fklog;
 	int funixsize = 0, funixmaxsize = 0;
-	struct sockaddr_un sunx, fromunix;
-	struct sockaddr_storage frominet;
-	char *p, *line, **pp;
-	struct pollfd *readfds;
+	struct kevent events[16];
+	struct sockaddr_un sunx;
+	char **pp;
+	struct kevent *ev;
 	uid_t uid = 0;
 	gid_t gid = 0;
 	char *user = NULL;
@@ -243,8 +315,11 @@ main(int argc, char *argv[])
 
 	(void)setlocale(LC_ALL, "");
 
-	while ((ch = getopt(argc, argv, "dnsf:m:p:P:ru:g:t:")) != -1)
+	while ((ch = getopt(argc, argv, "b:dnsSf:m:p:P:ru:g:t:TUv")) != -1)
 		switch(ch) {
+		case 'b':
+			bindhostname = optarg;
+			break;
 		case 'd':		/* debug */
 			Debug++;
 			break;
@@ -276,16 +351,29 @@ main(int argc, char *argv[])
 		case 's':		/* no network listen mode */
 			SecureMode++;
 			break;
+		case 'S':
+			SyncKernel = 1;
+			break;
 		case 't':
 			root = optarg;
 			if (*root == '\0')
 				usage();
 			break;
+		case 'T':
+			RemoteAddDate = 1;
+			break;
 		case 'u':
 			user = optarg;
 			if (*user == '\0')
 				usage();
 			break;
+		case 'U':		/* only log specified priority */
+			UniquePriority = 1;
+			break;
+		case 'v':		/* log facility and priority */
+			if (LogFacPri < 2)
+				LogFacPri++;
+			break;
 		default:
 			usage();
 		}
@@ -305,7 +393,7 @@ main(int argc, char *argv[])
 			if (uid != l) {
 				errno = 0;
 				logerror("UID out of range");
-				die(0);
+				die(NULL);
 			}
 		} else {
 getuser:
@@ -314,7 +402,7 @@ getuser:
 			} else {
 				errno = 0;  
 				logerror("Cannot find user `%s'", user);
-				die (0);
+				die(NULL);
 			}
 		}
 	}
@@ -330,7 +418,7 @@ getuser:
 			if (gid != l) {
 				errno = 0;
 				logerror("GID out of range");
-				die(0);
+				die(NULL);
 			}
 		} else {
 getgroup:
@@ -339,40 +427,28 @@ getgroup:
 			} else {
 				errno = 0;
 				logerror("Cannot find group `%s'", group);
-				die(0);
+				die(NULL);
 			}
 		}
 	}
 
-	if (access (root, F_OK | R_OK)) {
+	if (access(root, F_OK | R_OK)) {
 		logerror("Cannot access `%s'", root);
-		die (0);
+		die(NULL);
 	}
 
 	consfile.f_type = F_CONSOLE;
 	(void)strlcpy(consfile.f_un.f_fname, ctty,
 	    sizeof(consfile.f_un.f_fname));
-	(void)gethostname(LocalHostName, sizeof(LocalHostName));
-	LocalHostName[sizeof(LocalHostName) - 1] = '\0';
-	if ((p = strchr(LocalHostName, '.')) != NULL) {
-		*p++ = '\0';
-		LocalDomain = p;
-	} else
-		LocalDomain = "";
-	linesize = getmsgbufsize();
-	if (linesize < MAXLINE)
-		linesize = MAXLINE;
-	linesize++;
-	line = malloc(linesize);
-	if (line == NULL) {
+	linebufsize = getmsgbufsize();
+	if (linebufsize < MAXLINE)
+		linebufsize = MAXLINE;
+	linebufsize++;
+	linebuf = malloc(linebufsize);
+	if (linebuf == NULL) {
 		logerror("Couldn't allocate line buffer");
-		die(0);
+		die(NULL);
 	}
-	(void)signal(SIGTERM, die);
-	(void)signal(SIGINT, Debug ? die : SIG_IGN);
-	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
-	(void)signal(SIGCHLD, reapchild);
-	(void)signal(SIGALRM, domark);
 
 #ifndef SUN_LEN
 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
@@ -383,7 +459,7 @@ getgroup:
 	funix = (int *)malloc(sizeof(int) * funixsize);
 	if (funix == NULL) {
 		logerror("Couldn't allocate funix descriptors");
-		die(0);
+		die(NULL);
 	}
 	for (j = 0, pp = LogPaths; *pp; pp++, j++) {
 		dprintf("Making unix dgram socket `%s'\n", *pp);
@@ -396,67 +472,34 @@ getgroup:
 		    (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
 		    chmod(*pp, 0666) < 0) {
 			logerror("Cannot create `%s'", *pp);
-			die(0);
+			die(NULL);
 		}
 		dprintf("Listening on unix dgram socket `%s'\n", *pp);
 	}
 
-	init();
-
 	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0) {
 		dprintf("Can't open `%s' (%d)\n", _PATH_KLOG, errno);
 	} else {
 		dprintf("Listening on kernel log `%s'\n", _PATH_KLOG);
 	}
 
-	dprintf("Off & running....\n");
-
-	(void)signal(SIGHUP, sighup);
-
-	/* setup pollfd set. */
-	readfds = (struct pollfd *)malloc(sizeof(struct pollfd) *
-			(funixsize + (finet ? *finet : 0) + 1));
-	if (readfds == NULL) {
-		logerror("Couldn't allocate pollfds");
-		die(0);
-	}
-	nfds = 0;
-	if (fklog >= 0) {
-		nfklogix = nfds++;
-		readfds[nfklogix].fd = fklog;
-		readfds[nfklogix].events = POLLIN | POLLPRI;
-	}
-	if (finet && !SecureMode) {
-		nfinetix = malloc(*finet * sizeof(*nfinetix));
-		for (j = 0; j < *finet; j++) {
-			nfinetix[j] = nfds++;
-			readfds[nfinetix[j]].fd = finet[j+1];
-			readfds[nfinetix[j]].events = POLLIN | POLLPRI;
-		}
-	}
-	nfunixbaseix = nfds;
-	for (j = 0, pp = LogPaths; *pp; pp++) {
-		readfds[nfds].fd = funix[j++];
-		readfds[nfds++].events = POLLIN | POLLPRI;
-	}
-
 	/* 
 	 * All files are open, we can drop privileges and chroot
 	 */
 	dprintf("Attempt to chroot to `%s'\n", root);  
 	if (chroot(root)) {
 		logerror("Failed to chroot to `%s'", root);
-		die(0);
+		die(NULL);
 	}
 	dprintf("Attempt to set GID/EGID to `%d'\n", gid);  
 	if (setgid(gid) || setegid(gid)) {
 		logerror("Failed to set gid to `%d'", gid);
-		die(0);
+		die(NULL);
 	}
 	dprintf("Attempt to set UID/EUID to `%d'\n", uid);  
 	if (setuid(uid) || seteuid(uid)) {
 		logerror("Failed to set uid to `%d'", uid);
-		die(0);
+		die(NULL);
 	}
 
 	/* 
@@ -473,90 +516,90 @@ getgroup:
 		pidfile(NULL);
 	}
 
-	/* set timer for mark facility after the fork of daemon() */
-	(void)alarm(TIMERINTVL);
+	/*
+	 * Create the global kernel event descriptor.
+	 *
+	 * NOTE: We MUST do this after daemon(), bacause the kqueue()
+	 * API dictates that kqueue descriptors are not inherited
+	 * across forks (lame!).
+	 */
+	if ((fkq = kqueue()) < 0) {
+		logerror("Cannot create event queue");
+		die(NULL);	/* XXX This error is lost! */
+	}
+
+	/*
+	 * We must read the configuration file for the first time
+	 * after the kqueue descriptor is created, because we install
+	 * events during this process.
+	 */
+	init(NULL);
+
+	/*
+	 * Always exit on SIGTERM.  Also exit on SIGINT and SIGQUIT
+	 * if we're debugging.
+	 */
+	(void)signal(SIGTERM, SIG_IGN);
+	(void)signal(SIGINT, SIG_IGN);
+	(void)signal(SIGQUIT, SIG_IGN);
+	ev = allocevchange();
+	EV_SET(ev, SIGTERM, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+	    (intptr_t) die);
+	if (Debug) {
+		ev = allocevchange();
+		EV_SET(ev, SIGINT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+		    (intptr_t) die);
+
+		ev = allocevchange();
+		EV_SET(ev, SIGQUIT, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+		    (intptr_t) die);
+	}
+
+	ev = allocevchange();
+	EV_SET(ev, SIGCHLD, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+	    (intptr_t) reapchild);
+
+	ev = allocevchange();
+	EV_SET(ev, 0, EVFILT_TIMER, EV_ADD | EV_ENABLE, 0,
+	    TIMERINTVL * 1000 /* seconds -> ms */, (intptr_t) domark);
+
+	(void)signal(SIGPIPE, SIG_IGN);	/* We'll catch EPIPE instead. */
+
+	/* Re-read configuration on SIGHUP. */
+	(void) signal(SIGHUP, SIG_IGN);
+	ev = allocevchange();
+	EV_SET(ev, SIGHUP, EVFILT_SIGNAL, EV_ADD | EV_ENABLE, 0, 0,
+	    (intptr_t) init);
+
+	if (fklog >= 0) {
+		ev = allocevchange();
+		EV_SET(ev, fklog, EVFILT_READ, EV_ADD | EV_ENABLE,
+		    0, 0, (intptr_t) dispatch_read_klog);
+	}
+	for (j = 0, pp = LogPaths; *pp; pp++, j++) {
+		ev = allocevchange();
+		EV_SET(ev, funix[j], EVFILT_READ, EV_ADD | EV_ENABLE,
+		    0, 0, (intptr_t) dispatch_read_funix);
+	}
+
+	dprintf("Off & running....\n");
 
 	for (;;) {
-		int rv;
+		void (*handler)(struct kevent *);
+		int i, rv;
 
-		rv = poll(readfds, nfds, INFTIM);
+		rv = wait_for_events(events, A_CNT(events));
 		if (rv == 0)
 			continue;
 		if (rv < 0) {
 			if (errno != EINTR)
-				logerror("poll() failed");
-			if (gothup) {
-				init();
-				gothup = 0;
-			}
+				logerror("kevent() failed");
 			continue;
 		}
-		dprintf("Got a message (%d)\n", rv);
-		if (fklog >= 0 &&
-		    (readfds[nfklogix].revents & (POLLIN | POLLPRI))) {
-			dprintf("Kernel log active\n");
-			i = read(fklog, line, linesize - 1);
-			if (i > 0) {
-				line[i] = '\0';
-				printsys(line);
-			} else if (i < 0 && errno != EINTR) {
-				logerror("klog failed");
-				fklog = -1;
-			}
-		}
-		for (j = 0, pp = LogPaths; *pp; pp++, j++) {
-			if ((readfds[nfunixbaseix + j].revents &
-			    (POLLIN | POLLPRI)) == 0)
-				continue;
-
-			dprintf("Unix socket (%s) active\n", *pp);
-			len = sizeof(fromunix);
-			i = recvfrom(funix[j], line, MAXLINE, 0,
-			    (struct sockaddr *)&fromunix, &len);
-			if (i > 0) {
-				line[i] = '\0';
-				printline(LocalHostName, line);
-			} else if (i < 0 && errno != EINTR) {
-				logerror("recvfrom() unix `%s'", *pp);
-			}
-		}
-		if (finet && !SecureMode) {
-			for (j = 0; j < *finet; j++) {
-		    		if (readfds[nfinetix[j]].revents &
-				    (POLLIN | POLLPRI)) {
-#ifdef LIBWRAP
-					struct request_info req;
-#endif
-					int reject = 0;
-
-					dprintf("inet socket active\n");
-
-#ifdef LIBWRAP
-					request_init(&req, RQ_DAEMON, "syslogd",
-					    RQ_FILE, finet[j + 1], NULL);
-					fromhost(&req);
-					reject = !hosts_access(&req);
-					if (reject)
-						dprintf("access denied\n");
-#endif
-
-					len = sizeof(frominet);
-					i = recvfrom(finet[j+1], line, MAXLINE,
-					    0, (struct sockaddr *)&frominet,
-					    &len);
-					if (i == 0 || (i < 0 && errno == EINTR))
-						continue;
-					else if (i < 0) {
-						logerror("recvfrom inet");
-						continue;
-					}
-
-					line[i] = '\0';
-					if (!reject)
-						printline(cvthname(&frominet),
-						    line);
-				}
-			}
+		dprintf("Got an event (%d)\n", rv);
+		for (i = 0; i < rv; i++) {
+			handler = (void *) events[i].udata;
+			(*handler)(&events[i]);
 		}
 	}
 }
@@ -566,14 +609,120 @@ usage(void)
 {
 
 	(void)fprintf(stderr,
-	    "usage: %s [-dnrs] [-f config_file] [-g group] [-m mark_interval]\n"
-	    "\t[-P file_list] [-p log_socket [-p log_socket2 ...]]\n"
-	    "\t[-t chroot_dir] [-u user]\n", getprogname());
+	    "usage: %s [-dnrSsTUv] [-b bind_address] [-f config_file] [-g group]\n"
+	    "\t[-m mark_interval] [-P file_list] [-p log_socket\n"
+	    "\t[-p log_socket2 ...]] [-t chroot_dir] [-u user]\n",
+	    getprogname());
 	exit(1);
 }
 
 /*
- * given a pointer to an array of char *'s, a pointer to it's current
+ * Dispatch routine for reading /dev/klog
+ */
+static void
+dispatch_read_klog(struct kevent *ev)
+{
+	ssize_t rv;
+	int fd = ev->ident;
+
+	dprintf("Kernel log active\n");
+
+	rv = read(fd, linebuf, linebufsize - 1);
+	if (rv > 0) {
+		linebuf[rv] = '\0';
+		printsys(linebuf);
+	} else if (rv < 0 && errno != EINTR) {
+		/*
+		 * /dev/klog has croaked.  Disable the event
+		 * so it won't bother us again.
+		 */
+		struct kevent *cev = allocevchange();
+		logerror("klog failed");
+		EV_SET(cev, fd, EVFILT_READ, EV_DISABLE,
+		    0, 0, (intptr_t) dispatch_read_klog);
+	}
+}
+
+/*
+ * Dispatch routine for reading Unix domain sockets.
+ */
+static void
+dispatch_read_funix(struct kevent *ev)
+{
+	struct sockaddr_un myname, fromunix;
+	ssize_t rv;
+	socklen_t sunlen;
+	int fd = ev->ident;
+
+	sunlen = sizeof(myname);
+	if (getsockname(fd, (struct sockaddr *)&myname, &sunlen) != 0) {
+		/*
+		 * This should never happen, so ensure that it doesn't
+		 * happen again.
+		 */
+		struct kevent *cev = allocevchange();
+		logerror("getsockname() unix failed");
+		EV_SET(cev, fd, EVFILT_READ, EV_DISABLE,
+		    0, 0, (intptr_t) dispatch_read_funix);
+		return;
+	}
+
+	dprintf("Unix socket (%s) active\n", myname.sun_path);
+
+	sunlen = sizeof(fromunix);
+	rv = recvfrom(fd, linebuf, MAXLINE, 0,
+	    (struct sockaddr *)&fromunix, &sunlen);
+	if (rv > 0) {
+		linebuf[rv] = '\0';
+		printline(LocalHostName, linebuf, 0);
+	} else if (rv < 0 && errno != EINTR) {
+		logerror("recvfrom() unix `%s'", myname.sun_path);
+	}
+}
+
+/*
+ * Dispatch routine for reading Internet sockets.
+ */
+static void
+dispatch_read_finet(struct kevent *ev)
+{
+#ifdef LIBWRAP
+	struct request_info req;
+#endif
+	struct sockaddr_storage frominet;
+	ssize_t rv;
+	socklen_t len;
+	int fd = ev->ident;
+	int reject = 0;
+
+	dprintf("inet socket active\n");
+
+#ifdef LIBWRAP
+	request_init(&req, RQ_DAEMON, "syslogd", RQ_FILE, fd, NULL);
+	fromhost(&req);
+	reject = !hosts_access(&req);
+	if (reject)
+		dprintf("access denied\n");
+#endif
+
+	len = sizeof(frominet);
+	rv = recvfrom(fd, linebuf, MAXLINE, 0,
+	    (struct sockaddr *)&frominet, &len);
+	if (rv == 0 || (rv < 0 && errno == EINTR))
+		return;
+	else if (rv < 0) {
+		logerror("recvfrom inet");
+		return;
+	}
+
+	linebuf[rv] = '\0';
+	if (!reject)
+		printline(cvthname(&frominet), linebuf,
+			  RemoteAddDate ? ADDDATE : 0);
+}
+
+/*
+ * given a pointer to an array of char *'s, a pointer to its current
  * size and current allocated max size, and a new char * to add, add
  * it, update everything as necessary, possibly allocating a new array
  */
@@ -593,14 +742,14 @@ logpath_add(char ***lp, int *szp, int *m
 		nlp = realloc(*lp, sizeof(char *) * (newmaxsz + 1));
 		if (nlp == NULL) {
 			logerror("Couldn't allocate line buffer");
-			die(0);
+			die(NULL);
 		}
 		*lp = nlp;
 		*maxszp = newmaxsz;
 	}
 	if (((*lp)[(*szp)++] = strdup(new)) == NULL) {
 		logerror("Couldn't allocate logpath");
-		die(0);
+		die(NULL);
 	}
 	(*lp)[(*szp)] = NULL;		/* always keep it NULL terminated */
 }
@@ -616,7 +765,7 @@ logpath_fileadd(char ***lp, int *szp, in
 	fp = fopen(file, "r");
 	if (fp == NULL) {
 		logerror("Could not open socket file list `%s'", file);
-		die(0);
+		die(NULL);
 	}
 
 	while ((line = fgetln(fp, &len))) {
@@ -631,26 +780,32 @@ logpath_fileadd(char ***lp, int *szp, in
  * on the appropriate log files.
  */
 void
-printline(char *hname, char *msg)
+printline(char *hname, char *msg, int flags)
 {
 	int c, pri;
 	char *p, *q, line[MAXLINE + 1];
+	long n;
 
 	/* test for special codes */
 	pri = DEFUPRI;
 	p = msg;
 	if (*p == '<') {
-		pri = 0;
-		while (isdigit(*++p))
-			pri = 10 * pri + (*p - '0');
-		if (*p == '>')
-			++p;
+		errno = 0;
+		n = strtol(p + 1, &q, 10);
+		if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+			p = q + 1;
+			pri = (int)n;
+		}
 	}
 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
 		pri = DEFUPRI;
 
-	/* don't allow users to log kernel messages */
-	if (LOG_FAC(pri) == LOG_KERN)
+	/*
+	 * Don't allow users to log kernel messages.
+	 * NOTE: Since LOG_KERN == 0, this will also match
+	 *	 messages with no facility specified.
+	 */
+	if ((pri & LOG_FACMASK) == LOG_KERN)
 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
 
 	q = line;
@@ -672,7 +827,7 @@ printline(char *hname, char *msg)
 	}
 	*q = '\0';
 
-	logmsg(pri, line, hname, 0);
+	logmsg(pri, line, hname, flags);
 }
 
 /*
@@ -681,39 +836,75 @@ printline(char *hname, char *msg)
 void
 printsys(char *msg)
 {
-	int c, pri, flags;
-	char *lp, *p, *q, line[MAXLINE + 1];
+	int n, pri, flags, is_printf;
+	char *p, *q;
 
-	(void)strlcpy(line, _PATH_UNIX, sizeof(line));
-	(void)strlcat(line, ": ", sizeof(line));
-	lp = line + strlen(line);
 	for (p = msg; *p != '\0'; ) {
-		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
+		flags = ISKERNEL | ADDDATE;
+		if (SyncKernel)
+			flags |= SYNC_FILE;
 		pri = DEFSPRI;
+		is_printf = 1;
 		if (*p == '<') {
-			pri = 0;
-			while (isdigit(*++p))
-				pri = 10 * pri + (*p - '0');
-			if (*p == '>')
-				++p;
-		} else {
+			errno = 0;
+			n = (int)strtol(p + 1, &q, 10);
+			if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+				p = q + 1;
+				pri = n;
+				is_printf = 0;
+			}
+		}
+		if (is_printf) {
 			/* kernel printf's come out on console */
 			flags |= IGN_CONS;
 		}
 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
 			pri = DEFSPRI;
-		q = lp;
-		while (*p != '\0' && (c = *p++) != '\n' &&
-		    q < &line[MAXLINE])
-			*q++ = c;
-		*q = '\0';
-		logmsg(pri, line, LocalHostName, flags);
+		for (q = p; *q != '\0' && *q != '\n'; q++)
+			/* look for end of line */;
+		if (*q != '\0')
+			*q++ = '\0';
+		logmsg(pri, p, LocalHostName, flags);
+		p = q;
 	}
 }
 
 time_t	now;
 
 /*
+ * Check to see if `name' matches the provided specification, using the
+ * specified strstr function.
+ */
+int
+matches_spec(const char *name, const char *spec,
+    char *(*check)(const char *, const char *))
+{
+	const char *s;
+	const char *cursor;
+	char prev, next;
+	size_t len;
+
+	if (name[0] == '\0')
+		return (0);
+
+	if (strchr(name, ',')) /* sanity */
+		return (0);
+
+	len = strlen(name);
+	cursor = spec;
+	while ((s = (*check)(cursor, name)) != NULL) {
+		prev = s == spec ? ',' : *(s - 1);
+		cursor = s + len;
+		next = *cursor;
+
+		if (prev == ',' && (next == '\0' || next == ','))
+			return (1);
+	}
+
+	return (0);
+}
+
+/*
  * Log a message to the appropriate log files, users, etc. based on
  * the priority.
  */
@@ -721,8 +912,10 @@ void
 logmsg(int pri, char *msg, char *from, int flags)
 {
 	struct filed *f;
-	int fac, msglen, omask, prilev;
+	int fac, msglen, omask, prilev, i;
 	char *timestamp;
+	char prog[NAME_MAX + 1];
+	char buf[MAXLINE + 1];
 
 	dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
 	    pri, flags, from, msg);
@@ -746,6 +939,12 @@ logmsg(int pri, char *msg, char *from, i
 		msglen -= 16;
 	}
 
+	/* skip leading whitespace */
+	while (isspace((unsigned char)*msg)) {
+		msg++;
+		msglen--;
+	}
+
 	/* extract facility and priority level */
 	if (flags & MARK)
 		fac = LOG_NFACILITIES;
@@ -753,12 +952,30 @@ logmsg(int pri, char *msg, char *from, i
 		fac = LOG_FAC(pri);
 	prilev = LOG_PRI(pri);
 
+	/* extract program name */
+	for (i = 0; i < NAME_MAX; i++) {
+		if (!isprint((unsigned char)msg[i]) ||
+		    msg[i] == ':' || msg[i] == '[')
+			break;
+		prog[i] = msg[i];
+	}
+	prog[i] = '\0';
+
+	/* add kernel prefix for kernel messages */
+	if (flags & ISKERNEL) {
+		snprintf(buf, sizeof(buf), "%s: %s",
+		    _PATH_UNIX, msg);
+		msg = buf;
+		msglen = strlen(buf);
+	}
+
 	/* log the message to the particular outputs */
 	if (!Initialized) {
 		f = &consfile;
 		f->f_file = open(ctty, O_WRONLY, 0);
 
 		if (f->f_file >= 0) {
+			(void)strncpy(f->f_lasttime, timestamp, 15);
 			fprintlog(f, flags, msg);
 			(void)close(f->f_file);
 		}
@@ -767,10 +984,50 @@ logmsg(int pri, char *msg, char *from, i
 	}
 	for (f = Files; f; f = f->f_next) {
 		/* skip messages that are incorrect priority */
-		if (f->f_pmask[fac] < prilev ||
-		    f->f_pmask[fac] == INTERNAL_NOPRI)
+		if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
+		     ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
+		     ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
+		     )
+		    || f->f_pmask[fac] == INTERNAL_NOPRI)
 			continue;
 
+		/* skip messages with the incorrect host name */
+		if (f->f_host != NULL) {
+			switch (f->f_host[0]) {
+			case '+':
+				if (! matches_spec(from, f->f_host + 1,
+						   strcasestr))
+					continue;
+				break;
+			case '-':
+				if (matches_spec(from, f->f_host + 1,
+						 strcasestr))
+					continue;
+				break;
+			}
+		}
+
+		/* skip messages with the incorrect program name */
+		if (f->f_program != NULL) {
+			switch (f->f_program[0]) {
+			case '+':
+				if (! matches_spec(prog, f->f_program + 1,
+						   strstr))
+					continue;
+				break;
+			case '-':
+				if (matches_spec(prog, f->f_program + 1,
+						 strstr))
+					continue;
+				break;
+			default:
+				if (! matches_spec(prog, f->f_program,
+						   strstr))
+					continue;
+				break;
+			}
+		}
+
 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
 			continue;
 
@@ -784,7 +1041,7 @@ logmsg(int pri, char *msg, char *from, i
 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
 		    !NoRepeat &&
 		    !strcmp(msg, f->f_prevline) &&
-		    !strcmp(from, f->f_prevhost)) {
+		    !strcasecmp(from, f->f_prevhost)) {
 			(void)strncpy(f->f_lasttime, timestamp, 15);
 			f->f_prevcount++;
 			dprintf("Msg repeated %d times, %ld sec of %d\n",
@@ -827,11 +1084,12 @@ logmsg(int pri, char *msg, char *from, i
 void
 fprintlog(struct filed *f, int flags, char *msg)
 {
-	struct iovec iov[6];
+	struct iovec iov[10];
 	struct iovec *v;
 	struct addrinfo *r;
-	int j, l, lsent;
+	int j, l, lsent, fail, retry;
 	char line[MAXLINE + 1], repbuf[80], greetings[200];
+#define ADDEV() assert(++v - iov < A_CNT(iov))
 
 	v = iov;
 	if (f->f_type == F_WALL) {
@@ -839,24 +1097,65 @@ fprintlog(struct filed *f, int flags, ch
 		v->iov_len = snprintf(greetings, sizeof greetings,
 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
 		    f->f_prevhost, ctime(&now));
-		v++;
+		ADDEV();
 		v->iov_base = "";
 		v->iov_len = 0;
-		v++;
+		ADDEV();
 	} else {
 		v->iov_base = f->f_lasttime;
 		v->iov_len = 15;
-		v++;
+		ADDEV();
 		v->iov_base = " ";
 		v->iov_len = 1;
-		v++;
+		ADDEV();
 	}
+
+	if (LogFacPri) {
+		static char fp_buf[30];
+		const char *f_s = NULL, *p_s = NULL;
+		int fac = f->f_prevpri & LOG_FACMASK;
+		int pri = LOG_PRI(f->f_prevpri);
+		char f_n[5], p_n[5];
+
+		if (LogFacPri > 1) {
+			CODE *c;
+
+			for (c = facilitynames; c->c_name != NULL; c++) {
+				if (c->c_val == fac) {
+					f_s = c->c_name;
+					break;
+				}
+			}
+			for (c = prioritynames; c->c_name != NULL; c++) {
+				if (c->c_val == pri) {
+					p_s = c->c_name;
+					break;
+				}
+			}
+		}
+		if (f_s == NULL) {
+			snprintf(f_n, sizeof(f_n), "%d", LOG_FAC(fac));
+			f_s = f_n;
+		}
+		if (p_s == NULL) {
+			snprintf(p_n, sizeof(p_n), "%d", pri);
+			p_s = p_n;
+		}
+		snprintf(fp_buf, sizeof(fp_buf), "<%s.%s>", f_s, p_s);
+		v->iov_base = fp_buf;
+		v->iov_len = strlen(fp_buf);
+	} else {
+		v->iov_base = "";
+		v->iov_len = 0;
+	}
+	ADDEV();
+
 	v->iov_base = f->f_prevhost;
 	v->iov_len = strlen(v->iov_base);
-	v++;
+	ADDEV();
 	v->iov_base = " ";
 	v->iov_len = 1;
-	v++;
+	ADDEV();
 
 	if (msg) {
 		v->iov_base = msg;
@@ -869,7 +1168,7 @@ fprintlog(struct filed *f, int flags, ch
 		v->iov_base = f->f_prevline;
 		v->iov_len = f->f_prevlen;
 	}
-	v++;
+	ADDEV();
 
 	dprintf("Logging to %s", TypeNames[f->f_type]);
 	f->f_time = now;
@@ -885,20 +1184,23 @@ fprintlog(struct filed *f, int flags, ch
 			 * check for local vs remote messages
 			 * (from FreeBSD PR#bin/7055)
 			 */
-		if (strcmp(f->f_prevhost, LocalHostName)) {
+		if (strcasecmp(f->f_prevhost, LocalHostName)) {
 			l = snprintf(line, sizeof(line) - 1,
 				     "<%d>%.15s [%s]: %s",
 				     f->f_prevpri, (char *) iov[0].iov_base,
-				     f->f_prevhost, (char *) iov[4].iov_base);
+				     f->f_prevhost, (char *) iov[5].iov_base);
 		} else {
 			l = snprintf(line, sizeof(line) - 1, "<%d>%.15s %s",
 				     f->f_prevpri, (char *) iov[0].iov_base,
-				     (char *) iov[4].iov_base);
+				     (char *) iov[5].iov_base);
 		}
 		if (l > MAXLINE)
 			l = MAXLINE;
 		if (finet) {
+			lsent = -1;
+			fail = 0;
 			for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) {
+				retry = 0;
 				for (j = 0; j < *finet; j++) {
 #if 0 
 					/*
@@ -908,19 +1210,97 @@ fprintlog(struct filed *f, int flags, ch
 					if (r->ai_family ==
 					    address_family_of(finet[j+1])) 
 #endif
+sendagain:
 					lsent = sendto(finet[j+1], line, l, 0,
 					    r->ai_addr, r->ai_addrlen);
-					if (lsent == l) 
+					if (lsent == -1) {
+						switch (errno) {
+						case ENOBUFS:
+							/* wait/retry/drop */
+							if (++retry < 5) {
+								usleep(1000);
+								goto sendagain;
+							}
+							break;
+						case EHOSTDOWN:
+						case EHOSTUNREACH:
+						case ENETDOWN:
+							/* drop */
+							break;
+						default:
+							/* busted */
+							fail++;
+							break;
+						}
+					} else if (lsent == l) 
 						break;
 				}
 			}
-			if (lsent != l) {
+			if (lsent != l && fail) {
 				f->f_type = F_UNUSED;
 				logerror("sendto() failed");
 			}
 		}
 		break;
 
+	case F_PIPE:
+		dprintf(" %s\n", f->f_un.f_pipe.f_pname);
+		v->iov_base = "\n";
+		v->iov_len = 1;
+		ADDEV();
+		if (f->f_un.f_pipe.f_pid == 0) {
+			if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
+						&f->f_un.f_pipe.f_pid)) < 0) {
+				f->f_type = F_UNUSED;
+				logerror(f->f_un.f_pipe.f_pname);
+				break;
+			}
+		}
+		if (writev(f->f_file, iov, v - iov) < 0) {
+			int e = errno;
+			if (f->f_un.f_pipe.f_pid > 0) {
+				(void) close(f->f_file);
+				deadq_enter(f->f_un.f_pipe.f_pid,
+					    f->f_un.f_pipe.f_pname);
+			}
+			f->f_un.f_pipe.f_pid = 0;
+			/*
+			 * If the error was EPIPE, then what is likely
+			 * has happened is we have a command that is
+			 * designed to take a single message line and
+			 * then exit, but we tried to feed it another
+			 * one before we reaped the child and thus
+			 * reset our state.
+			 *
+			 * Well, now we've reset our state, so try opening
+			 * the pipe and sending the message again if EPIPE
+			 * was the error.
+			 */
+			if (e == EPIPE) {
+				if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
+				     &f->f_un.f_pipe.f_pid)) < 0) {
+					f->f_type = F_UNUSED;
+					logerror(f->f_un.f_pipe.f_pname);
+					break;
+				}
+				if (writev(f->f_file, iov, v - iov) < 0) {
+					e = errno;
+					if (f->f_un.f_pipe.f_pid > 0) {
+					    (void) close(f->f_file);
+					    deadq_enter(f->f_un.f_pipe.f_pid,
+							f->f_un.f_pipe.f_pname);
+					}
+					f->f_un.f_pipe.f_pid = 0;
+				} else
+					e = 0;
+			}
+			if (e != 0) {
+				errno = e;
+				logerror(f->f_un.f_pipe.f_pname);
+			}
+		}
+		break;
+
 	case F_CONSOLE:
 		if (flags & IGN_CONS) {
 			dprintf(" (ignored)\n");
@@ -938,9 +1318,17 @@ fprintlog(struct filed *f, int flags, ch
 			v->iov_base = "\n";
 			v->iov_len = 1;
 		}
+		ADDEV();
 	again:
-		if (writev(f->f_file, iov, 6) < 0) {
+		if (writev(f->f_file, iov, v - iov) < 0) {
 			int e = errno;
+			if (f->f_type == F_FILE && e == ENOSPC) {
+				int lasterror = f->f_lasterror;
+				f->f_lasterror = e;
+				if (lasterror != e)
+					logerror(f->f_un.f_fname);
+				break;
+			}
 			(void)close(f->f_file);
 			/*
 			 * Check for errors on TTY's due to loss of tty
@@ -956,10 +1344,14 @@ fprintlog(struct filed *f, int flags, ch
 			} else {
 				f->f_type = F_UNUSED;
 				errno = e;
+				f->f_lasterror = e;
 				logerror(f->f_un.f_fname);
 			}
-		} else if (flags & SYNC_FILE)
-			(void)fsync(f->f_file);
+		} else {
+			f->f_lasterror = 0;
+			if ((flags & SYNC_FILE) && (f->f_flags & FFLAG_SYNC))
+				(void)fsync(f->f_file);
+		}
 		break;
 
 	case F_USERS:
@@ -967,7 +1359,8 @@ fprintlog(struct filed *f, int flags, ch
 		dprintf("\n");
 		v->iov_base = "\r\n";
 		v->iov_len = 2;
-		wallmsg(f, iov);
+		ADDEV();
+		wallmsg(f, iov, v - iov);
 		break;
 	}
 	f->f_prevcount = 0;
@@ -980,7 +1373,7 @@ fprintlog(struct filed *f, int flags, ch
  *	world, or a list of approved users.
  */
 void
-wallmsg(struct filed *f, struct iovec *iov)
+wallmsg(struct filed *f, struct iovec *iov, size_t iovcnt)
 {
 	static int reenter;			/* avoid calling ourselves */
 	int i;
@@ -999,7 +1392,7 @@ wallmsg(struct filed *f, struct iovec *i
 	/* NOSTRICT */
 	for (; ep; ep = ep->next) {
 		if (f->f_type == F_WALL) {
-			if ((p = ttymsg(iov, 6, ep->line, TTYMSGTIME))
+			if ((p = ttymsg(iov, iovcnt, ep->line, TTYMSGTIME))
 			    != NULL) {
 				errno = 0;	/* already in msg */
 				logerror(p);
@@ -1011,8 +1404,8 @@ wallmsg(struct filed *f, struct iovec *i
 			if (!f->f_un.f_uname[i][0])
 				break;
 			if (strcmp(f->f_un.f_uname[i], ep->name) == 0) {
-				if ((p = ttymsg(iov, 6, ep->line, TTYMSGTIME))
-				    != NULL) {
+				if ((p = ttymsg(iov, iovcnt, ep->line,
+				    TTYMSGTIME)) != NULL) {
 					errno = 0;	/* already in msg */
 					logerror(p);
 				}
@@ -1024,19 +1417,36 @@ wallmsg(struct filed *f, struct iovec *i
 }
 
 void
-sighup(int signo)
+reapchild(struct kevent *ev)
 {
+	int status;
+	pid_t pid;
+	struct filed *f;
 
-	gothup = 1;
-}
+	while ((pid = wait3(&status, WNOHANG, NULL)) > 0) {
+		if (!Initialized || ShuttingDown) {
+			/*
+			 * Be silent while we are initializing or
+			 * shutting down.
+			 */
+			continue;
+		}
 
-void
-reapchild(int signo)
-{
-	union wait status;
+		if (deadq_remove(pid))
+			continue;
 
-	while (wait3((int *)&status, WNOHANG, (struct rusage *)NULL) > 0)
-		;
+		/* Now, look in the list of active processes. */
+		for (f = Files; f != NULL; f = f->f_next) {
+			if (f->f_type == F_PIPE &&
+			    f->f_un.f_pipe.f_pid == pid) {
+				(void) close(f->f_file);
+				f->f_un.f_pipe.f_pid = 0;
+				log_deadchild(pid, status,
+					      f->f_un.f_pipe.f_pname);
+				break;
+			}
+		}
+	}
 }
 
 /*
@@ -1046,7 +1456,6 @@ char *
 cvthname(struct sockaddr_storage *f)
 {
 	int error;
-	char *p;
 	const int niflag = NI_DGRAM;
 	static char host[NI_MAXHOST], ip[NI_MAXHOST];
 
@@ -1069,15 +1478,37 @@ cvthname(struct sockaddr_storage *f)
 		dprintf("Host name for your address (%s) unknown\n", ip);
 		return (ip);
 	}
-	if ((p = strchr(host, '.')) && strcmp(p + 1, LocalDomain) == 0)
-		*p = '\0';
+
+	trim_localdomain(host);
+
 	return (host);
 }
 
 void
-domark(int signo)
+trim_localdomain(char *host)
+{
+	size_t hl;
+
+	hl = strlen(host);
+	if (hl > 0 && host[hl - 1] == '.')
+		host[--hl] = '\0';
+
+	if (hl > LocalDomainLen && host[hl - LocalDomainLen - 1] == '.' &&
+	    strcasecmp(&host[hl - LocalDomainLen], LocalDomain) == 0)
+		host[hl - LocalDomainLen - 1] = '\0';
+}
+
+void
+domark(struct kevent *ev)
 {
 	struct filed *f;
+	dq_t q, nextq;
+
+	/*
+	 * XXX Should we bother to adjust for the # of times the timer
+	 * has expired (i.e. in case we miss one?).  This information is
+	 * returned to us in ev->data.
+	 */
 
 	now = time((time_t *)NULL);
 	MarkSeq += TIMERINTVL;
@@ -1095,7 +1526,36 @@ domark(int signo)
 			BACKOFF(f);
 		}
 	}
-	(void)alarm(TIMERINTVL);
+
+	/* Walk the dead queue, and see if we should signal somebody. */
+	for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = nextq) {
+		nextq = TAILQ_NEXT(q, dq_entries);
+		switch (q->dq_timeout) {
+		case 0:
+			/* Already signalled once, try harder now. */
+			if (kill(q->dq_pid, SIGKILL) != 0)
+				(void) deadq_remove(q->dq_pid);
+			break;
+
+		case 1:
+			/*
+			 * Timed out on the dead queue, send terminate
+			 * signal.  Note that we leave the removal from
+			 * the dead queue to reapchild(), which will
+			 * also log the event (unless the process
+			 * didn't even really exist, in case we simply
+			 * drop it from the dead queue).
+			 */
+			if (kill(q->dq_pid, SIGTERM) != 0) {
+				(void) deadq_remove(q->dq_pid);
+				break;
+			}
+			/* FALLTHROUGH */
+
+		default:
+			q->dq_timeout--;
+		}
+	}
 }
 
 /*
@@ -1104,10 +1564,16 @@ domark(int signo)
 void
 logerror(const char *fmt, ...)
 {
+	static int logerror_running;
 	va_list ap;
 	char tmpbuf[BUFSIZ];
 	char buf[BUFSIZ];
 
+	/* If there's an error while trying to log an error, give up. */
+	if (logerror_running)
+		return;
+	logerror_running = 1;
+
 	va_start(ap, fmt);
 
 	(void)vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, ap);
@@ -1127,23 +1593,28 @@ logerror(const char *fmt, ...)
 	if (!daemonized && !Debug)
 		printf("%s\n", buf);
 
-	return;
+	logerror_running = 0;
 }
 
 void
-die(int signo)
+die(struct kevent *ev)
 {
 	struct filed *f;
 	char **p;
 
+	ShuttingDown = 1;	/* Don't log SIGCHLDs. */
 	for (f = Files; f != NULL; f = f->f_next) {
 		/* flush any pending output */
 		if (f->f_prevcount)
 			fprintlog(f, 0, (char *)NULL);
+		if (f->f_type == F_PIPE && f->f_un.f_pipe.f_pid > 0) {
+			(void) close(f->f_file);
+			f->f_un.f_pipe.f_pid = 0;
+		}
 	}
 	errno = 0;
-	if (signo)
-		logerror("Exiting on signal %d", signo);
+	if (ev != NULL)
+		logerror("Exiting on signal %d", (int) ev->ident);
 	else
 		logerror("Fatal error, exiting");
 	for (p = LogPaths; p && *p; p++)
@@ -1155,16 +1626,29 @@ die(int signo)
  *  INIT -- Initialize syslogd from configuration table
  */
 void
-init(void)
+init(struct kevent *ev)
 {
-	int i;
+	size_t i;
 	FILE *cf;
 	struct filed *f, *next, **nextp;
 	char *p;
 	char cline[LINE_MAX];
+	char prog[NAME_MAX + 1];
+	char host[MAXHOSTNAMELEN];
+	char hostMsg[2*MAXHOSTNAMELEN + 40];
 
 	dprintf("init\n");
 
+	(void)strlcpy(oldLocalHostName, LocalHostName,
+		      sizeof(oldLocalHostName));
+	(void)gethostname(LocalHostName, sizeof(LocalHostName));
+	if ((p = strchr(LocalHostName, '.')) != NULL) {
+		*p++ = '\0';
+		LocalDomain = p;
+	} else
+		LocalDomain = "";
+	LocalDomainLen = strlen(LocalDomain);
+
 	/*
 	 *  Close all open log files.
 	 */
@@ -1180,12 +1664,24 @@ init(void)
 		case F_CONSOLE:
 			(void)close(f->f_file);
 			break;
+		case F_PIPE:
+			if (f->f_un.f_pipe.f_pid > 0) {
+				(void)close(f->f_file);
+				deadq_enter(f->f_un.f_pipe.f_pid,
+					    f->f_un.f_pipe.f_pname);
+			}
+			f->f_un.f_pipe.f_pid = 0;
+			break;
 		case F_FORW:
 			if (f->f_un.f_forw.f_addr)
 				freeaddrinfo(f->f_un.f_forw.f_addr);
 			break;
 		}
 		next = f->f_next;
+		if (f->f_program != NULL)
+			free(f->f_program);
+		if (f->f_host != NULL)
+			free(f->f_host);
 		free((char *)f);
 	}
 	Files = NULL;
@@ -1199,7 +1695,7 @@ init(void)
 		for (i = 0; i < *finet; i++) {
 			if (close(finet[i+1]) < 0) {
 				logerror("close() failed");
-				die(0);
+				die(NULL);
 			}
 		}
 	}
@@ -1214,9 +1710,9 @@ init(void)
 	if ((cf = fopen(ConfFile, "r")) == NULL) {
 		dprintf("Cannot open `%s'\n", ConfFile);
 		*nextp = (struct filed *)calloc(1, sizeof(*f));
-		cfline("*.ERR\t/dev/console", *nextp);
+		cfline("*.ERR\t/dev/console", *nextp, "*", "*");
 		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
-		cfline("*.PANIC\t*", (*nextp)->f_next);
+		cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
 		Initialized = 1;
 		return;
 	}
@@ -1225,22 +1721,71 @@ init(void)
 	 *  Foreach line in the conf table, open that file.
 	 */
 	f = NULL;
+	strcpy(prog, "*");
+	strcpy(host, "*");
 	while (fgets(cline, sizeof(cline), cf) != NULL) {
 		/*
 		 * check for end-of-section, comments, strip off trailing
-		 * spaces and newline character.
+		 * spaces and newline character.  #!prog is treated specially:
+		 * following lines apply only to that program.
 		 */
-		for (p = cline; isspace(*p); ++p)
+		for (p = cline; isspace((unsigned char)*p); ++p)
 			continue;
-		if (*p == '\0' || *p == '#')
+		if (*p == '\0')
 			continue;
-		for (p = strchr(cline, '\0'); isspace(*--p);)
+		if (*p == '#') {
+			p++;
+			if (*p != '!' && *p != '+' && *p != '-')
+				continue;
+		}
+		if (*p == '+' || *p == '-') {
+			host[0] = *p++;
+			while (isspace((unsigned char)*p))
+				p++;
+			if (*p == '\0' || *p == '*') {
+				strcpy(host, "*");
+				continue;
+			}
+			for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
+				if (*p == '@') {
+					(void)strncpy(&host[i], LocalHostName,
+					    sizeof(host) - 1 - i);
+					host[sizeof(host) - 1] = '\0';
+					i = strlen(host) - 1;
+					p++;
+					continue;
+				}
+				if (!isalnum((unsigned char)*p) &&
+				    *p != '.' && *p != '-' && *p != ',')
+					break;
+				host[i] = *p++;
+			}
+			host[i] = '\0';
+			continue;
+		}
+		if (*p == '!') {
+			p++;
+			while (isspace((unsigned char)*p))
+				p++;
+			if (*p == '\0' || *p == '*') {
+				strcpy(prog, "*");
+				continue;
+			}
+			for (i = 0; i < NAME_MAX; i++) {
+				if (!isprint((unsigned char)p[i]))
+					break;
+				prog[i] = p[i];
+			}
+			prog[i] = '\0';
+			continue;
+		}
+		for (p = strchr(cline, '\0'); isspace((unsigned char)*--p);)
 			continue;
 		*++p = '\0';
 		f = (struct filed *)calloc(1, sizeof(*f));
 		*nextp = f;
 		nextp = &f->f_next;
-		cfline(cline, f);
+		cfline(cline, f, prog, host);
 	}
 
 	/* close the configuration file */
@@ -1267,23 +1812,29 @@ init(void)
 				printf("%s", f->f_un.f_forw.f_hname);
 				break;
 
+			case F_PIPE:
+				printf("%s", f->f_un.f_pipe.f_pname);
+				break;
+
 			case F_USERS:
 				for (i = 0;
 				    i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
 					printf("%s, ", f->f_un.f_uname[i]);
 				break;
 			}
+			if (f->f_program != NULL)
+				printf(" (%s)", f->f_program);
 			printf("\n");
 		}
 	}
 
-	finet = socksetup(PF_UNSPEC);
+	finet = socksetup(PF_UNSPEC, bindhostname);
 	if (finet) {
 		if (SecureMode) {
 			for (i = 0; i < *finet; i++) {
 				if (shutdown(finet[i+1], SHUT_RD) < 0) {
 					logerror("shutdown() failed");
-					die(0);
+					die(NULL);
 				}
 			}
 		} else
@@ -1293,21 +1844,31 @@ init(void)
 
 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
 	dprintf("syslogd: restarted\n");
+	/*
+	 * Log a change in hostname, but only on a restart (we detect this
+	 * by checking to see if we're passed a kevent).
+	 */
+	if (ev != NULL && strcmp(oldLocalHostName, LocalHostName) != 0) {
+		(void)snprintf(hostMsg, sizeof(hostMsg),
+		    "syslogd: host name changed, \"%s\" to \"%s\"",
+		    oldLocalHostName, LocalHostName);
+		logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE);
+		dprintf("%s\n", hostMsg);
+	}
 }
 
 /*
  * Crack a configuration file line
  */
 void
-cfline(char *line, struct filed *f)
+cfline(char *line, struct filed *f, char *prog, char *host)
 {
 	struct addrinfo hints, *res;
-	int    error, i, pri;
+	int    error, i, pri, syncfile;
 	char   *bp, *p, *q;
 	char   buf[MAXLINE];
-	int    sp_err;
 
-	dprintf("cfline(%s)\n", line);
+	dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
 
 	errno = 0;	/* keep strerror() stuff out of logerror messages */
 
@@ -1327,7 +1888,8 @@ cfline(char *line, struct filed *f)
 		    "Warning: `%s' space or tab before the log facility",
 		    line);
 		/* Fix: strip all spaces/tabs before the log facility */
-		while (*q++ && isblank((unsigned char)*q));
+		while (*q++ && isblank((unsigned char)*q))
+			/* skip blanks */;
 		line = q; 
 	}
 
@@ -1345,49 +1907,73 @@ cfline(char *line, struct filed *f)
 		logerror(
 		    "Error: `%s' log facility or log target missing",
 		    line);
+		return;
 	}
 	
-	/* q is at the end of the blank between the two fields */
-	sp_err = 0;
-	while (isblank((unsigned char)*q) && (q != line))
-		if (*q-- == ' ')
-			sp_err = 1;
-
-	if (sp_err) {
-		/* 
-		 * A space somewhere between the log facility 
-		 * and the log target: complain 
-		 */
-		errno = 0;
-		logerror(
-		    "Warning: `%s' space found where tab is expected",
-		    line);
-		/* ... and fix the problem: replace all spaces by tabs */
-		while (*++q && isblank((unsigned char)*q))
-			if (*q == ' ')
-				*q='\t';	
-	}	
+	/* save host name, if any */
+	if (*host == '*')
+		f->f_host = NULL;
+	else {
+		f->f_host = strdup(host);
+		trim_localdomain(f->f_host);
+	}
+
+	/* save program name, if any */
+	if (*prog == '*')
+		f->f_program = NULL;
+	else
+		f->f_program = strdup(prog);
 
 	/* scan through the list of selectors */
-	for (p = line; *p && *p != '\t';) {
+	for (p = line; *p && !isblank((unsigned char)*p);) {
+		int pri_done, pri_cmp, pri_invert;
 
 		/* find the end of this facility name list */
-		for (q = p; *q && *q != '\t' && *q++ != '.'; )
+		for (q = p; *q && !isblank((unsigned char)*q) && *q++ != '.'; )
 			continue;
 
+		/* get the priority comparison */
+		pri_cmp = 0;
+		pri_done = 0;
+		pri_invert = 0;
+		if (*q == '!') {
+			pri_invert = 1;
+			q++;
+		}
+		while (! pri_done) {
+			switch (*q) {
+			case '<':
+				pri_cmp = PRI_LT;
+				q++;
+				break;
+			case '=':
+				pri_cmp = PRI_EQ;
+				q++;
+				break;
+			case '>':
+				pri_cmp = PRI_GT;
+				q++;
+				break;
+			default:
+				pri_done = 1;
+				break;
+			}
+		}
+
 		/* collect priority name */
-		for (bp = buf; *q && !strchr("\t,;", *q); )
+		for (bp = buf; *q && !strchr("\t ,;", *q); )
 			*bp++ = *q++;
 		*bp = '\0';
 
 		/* skip cruft */
-		while (strchr(", ;", *q))
+		while (strchr(",;", *q))
 			q++;
 
 		/* decode priority name */
-		if (*buf == '*')
+		if (*buf == '*') {
 			pri = LOG_PRIMASK + 1;
-		else {
+			pri_cmp = PRI_LT | PRI_EQ | PRI_GT;
+		} else {
 			pri = decode(buf, prioritynames);
 			if (pri < 0) {
 				errno = 0;
@@ -1395,15 +1981,22 @@ cfline(char *line, struct filed *f)
 				return;
 			}
 		}
+		if (pri_cmp == 0)
+			pri_cmp = UniquePriority ? PRI_EQ
+						 : PRI_EQ | PRI_GT;
+		if (pri_invert)
+			pri_cmp ^= PRI_LT | PRI_EQ | PRI_GT;
 
 		/* scan facilities */
-		while (*p && !strchr("\t.;", *p)) {
-			for (bp = buf; *p && !strchr("\t,;.", *p); )
+		while (*p && !strchr("\t .;", *p)) {
+			for (bp = buf; *p && !strchr("\t ,;.", *p); )
 				*bp++ = *p++;
 			*bp = '\0';
 			if (*buf == '*')
-				for (i = 0; i < LOG_NFACILITIES; i++)
+				for (i = 0; i < LOG_NFACILITIES; i++) {
 					f->f_pmask[i] = pri;
+					f->f_pcmp[i] = pri_cmp;
+				}
 			else {
 				i = decode(buf, facilitynames);
 				if (i < 0) {
@@ -1413,6 +2006,7 @@ cfline(char *line, struct filed *f)
 					return;
 				}
 				f->f_pmask[i >> 3] = pri;
+				f->f_pcmp[i >> 3] = pri_cmp;
 			}
 			while (*p == ',' || *p == ' ')
 				p++;
@@ -1422,12 +2016,16 @@ cfline(char *line, struct filed *f)
 	}
 
 	/* skip to action part */
-	sp_err = 0;
-	while ((*p == '\t') || (*p == ' '))
+	while (isblank((unsigned char)*p))
 		p++;
 
-	switch (*p)
-	{
+	if (*p == '-') {
+		syncfile = 0;
+		p++;
+	} else
+		syncfile = 1;
+
+	switch (*p) {
 	case '@':
 		(void)strlcpy(f->f_un.f_forw.f_hname, ++p,
 		    sizeof(f->f_un.f_forw.f_hname));
@@ -1453,6 +2051,8 @@ cfline(char *line, struct filed *f)
 			logerror(p);
 			break;
 		}
+		if (syncfile)
+			f->f_flags |= FFLAG_SYNC;
 		if (isatty(f->f_file))
 			f->f_type = F_TTY;
 		else
@@ -1461,6 +2061,13 @@ cfline(char *line, struct filed *f)
 			f->f_type = F_CONSOLE;
 		break;
 
+	case '|':
+		f->f_un.f_pipe.f_pid = 0;
+		(void) strlcpy(f->f_un.f_pipe.f_pname, p + 1,
+		    sizeof(f->f_un.f_pipe.f_pname));
+		f->f_type = F_PIPE;
+		break;
+
 	case '*':
 		f->f_type = F_WALL;
 		break;
@@ -1493,12 +2100,12 @@ decode(const char *name, CODE *codetab)
 	CODE *c;
 	char *p, buf[40];
 
-	if (isdigit(*name))
+	if (isdigit((unsigned char)*name))
 		return (atoi(name));
 
 	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
-		if (isupper(*name))
-			*p = tolower(*name);
+		if (isupper((unsigned char)*name))
+			*p = tolower((unsigned char)*name);
 		else
 			*p = *name;
 	}
@@ -1530,9 +2137,10 @@ getmsgbufsize(void)
 }
 
 int *
-socksetup(int af)
+socksetup(int af, const char *hostname)
 {
 	struct addrinfo hints, *res, *r;
+	struct kevent *ev;
 	int error, maxs, *s, *socks;
 	const int on = 1;
 
@@ -1543,11 +2151,11 @@ socksetup(int af)
 	hints.ai_flags = AI_PASSIVE;
 	hints.ai_family = af;
 	hints.ai_socktype = SOCK_DGRAM;
-	error = getaddrinfo(NULL, "syslog", &hints, &res);
+	error = getaddrinfo(hostname, "syslog", &hints, &res);
 	if (error) {
 		logerror(gai_strerror(error));
 		errno = 0;
-		die(0);
+		die(NULL);
 	}
 
 	/* Count max number of sockets we may open */
@@ -1556,7 +2164,7 @@ socksetup(int af)
 	socks = malloc((maxs+1) * sizeof(int));
 	if (!socks) {
 		logerror("Couldn't allocate memory for sockets");
-		die(0);
+		die(NULL);
 	}
 
 	*socks = 0;   /* num of sockets counter at start of array */
@@ -1573,10 +2181,16 @@ socksetup(int af)
 			close(*s);
 			continue;
 		}
-		if (!SecureMode && bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
-			logerror("bind() failed");
-			close (*s);
-			continue;
+
+		if (!SecureMode) {
+			if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+				logerror("bind() failed");
+				close(*s);
+				continue;
+			}
+			ev = allocevchange();
+			EV_SET(ev, *s, EVFILT_READ, EV_ADD | EV_ENABLE,
+			    0, 0, (intptr_t) dispatch_read_finet);
 		}
 
 		*socks = *socks + 1;
@@ -1588,10 +2202,179 @@ socksetup(int af)
 		if(Debug)
 			return(NULL);
 		else
-			die(0);
+			die(NULL);
 	}
 	if (res)
 		freeaddrinfo(res);
 
 	return(socks);
 }
+
+/*
+ * Fairly similar to popen(3), but returns an open descriptor, as opposed
+ * to a FILE *.
+ */
+int
+p_open(char *prog, pid_t *rpid)
+{
+	int pfd[2], nulldesc, i;
+	pid_t pid;
+	char *argv[4];	/* sh -c cmd NULL */
+	char errmsg[200];
+
+	if (pipe(pfd) == -1)
+		return (-1);
+	if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+		/* We are royally screwed anyway. */
+		return (-1);
+	}
+
+	switch ((pid = fork())) {
+	case -1:
+		(void) close(nulldesc);
+		return (-1);
+
+	case 0:
+		argv[0] = "sh";
+		argv[1] = "-c";
+		argv[2] = prog;
+		argv[3] = NULL;
+
+		(void) setsid();	/* avoid catching SIGHUPs. */
+
+		/*
+		 * Reset ignored signals to their default behavior.
+		 */
+		(void)signal(SIGTERM, SIG_DFL);
+		(void)signal(SIGINT, SIG_DFL);
+		(void)signal(SIGQUIT, SIG_DFL);
+		(void)signal(SIGPIPE, SIG_DFL);
+		(void)signal(SIGHUP, SIG_DFL);
+
+		dup2(pfd[0], STDIN_FILENO);
+		dup2(nulldesc, STDOUT_FILENO);
+		dup2(nulldesc, STDERR_FILENO);
+		for (i = getdtablesize(); i > 2; i--)
+			(void) close(i);
+
+		(void) execvp(_PATH_BSHELL, argv);
+		_exit(255);
+	}
+
+	(void) close(nulldesc);
+	(void) close(pfd[0]);
+
+	/*
+	 * Avoid blocking on a hung pipe.  With O_NONBLOCK, we are
+	 * supposed to get an EWOULDBLOCK on writev(2), which is
+	 * caught by the logic above anyway, which will in turn
+	 * close the pipe, and fork a new logging subprocess if
+	 * necessary.  The stale subprocess will be killed some
+	 * time later unless it terminated itself due to closing
+	 * its input pipe.
+	 */
+	if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
+		/* This is bad. */
+		(void) snprintf(errmsg, sizeof(errmsg),
+		    "Warning: cannot change pipe to pid %d to "
+		    "non-blocking.", (int) pid);
+		logerror(errmsg);
+	}
+	*rpid = pid;
+	return (pfd[1]);
+}
+
+void
+deadq_enter(pid_t pid, const char *name)
+{
+	dq_t p;
+	int status;
+
+	/*
+	 * Be paranoid: if we can't signal the process, don't enter it
+	 * into the dead queue (perhaps it's already dead).  If possible,
+	 * we try to fetch and log the child's status.
+	 */
+	if (kill(pid, 0) != 0) {
+		if (waitpid(pid, &status, WNOHANG) > 0)
+			log_deadchild(pid, status, name);
+		return;
+	}
+
+	p = malloc(sizeof(*p));
+	if (p == NULL) {
+		errno = 0;
+		logerror("panic: out of memory!");
+		exit(1);
+	}
+
+	p->dq_pid = pid;
+	p->dq_timeout = DQ_TIMO_INIT;
+	TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
+}
+
+int
+deadq_remove(pid_t pid)
+{
+	dq_t q;
+
+	for (q = TAILQ_FIRST(&deadq_head); q != NULL;
+	     q = TAILQ_NEXT(q, dq_entries)) {
+		if (q->dq_pid == pid) {
+			TAILQ_REMOVE(&deadq_head, q, dq_entries);
+			free(q);
+			return (1);
+		}
+	}
+	return (0);
+}
+
+void
+log_deadchild(pid_t pid, int status, const char *name)
+{
+	int code;
+	char buf[256];
+	const char *reason;
+
+	/* Keep strerror() struff out of logerror messages. */
+	errno = 0;
+	if (WIFSIGNALED(status)) {
+		reason = "due to signal";
+		code = WTERMSIG(status);
+	} else {
+		reason = "with status";
+		code = WEXITSTATUS(status);
+		if (code == 0)
+			return;
+	}
+	(void) snprintf(buf, sizeof(buf),
+	    "Logging subprocess %d (%s) exited %s %d.",
+	    pid, name, reason, code);
+	logerror(buf);
+}
+
+static struct kevent changebuf[8];
+static int nchanges;
+
+static struct kevent *
+allocevchange(void)
+{
+
+	if (nchanges == A_CNT(changebuf)) {
+		/* XXX Error handling could be improved. */
+		(void) wait_for_events(NULL, 0);
+	}
+
+	return (&changebuf[nchanges++]);
+}
+
+static int
+wait_for_events(struct kevent *events, size_t nevents)
+{
+	int rv;
+
+	rv = kevent(fkq, nchanges ? changebuf : NULL, nchanges,
+		    events, nevents, NULL);
+	nchanges = 0;
+	return (rv);
+}
