syslogd.c revision 69175
1140308Scognet/*
2140308Scognet * Copyright (c) 1983, 1988, 1993, 1994
3140308Scognet *	The Regents of the University of California.  All rights reserved.
4140308Scognet *
5140308Scognet * Redistribution and use in source and binary forms, with or without
6140308Scognet * modification, are permitted provided that the following conditions
7140308Scognet * are met:
8140308Scognet * 1. Redistributions of source code must retain the above copyright
9140308Scognet *    notice, this list of conditions and the following disclaimer.
10140308Scognet * 2. Redistributions in binary form must reproduce the above copyright
11140308Scognet *    notice, this list of conditions and the following disclaimer in the
12140308Scognet *    documentation and/or other materials provided with the distribution.
13140308Scognet * 3. All advertising materials mentioning features or use of this software
14140308Scognet *    must display the following acknowledgement:
15140308Scognet *	This product includes software developed by the University of
16140308Scognet *	California, Berkeley and its contributors.
17140308Scognet * 4. Neither the name of the University nor the names of its contributors
18140308Scognet *    may be used to endorse or promote products derived from this software
19140308Scognet *    without specific prior written permission.
20140308Scognet *
21140308Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22140308Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23140308Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24140308Scognet * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25140308Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26140308Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27140308Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28140308Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29140308Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30140308Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31140308Scognet * SUCH DAMAGE.
32140308Scognet */
33140308Scognet
34140308Scognet#ifndef lint
35140308Scognetstatic const char copyright[] =
36140308Scognet"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
37140308Scognet	The Regents of the University of California.  All rights reserved.\n";
38140308Scognet#endif /* not lint */
39140308Scognet
40140308Scognet#ifndef lint
41140308Scognet#if 0
42140308Scognetstatic char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";
43140308Scognet#endif
44140308Scognetstatic const char rcsid[] =
45140308Scognet  "$FreeBSD: head/usr.sbin/syslogd/syslogd.c 69175 2000-11-25 21:00:58Z phk $";
46140308Scognet#endif /* not lint */
47140308Scognet
48140308Scognet/*
49140308Scognet *  syslogd -- log system messages
50140350Scognet *
51140308Scognet * This program implements a system log. It takes a series of lines.
52140308Scognet * Each line may have a priority, signified as "<n>" as
53140308Scognet * the first characters of the line.  If this is
54140308Scognet * not present, a default priority is used.
55140308Scognet *
56140308Scognet * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
57140308Scognet * cause it to reread its configuration file.
58140308Scognet *
59140308Scognet * Defined Constants:
60140308Scognet *
61140308Scognet * MAXLINE -- the maximimum line length that can be handled.
62140308Scognet * DEFUPRI -- the default priority for user messages
63140308Scognet * DEFSPRI -- the default priority for kernel messages
64140308Scognet *
65140308Scognet * Author: Eric Allman
66140308Scognet * extensive changes by Ralph Campbell
67140308Scognet * more extensive changes by Eric Allman (again)
68140308Scognet * Extension to log by program name as well as facility and priority
69140308Scognet *   by Peter da Silva.
70140308Scognet * -u and -v by Harlan Stenn.
71140308Scognet * Priority comparison code by Harlan Stenn.
72140308Scognet */
73140308Scognet
74140308Scognet#define	MAXLINE		1024		/* maximum line length */
75140308Scognet#define	MAXSVLINE	120		/* maximum saved line length */
76140308Scognet#define DEFUPRI		(LOG_USER|LOG_NOTICE)
77140308Scognet#define DEFSPRI		(LOG_KERN|LOG_CRIT)
78140308Scognet#define TIMERINTVL	30		/* interval for checking flush, mark */
79140308Scognet#define TTYMSGTIME	1		/* timed out passed to ttymsg */
80140308Scognet
81140308Scognet#include <sys/param.h>
82140308Scognet#include <sys/ioctl.h>
83140308Scognet#include <sys/stat.h>
84140308Scognet#include <sys/wait.h>
85140308Scognet#include <sys/socket.h>
86140308Scognet#include <sys/queue.h>
87140308Scognet#include <sys/uio.h>
88140308Scognet#include <sys/un.h>
89140308Scognet#include <sys/time.h>
90140308Scognet#include <sys/resource.h>
91140308Scognet#include <sys/syslimits.h>
92140308Scognet#include <paths.h>
93140308Scognet
94140308Scognet#include <netinet/in.h>
95140308Scognet#include <netdb.h>
96140308Scognet#include <arpa/inet.h>
97140308Scognet
98140308Scognet#include <ctype.h>
99140308Scognet#include <err.h>
100140308Scognet#include <errno.h>
101140308Scognet#include <fcntl.h>
102140308Scognet#include <regex.h>
103140308Scognet#include <setjmp.h>
104140308Scognet#include <signal.h>
105140308Scognet#include <stdio.h>
106140308Scognet#include <stdlib.h>
107140308Scognet#include <string.h>
108140308Scognet#include <sysexits.h>
109140308Scognet#include <unistd.h>
110140308Scognet#include <utmp.h>
111140308Scognet#include "pathnames.h"
112140308Scognet
113140308Scognet#define SYSLOG_NAMES
114140308Scognet#include <sys/syslog.h>
115140308Scognet
116140308Scognetconst char	*ConfFile = _PATH_LOGCONF;
117140308Scognetconst char	*PidFile = _PATH_LOGPID;
118140308Scognetconst char	ctty[] = _PATH_CONSOLE;
119140308Scognet
120140308Scognet#define	dprintf		if (Debug) printf
121140308Scognet
122140308Scognet#define MAXUNAMES	20	/* maximum number of user names */
123140308Scognet
124140308Scognet#define MAXFUNIX       20
125140308Scognet
126140308Scognetint nfunix = 1;
127140308Scognetchar *funixn[MAXFUNIX] = { _PATH_LOG };
128140308Scognetint funix[MAXFUNIX];
129140308Scognet
130140308Scognet/*
131140308Scognet * Flags to logmsg().
132140308Scognet */
133140308Scognet
134140308Scognet#define IGN_CONS	0x001	/* don't print on console */
135140308Scognet#define SYNC_FILE	0x002	/* do fsync on file after printing */
136140308Scognet#define ADDDATE		0x004	/* add a date to the message */
137140308Scognet#define MARK		0x008	/* this message is a mark */
138140308Scognet#define ISKERNEL	0x010	/* kernel generated message */
139140308Scognet
140140308Scognet/*
141140308Scognet * This structure represents the files that will have log
142140308Scognet * copies printed.
143140308Scognet */
144140308Scognet
145140308Scognetstruct filed {
146140308Scognet	struct	filed *f_next;		/* next in linked list */
147140308Scognet	short	f_type;			/* entry type, see below */
148140308Scognet	short	f_file;			/* file descriptor */
149140308Scognet	time_t	f_time;			/* time this was last written */
150140308Scognet	char	*f_host;		/* host from which to recd. */
151140308Scognet	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
152140308Scognet	u_char	f_pcmp[LOG_NFACILITIES+1];	/* compare priority */
153140308Scognet#define PRI_LT	0x1
154140308Scognet#define PRI_EQ	0x2
155140308Scognet#define PRI_GT	0x4
156140308Scognet	char	*f_program;		/* program this applies to */
157140308Scognet	union {
158140308Scognet		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
159140308Scognet		struct {
160140308Scognet			char	f_hname[MAXHOSTNAMELEN+1];
161140308Scognet			struct sockaddr_in	f_addr;
162140308Scognet		} f_forw;		/* forwarding address */
163140308Scognet		char	f_fname[MAXPATHLEN];
164140308Scognet		struct {
165140308Scognet			char	f_pname[MAXPATHLEN];
166140308Scognet			pid_t	f_pid;
167140308Scognet		} f_pipe;
168140308Scognet	} f_un;
169140308Scognet	char	f_prevline[MAXSVLINE];		/* last message logged */
170140308Scognet	char	f_lasttime[16];			/* time of last occurrence */
171140308Scognet	char	f_prevhost[MAXHOSTNAMELEN+1];	/* host from which recd. */
172140308Scognet	int	f_prevpri;			/* pri of f_prevline */
173140308Scognet	int	f_prevlen;			/* length of f_prevline */
174140308Scognet	int	f_prevcount;			/* repetition cnt of prevline */
175140308Scognet	int	f_repeatcount;			/* number of "repeated" msgs */
176140308Scognet};
177140308Scognet
178140308Scognet/*
179140308Scognet * Queue of about-to-be dead processes we should watch out for.
180140308Scognet */
181140308Scognet
182140308ScognetTAILQ_HEAD(stailhead, deadq_entry) deadq_head;
183140308Scognetstruct stailhead *deadq_headp;
184140308Scognet
185140308Scognetstruct deadq_entry {
186140308Scognet	pid_t				dq_pid;
187140308Scognet	int				dq_timeout;
188140308Scognet	TAILQ_ENTRY(deadq_entry)	dq_entries;
189140308Scognet};
190140308Scognet
191140308Scognet/*
192140308Scognet * The timeout to apply to processes waiting on the dead queue.  Unit
193140308Scognet * of measure is `mark intervals', i.e. 20 minutes by default.
194140308Scognet * Processes on the dead queue will be terminated after that time.
195140308Scognet */
196140308Scognet
197140308Scognet#define DQ_TIMO_INIT	2
198140308Scognet
199140308Scognettypedef struct deadq_entry *dq_t;
200140308Scognet
201140308Scognet
202140308Scognet/*
203140308Scognet * Struct to hold records of network addresses that are allowed to log
204140308Scognet * to us.
205140308Scognet */
206140308Scognetstruct allowedpeer {
207140308Scognet	int isnumeric;
208140308Scognet	u_short port;
209140308Scognet	union {
210140308Scognet		struct {
211140308Scognet			struct in_addr addr;
212140308Scognet			struct in_addr mask;
213140308Scognet		} numeric;
214140308Scognet		char *name;
215140308Scognet	} u;
216140308Scognet#define a_addr u.numeric.addr
217140308Scognet#define a_mask u.numeric.mask
218140308Scognet#define a_name u.name
219140308Scognet};
220140308Scognet
221140308Scognet
222140308Scognet/*
223140308Scognet * Intervals at which we flush out "message repeated" messages,
224140308Scognet * in seconds after previous message is logged.  After each flush,
225140308Scognet * we move to the next interval until we reach the largest.
226140308Scognet */
227140308Scognetint	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
228140308Scognet#define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
229140308Scognet#define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
230140308Scognet#define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
231140308Scognet				 (f)->f_repeatcount = MAXREPEAT; \
232140308Scognet			}
233140308Scognet
234140308Scognet/* values for f_type */
235140308Scognet#define F_UNUSED	0		/* unused entry */
236140308Scognet#define F_FILE		1		/* regular file */
237140308Scognet#define F_TTY		2		/* terminal */
238140308Scognet#define F_CONSOLE	3		/* console terminal */
239140308Scognet#define F_FORW		4		/* remote machine */
240140308Scognet#define F_USERS		5		/* list of users */
241140308Scognet#define F_WALL		6		/* everyone logged on */
242140308Scognet#define F_PIPE		7		/* pipe to program */
243140308Scognet
244140308Scognetchar	*TypeNames[8] = {
245140308Scognet	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
246140308Scognet	"FORW",		"USERS",	"WALL",		"PIPE"
247140308Scognet};
248140308Scognet
249140308Scognetstruct	filed *Files;
250140308Scognetstruct	filed consfile;
251140308Scognet
252140308Scognetint	Debug;			/* debug flag */
253140308Scognetint	resolve = 1;		/* resolve hostname */
254140308Scognetchar	LocalHostName[MAXHOSTNAMELEN+1];	/* our hostname */
255140308Scognetchar	*LocalDomain;		/* our local domain name */
256140308Scognetint	finet = -1;		/* Internet datagram socket */
257140308Scognetint	fklog = -1;		/* /dev/klog */
258140308Scognetint	LogPort;		/* port number for INET connections */
259140308Scognetint	Initialized = 0;	/* set when we have initialized ourselves */
260140308Scognetint	MarkInterval = 20 * 60;	/* interval between marks in seconds */
261140308Scognetint	MarkSeq = 0;		/* mark sequence number */
262140308Scognetint	SecureMode = 0;		/* when true, receive only unix domain socks */
263140308Scognet
264140308Scognetchar	bootfile[MAXLINE+1];	/* booted kernel file */
265140308Scognet
266140308Scognetstruct allowedpeer *AllowedPeers;
267140308Scognetint	NumAllowed = 0;		/* # of AllowedPeer entries */
268140308Scognet
269140308Scognetint	UniquePriority = 0;	/* Only log specified priority? */
270140308Scognetint	LogFacPri = 0;		/* Put facility and priority in log message: */
271140308Scognet				/* 0=no, 1=numeric, 2=names */
272140308Scognetint	KeepKernFac = 0;	/* Keep remotely logged kernel facility */
273140308Scognet
274140308Scognetint	allowaddr __P((char *));
275140308Scognetvoid	cfline __P((char *, struct filed *, char *, char *));
276140308Scognetchar   *cvthname __P((struct sockaddr_in *));
277140308Scognetvoid	deadq_enter __P((pid_t, const char *));
278140308Scognetint	deadq_remove __P((pid_t));
279140308Scognetint	decode __P((const char *, CODE *));
280140308Scognetvoid	die __P((int));
281140308Scognetvoid	domark __P((int));
282140308Scognetvoid	fprintlog __P((struct filed *, int, char *));
283140308Scognetvoid	init __P((int));
284140308Scognetvoid	logerror __P((const char *));
285140308Scognetvoid	logmsg __P((int, char *, char *, int));
286140308Scognetvoid	log_deadchild __P((pid_t, int, const char *));
287140308Scognetvoid	printline __P((char *, char *));
288140308Scognetvoid	printsys __P((char *));
289140308Scognetint	p_open __P((char *, pid_t *));
290140308Scognetvoid	readklog __P((void));
291140308Scognetvoid	reapchild __P((int));
292140308Scognetchar   *ttymsg __P((struct iovec *, int, char *, int));
293140308Scognetstatic void	usage __P((void));
294140308Scognetint	validate __P((struct sockaddr_in *, const char *));
295140308Scognetvoid	wallmsg __P((struct filed *, struct iovec *));
296140308Scognetint	waitdaemon __P((int, int, int));
297140308Scognetvoid	timedout __P((int));
298140308Scognet
299140308Scognetint
300140308Scognetmain(argc, argv)
301140308Scognet	int argc;
302140308Scognet	char *argv[];
303140308Scognet{
304140308Scognet	int ch, i, l;
305140308Scognet	struct sockaddr_un sunx, fromunix;
306140308Scognet	struct sockaddr_in sin, frominet;
307140308Scognet	FILE *fp;
308140308Scognet	char *p, *hname, line[MAXLINE + 1];
309140308Scognet	struct timeval tv, *tvp;
310140308Scognet	struct sigaction sact;
311140308Scognet	sigset_t mask;
312140308Scognet	pid_t ppid = 1;
313140308Scognet	socklen_t len;
314140308Scognet
315140308Scognet	while ((ch = getopt(argc, argv, "a:df:kl:m:np:suv")) != -1)
316140308Scognet		switch (ch) {
317140308Scognet		case 'a':		/* allow specific network addresses only */
318140308Scognet			if (allowaddr(optarg) == -1)
319140308Scognet				usage();
320140308Scognet			break;
321140308Scognet		case 'd':		/* debug */
322140308Scognet			Debug++;
323140308Scognet			break;
324140308Scognet		case 'f':		/* configuration file */
325140308Scognet			ConfFile = optarg;
326140308Scognet			break;
327140308Scognet		case 'k':		/* keep remote kern fac */
328140308Scognet			KeepKernFac = 1;
329140308Scognet			break;
330140308Scognet		case 'l':
331140308Scognet			if (nfunix < MAXFUNIX)
332140308Scognet				funixn[nfunix++] = optarg;
333140308Scognet			else
334140308Scognet				warnx("out of descriptors, ignoring %s",
335140308Scognet					optarg);
336140308Scognet			break;
337140350Scognet		case 'm':		/* mark interval */
338140350Scognet			MarkInterval = atoi(optarg) * 60;
339140350Scognet			break;
340140350Scognet		case 'n':
341140308Scognet			resolve = 0;
342140308Scognet			break;
343140308Scognet		case 'p':		/* path */
344140308Scognet			funixn[0] = optarg;
345140308Scognet			break;
346140308Scognet		case 's':		/* no network mode */
347140308Scognet			SecureMode++;
348140350Scognet			break;
349140308Scognet		case 'u':		/* only log specified priority */
350140308Scognet		        UniquePriority++;
351140308Scognet			break;
352140308Scognet		case 'v':		/* log facility and priority */
353140308Scognet		  	LogFacPri++;
354140308Scognet			break;
355140308Scognet		case '?':
356140308Scognet		default:
357140308Scognet			usage();
358140308Scognet		}
359140308Scognet	if ((argc -= optind) != 0)
360140308Scognet		usage();
361140308Scognet
362140308Scognet	if (!Debug) {
363140308Scognet		ppid = waitdaemon(0, 0, 30);
364140308Scognet		if (ppid < 0)
365140308Scognet			err(1, "could not become daemon");
366140308Scognet	} else
367140308Scognet		setlinebuf(stdout);
368140308Scognet
369140308Scognet	if (NumAllowed)
370140308Scognet		endservent();
371140308Scognet
372140308Scognet	consfile.f_type = F_CONSOLE;
373140308Scognet	(void)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1);
374140308Scognet	(void)gethostname(LocalHostName, sizeof(LocalHostName));
375140308Scognet	if ((p = strchr(LocalHostName, '.')) != NULL) {
376140308Scognet		*p++ = '\0';
377140308Scognet		LocalDomain = p;
378140308Scognet	} else
379140308Scognet		LocalDomain = "";
380140308Scognet	(void)strcpy(bootfile, getbootfile());
381140308Scognet	(void)signal(SIGTERM, die);
382140308Scognet	(void)signal(SIGINT, Debug ? die : SIG_IGN);
383140308Scognet	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
384140308Scognet	/*
385140308Scognet	 * We don't want the SIGCHLD and SIGHUP handlers to interfere
386140308Scognet	 * with each other; they are likely candidates for being called
387140308Scognet	 * simultaneously (SIGHUP closes pipe descriptor, process dies,
388140308Scognet	 * SIGCHLD happens).
389140308Scognet	 */
390140308Scognet	sigemptyset(&mask);
391	sigaddset(&mask, SIGHUP);
392	sact.sa_handler = reapchild;
393	sact.sa_mask = mask;
394	sact.sa_flags = SA_RESTART;
395	(void)sigaction(SIGCHLD, &sact, NULL);
396	(void)signal(SIGALRM, domark);
397	(void)signal(SIGPIPE, SIG_IGN);	/* We'll catch EPIPE instead. */
398	(void)alarm(TIMERINTVL);
399
400	TAILQ_INIT(&deadq_head);
401
402#ifndef SUN_LEN
403#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
404#endif
405	for (i = 0; i < nfunix; i++) {
406		memset(&sunx, 0, sizeof(sunx));
407		sunx.sun_family = AF_UNIX;
408		(void)strncpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
409		funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
410		if (funix[i] < 0 ||
411		    bind(funix[i], (struct sockaddr *)&sunx,
412			 SUN_LEN(&sunx)) < 0 ||
413		    chmod(funixn[i], 0666) < 0) {
414			(void) snprintf(line, sizeof line,
415					"cannot create %s", funixn[i]);
416			logerror(line);
417			dprintf("cannot create %s (%d)\n", funixn[i], errno);
418			if (i == 0)
419				die(0);
420		}
421	}
422	if (SecureMode <= 1)
423		finet = socket(AF_INET, SOCK_DGRAM, 0);
424	if (finet >= 0) {
425		struct servent *sp;
426
427		sp = getservbyname("syslog", "udp");
428		if (sp == NULL) {
429			errno = 0;
430			logerror("syslog/udp: unknown service");
431			die(0);
432		}
433		memset(&sin, 0, sizeof(sin));
434		sin.sin_family = AF_INET;
435		sin.sin_port = LogPort = sp->s_port;
436
437		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
438			logerror("bind");
439			if (!Debug)
440				die(0);
441		}
442	}
443	if (finet >= 0 && SecureMode) {
444		if (shutdown(finet, SHUT_RD) < 0) {
445			logerror("shutdown");
446			if (!Debug)
447				die(0);
448		}
449	}
450
451	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
452		if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0)
453			fklog = -1;
454	if (fklog < 0)
455		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
456
457	/* tuck my process id away */
458	fp = fopen(PidFile, "w");
459	if (fp != NULL) {
460		fprintf(fp, "%d\n", getpid());
461		(void) fclose(fp);
462	}
463
464	dprintf("off & running....\n");
465
466	init(0);
467	/* prevent SIGHUP and SIGCHLD handlers from running in parallel */
468	sigemptyset(&mask);
469	sigaddset(&mask, SIGCHLD);
470	sact.sa_handler = init;
471	sact.sa_mask = mask;
472	sact.sa_flags = SA_RESTART;
473	(void)sigaction(SIGHUP, &sact, NULL);
474
475	tvp = &tv;
476	tv.tv_sec = tv.tv_usec = 0;
477
478	for (;;) {
479		fd_set readfds;
480		int nfds = 0;
481
482		FD_ZERO(&readfds);
483		if (fklog != -1) {
484			FD_SET(fklog, &readfds);
485			if (fklog > nfds)
486				nfds = fklog;
487		}
488		if (finet != -1 && !SecureMode) {
489			FD_SET(finet, &readfds);
490			if (finet > nfds)
491				nfds = finet;
492		}
493		for (i = 0; i < nfunix; i++) {
494			if (funix[i] != -1) {
495				FD_SET(funix[i], &readfds);
496				if (funix[i] > nfds)
497					nfds = funix[i];
498			}
499		}
500
501		/*dprintf("readfds = %#x\n", readfds);*/
502		nfds = select(nfds+1, &readfds, (fd_set *)NULL,
503			      (fd_set *)NULL, tvp);
504		if (nfds == 0) {
505			if (tvp) {
506				tvp = NULL;
507				if (ppid != 1)
508					kill(ppid, SIGALRM);
509			}
510			continue;
511		}
512		if (nfds < 0) {
513			if (errno != EINTR)
514				logerror("select");
515			continue;
516		}
517		/*dprintf("got a message (%d, %#x)\n", nfds, readfds);*/
518		if (fklog != -1 && FD_ISSET(fklog, &readfds))
519			readklog();
520		if (finet != -1 && FD_ISSET(finet, &readfds)) {
521			len = sizeof(frominet);
522			l = recvfrom(finet, line, MAXLINE, 0,
523			    (struct sockaddr *)&frominet, &len);
524			if (l > 0) {
525				line[l] = '\0';
526				if (resolve)
527					hname = cvthname(&frominet);
528				else
529					hname = inet_ntoa(frominet.sin_addr);
530				if (validate(&frominet, hname))
531					printline(hname, line);
532			} else if (l < 0 && errno != EINTR)
533				logerror("recvfrom inet");
534		}
535		for (i = 0; i < nfunix; i++) {
536			if (funix[i] != -1 && FD_ISSET(funix[i], &readfds)) {
537				len = sizeof(fromunix);
538				l = recvfrom(funix[i], line, MAXLINE, 0,
539				    (struct sockaddr *)&fromunix, &len);
540				if (l > 0) {
541					line[l] = '\0';
542					printline(LocalHostName, line);
543				} else if (l < 0 && errno != EINTR)
544					logerror("recvfrom unix");
545			}
546		}
547	}
548}
549
550static void
551usage()
552{
553
554	fprintf(stderr, "%s\n%s\n%s\n",
555		"usage: syslogd [-dnsuv] [-a allowed_peer] [-f config_file]",
556		"               [-m mark_interval] [-p log_socket]",
557		"               [-l log_socket]");
558	exit(1);
559}
560
561/*
562 * Take a raw input line, decode the message, and print the message
563 * on the appropriate log files.
564 */
565void
566printline(hname, msg)
567	char *hname;
568	char *msg;
569{
570	int c, pri;
571	char *p, *q, line[MAXLINE + 1];
572
573	/* test for special codes */
574	pri = DEFUPRI;
575	p = msg;
576	if (*p == '<') {
577		pri = 0;
578		while (isdigit(*++p))
579			pri = 10 * pri + (*p - '0');
580		if (*p == '>')
581			++p;
582	}
583	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
584		pri = DEFUPRI;
585
586	/* don't allow users to log kernel messages */
587	if (LOG_FAC(pri) == LOG_KERN && !KeepKernFac)
588		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
589
590	q = line;
591
592	while ((c = (unsigned char)*p++) != '\0' &&
593	    q < &line[sizeof(line) - 4]) {
594		if ((c & 0x80) && c < 0xA0) {
595			c &= 0x7F;
596			*q++ = 'M';
597			*q++ = '-';
598		}
599		if (isascii(c) && iscntrl(c)) {
600			if (c == '\n')
601				*q++ = ' ';
602			else if (c == '\t')
603				*q++ = '\t';
604			else {
605				*q++ = '^';
606				*q++ = c ^ 0100;
607			}
608		} else
609			*q++ = c;
610	}
611	*q = '\0';
612
613	logmsg(pri, line, hname, 0);
614}
615
616/*
617 * Read /dev/klog while data are available, split into lines.
618 */
619void
620readklog()
621{
622	char *p, *q, line[MAXLINE + 1];
623	int len, i;
624
625	len = 0;
626	for (;;) {
627		i = read(fklog, line + len, MAXLINE - 1 - len);
628		if (i > 0)
629			line[i + len] = '\0';
630		else if (i < 0 && errno != EINTR && errno != EAGAIN) {
631			logerror("klog");
632			fklog = -1;
633			break;
634		} else
635			break;
636
637		for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) {
638			*q = '\0';
639			printsys(p);
640		}
641		len = strlen(p);
642		if (len >= MAXLINE - 1) {
643			printsys(p);
644			len = 0;
645		}
646		if (len > 0)
647			memmove(line, p, len + 1);
648	}
649	if (len > 0)
650		printsys(line);
651}
652
653/*
654 * Take a raw input line from /dev/klog, format similar to syslog().
655 */
656void
657printsys(p)
658	char *p;
659{
660	int pri, flags;
661
662	flags = ISKERNEL | SYNC_FILE | ADDDATE;	/* fsync after write */
663	pri = DEFSPRI;
664	if (*p == '<') {
665		pri = 0;
666		while (isdigit(*++p))
667			pri = 10 * pri + (*p - '0');
668		if (*p == '>')
669			++p;
670		if (LOG_FAC(pri) == LOG_CONSOLE)
671			flags |= IGN_CONS;
672	} else {
673		/* kernel printf's come out on console */
674		flags |= IGN_CONS;
675	}
676	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
677		pri = DEFSPRI;
678	logmsg(pri, p, LocalHostName, flags);
679}
680
681time_t	now;
682
683/*
684 * Log a message to the appropriate log files, users, etc. based on
685 * the priority.
686 */
687void
688logmsg(pri, msg, from, flags)
689	int pri;
690	char *msg, *from;
691	int flags;
692{
693	struct filed *f;
694	int i, fac, msglen, omask, prilev;
695	char *timestamp;
696 	char prog[NAME_MAX+1];
697	char buf[MAXLINE+1];
698
699	dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
700	    pri, flags, from, msg);
701
702	omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
703
704	/*
705	 * Check to see if msg looks non-standard.
706	 */
707	msglen = strlen(msg);
708	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
709	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
710		flags |= ADDDATE;
711
712	(void)time(&now);
713	if (flags & ADDDATE)
714		timestamp = ctime(&now) + 4;
715	else {
716		timestamp = msg;
717		msg += 16;
718		msglen -= 16;
719	}
720
721	/* skip leading blanks */
722	while (isspace(*msg)) {
723		msg++;
724		msglen--;
725	}
726
727	/* extract facility and priority level */
728	if (flags & MARK)
729		fac = LOG_NFACILITIES;
730	else
731		fac = LOG_FAC(pri);
732	prilev = LOG_PRI(pri);
733
734	/* extract program name */
735	for (i = 0; i < NAME_MAX; i++) {
736		if (!isalnum(msg[i]))
737			break;
738		prog[i] = msg[i];
739	}
740	prog[i] = 0;
741
742	/* add kernel prefix for kernel messages */
743	if (flags & ISKERNEL) {
744		snprintf(buf, sizeof(buf), "%s: %s", bootfile, msg);
745		msg = buf;
746		msglen = strlen(buf);
747	}
748
749	/* log the message to the particular outputs */
750	if (!Initialized) {
751		f = &consfile;
752		f->f_file = open(ctty, O_WRONLY, 0);
753
754		if (f->f_file >= 0) {
755			fprintlog(f, flags, msg);
756			(void)close(f->f_file);
757		}
758		(void)sigsetmask(omask);
759		return;
760	}
761	for (f = Files; f; f = f->f_next) {
762		/* skip messages that are incorrect priority */
763		if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
764		     ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
765		     ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
766		     )
767		    || f->f_pmask[fac] == INTERNAL_NOPRI)
768			continue;
769		/* skip messages with the incorrect hostname */
770		if (f->f_host)
771			switch (f->f_host[0]) {
772			case '+':
773				if (strcmp(from, f->f_host + 1) != 0)
774					continue;
775				break;
776			case '-':
777				if (strcmp(from, f->f_host + 1) == 0)
778					continue;
779				break;
780			}
781
782		/* skip messages with the incorrect program name */
783		if (f->f_program)
784			if (strcmp(prog, f->f_program) != 0)
785				continue;
786
787		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
788			continue;
789
790		/* don't output marks to recently written files */
791		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
792			continue;
793
794		/*
795		 * suppress duplicate lines to this file
796		 */
797		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
798		    !strcmp(msg, f->f_prevline) &&
799		    !strcasecmp(from, f->f_prevhost)) {
800			(void)strncpy(f->f_lasttime, timestamp, 15);
801			f->f_prevcount++;
802			dprintf("msg repeated %d times, %ld sec of %d\n",
803			    f->f_prevcount, (long)(now - f->f_time),
804			    repeatinterval[f->f_repeatcount]);
805			/*
806			 * If domark would have logged this by now,
807			 * flush it now (so we don't hold isolated messages),
808			 * but back off so we'll flush less often
809			 * in the future.
810			 */
811			if (now > REPEATTIME(f)) {
812				fprintlog(f, flags, (char *)NULL);
813				BACKOFF(f);
814			}
815		} else {
816			/* new line, save it */
817			if (f->f_prevcount)
818				fprintlog(f, 0, (char *)NULL);
819			f->f_repeatcount = 0;
820			f->f_prevpri = pri;
821			(void)strncpy(f->f_lasttime, timestamp, 15);
822			(void)strncpy(f->f_prevhost, from,
823					sizeof(f->f_prevhost)-1);
824			f->f_prevhost[sizeof(f->f_prevhost)-1] = '\0';
825			if (msglen < MAXSVLINE) {
826				f->f_prevlen = msglen;
827				(void)strcpy(f->f_prevline, msg);
828				fprintlog(f, flags, (char *)NULL);
829			} else {
830				f->f_prevline[0] = 0;
831				f->f_prevlen = 0;
832				fprintlog(f, flags, msg);
833			}
834		}
835	}
836	(void)sigsetmask(omask);
837}
838
839void
840fprintlog(f, flags, msg)
841	struct filed *f;
842	int flags;
843	char *msg;
844{
845	struct iovec iov[7];
846	struct iovec *v;
847	int l;
848	char line[MAXLINE + 1], repbuf[80], greetings[200];
849	char *msgret;
850
851	v = iov;
852	if (f->f_type == F_WALL) {
853		v->iov_base = greetings;
854		v->iov_len = snprintf(greetings, sizeof greetings,
855		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
856		    f->f_prevhost, ctime(&now));
857		v++;
858		v->iov_base = "";
859		v->iov_len = 0;
860		v++;
861	} else {
862		v->iov_base = f->f_lasttime;
863		v->iov_len = 15;
864		v++;
865		v->iov_base = " ";
866		v->iov_len = 1;
867		v++;
868	}
869
870	if (LogFacPri) {
871	  	static char fp_buf[30];	/* Hollow laugh */
872		int fac = f->f_prevpri & LOG_FACMASK;
873		int pri = LOG_PRI(f->f_prevpri);
874		char *f_s = 0;
875		char f_n[5];	/* Hollow laugh */
876		char *p_s = 0;
877		char p_n[5];	/* Hollow laugh */
878
879		if (LogFacPri > 1) {
880		  CODE *c;
881
882		  for (c = facilitynames; c->c_name; c++) {
883		    if (c->c_val == fac) {
884		      f_s = c->c_name;
885		      break;
886		    }
887		  }
888		  for (c = prioritynames; c->c_name; c++) {
889		    if (c->c_val == pri) {
890		      p_s = c->c_name;
891		      break;
892		    }
893		  }
894		}
895		if (!f_s) {
896		  snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac));
897		  f_s = f_n;
898		}
899		if (!p_s) {
900		  snprintf(p_n, sizeof p_n, "%d", pri);
901		  p_s = p_n;
902		}
903		snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s);
904		v->iov_base = fp_buf;
905		v->iov_len = strlen(fp_buf);
906	} else {
907	        v->iov_base="";
908		v->iov_len = 0;
909	}
910	v++;
911
912	v->iov_base = f->f_prevhost;
913	v->iov_len = strlen(v->iov_base);
914	v++;
915	v->iov_base = " ";
916	v->iov_len = 1;
917	v++;
918
919	if (msg) {
920		v->iov_base = msg;
921		v->iov_len = strlen(msg);
922	} else if (f->f_prevcount > 1) {
923		v->iov_base = repbuf;
924		v->iov_len = sprintf(repbuf, "last message repeated %d times",
925		    f->f_prevcount);
926	} else {
927		v->iov_base = f->f_prevline;
928		v->iov_len = f->f_prevlen;
929	}
930	v++;
931
932	dprintf("Logging to %s", TypeNames[f->f_type]);
933	f->f_time = now;
934
935	switch (f->f_type) {
936	case F_UNUSED:
937		dprintf("\n");
938		break;
939
940	case F_FORW:
941		dprintf(" %s\n", f->f_un.f_forw.f_hname);
942		/* check for local vs remote messages */
943		if (strcasecmp(f->f_prevhost, LocalHostName))
944			l = snprintf(line, sizeof line - 1,
945			    "<%d>%.15s Forwarded from %s: %s",
946			    f->f_prevpri, iov[0].iov_base, f->f_prevhost,
947			    iov[5].iov_base);
948		else
949			l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
950			     f->f_prevpri, iov[0].iov_base, iov[5].iov_base);
951		if (l > MAXLINE)
952			l = MAXLINE;
953		if ((finet >= 0) &&
954		     (sendto(finet, line, l, 0,
955			     (struct sockaddr *)&f->f_un.f_forw.f_addr,
956			     sizeof(f->f_un.f_forw.f_addr)) != l)) {
957			int e = errno;
958			(void)close(f->f_file);
959			f->f_type = F_UNUSED;
960			errno = e;
961			logerror("sendto");
962		}
963		break;
964
965	case F_FILE:
966		dprintf(" %s\n", f->f_un.f_fname);
967		v->iov_base = "\n";
968		v->iov_len = 1;
969		if (writev(f->f_file, iov, 7) < 0) {
970			int e = errno;
971			(void)close(f->f_file);
972			f->f_type = F_UNUSED;
973			errno = e;
974			logerror(f->f_un.f_fname);
975		} else if (flags & SYNC_FILE)
976			(void)fsync(f->f_file);
977		break;
978
979	case F_PIPE:
980		dprintf(" %s\n", f->f_un.f_pipe.f_pname);
981		v->iov_base = "\n";
982		v->iov_len = 1;
983		if (f->f_un.f_pipe.f_pid == 0) {
984			if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
985						&f->f_un.f_pipe.f_pid)) < 0) {
986				f->f_type = F_UNUSED;
987				logerror(f->f_un.f_pipe.f_pname);
988				break;
989			}
990		}
991		if (writev(f->f_file, iov, 7) < 0) {
992			int e = errno;
993			(void)close(f->f_file);
994			if (f->f_un.f_pipe.f_pid > 0)
995				deadq_enter(f->f_un.f_pipe.f_pid,
996					    f->f_un.f_pipe.f_pname);
997			f->f_un.f_pipe.f_pid = 0;
998			errno = e;
999			logerror(f->f_un.f_pipe.f_pname);
1000		}
1001		break;
1002
1003	case F_CONSOLE:
1004		if (flags & IGN_CONS) {
1005			dprintf(" (ignored)\n");
1006			break;
1007		}
1008		/* FALLTHROUGH */
1009
1010	case F_TTY:
1011		dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
1012		v->iov_base = "\r\n";
1013		v->iov_len = 2;
1014
1015		errno = 0;	/* ttymsg() only sometimes returns an errno */
1016		if ((msgret = ttymsg(iov, 7, f->f_un.f_fname, 10))) {
1017			f->f_type = F_UNUSED;
1018			logerror(msgret);
1019		}
1020		break;
1021
1022	case F_USERS:
1023	case F_WALL:
1024		dprintf("\n");
1025		v->iov_base = "\r\n";
1026		v->iov_len = 2;
1027		wallmsg(f, iov);
1028		break;
1029	}
1030	f->f_prevcount = 0;
1031}
1032
1033/*
1034 *  WALLMSG -- Write a message to the world at large
1035 *
1036 *	Write the specified message to either the entire
1037 *	world, or a list of approved users.
1038 */
1039void
1040wallmsg(f, iov)
1041	struct filed *f;
1042	struct iovec *iov;
1043{
1044	static int reenter;			/* avoid calling ourselves */
1045	FILE *uf;
1046	struct utmp ut;
1047	int i;
1048	char *p;
1049	char line[sizeof(ut.ut_line) + 1];
1050
1051	if (reenter++)
1052		return;
1053	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
1054		logerror(_PATH_UTMP);
1055		reenter = 0;
1056		return;
1057	}
1058	/* NOSTRICT */
1059	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
1060		if (ut.ut_name[0] == '\0')
1061			continue;
1062		strncpy(line, ut.ut_line, sizeof(ut.ut_line));
1063		line[sizeof(ut.ut_line)] = '\0';
1064		if (f->f_type == F_WALL) {
1065			if ((p = ttymsg(iov, 7, line, TTYMSGTIME)) != NULL) {
1066				errno = 0;	/* already in msg */
1067				logerror(p);
1068			}
1069			continue;
1070		}
1071		/* should we send the message to this user? */
1072		for (i = 0; i < MAXUNAMES; i++) {
1073			if (!f->f_un.f_uname[i][0])
1074				break;
1075			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
1076			    UT_NAMESIZE)) {
1077				if ((p = ttymsg(iov, 7, line, TTYMSGTIME))
1078								!= NULL) {
1079					errno = 0;	/* already in msg */
1080					logerror(p);
1081				}
1082				break;
1083			}
1084		}
1085	}
1086	(void)fclose(uf);
1087	reenter = 0;
1088}
1089
1090void
1091reapchild(signo)
1092	int signo;
1093{
1094	int status;
1095	pid_t pid;
1096	struct filed *f;
1097
1098	while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
1099		if (!Initialized)
1100			/* Don't tell while we are initting. */
1101			continue;
1102
1103		/* First, look if it's a process from the dead queue. */
1104		if (deadq_remove(pid))
1105			goto oncemore;
1106
1107		/* Now, look in list of active processes. */
1108		for (f = Files; f; f = f->f_next)
1109			if (f->f_type == F_PIPE &&
1110			    f->f_un.f_pipe.f_pid == pid) {
1111				(void)close(f->f_file);
1112				f->f_un.f_pipe.f_pid = 0;
1113				log_deadchild(pid, status,
1114					      f->f_un.f_pipe.f_pname);
1115				break;
1116			}
1117	  oncemore:
1118		continue;
1119	}
1120}
1121
1122/*
1123 * Return a printable representation of a host address.
1124 */
1125char *
1126cvthname(f)
1127	struct sockaddr_in *f;
1128{
1129	struct hostent *hp;
1130	sigset_t omask, nmask;
1131	char *p;
1132
1133	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
1134
1135	if (f->sin_family != AF_INET) {
1136		dprintf("Malformed from address\n");
1137		return ("???");
1138	}
1139	sigemptyset(&nmask);
1140	sigaddset(&nmask, SIGHUP);
1141	sigprocmask(SIG_BLOCK, &nmask, &omask);
1142	hp = gethostbyaddr((char *)&f->sin_addr,
1143	    sizeof(struct in_addr), f->sin_family);
1144	sigprocmask(SIG_SETMASK, &omask, NULL);
1145	if (hp == 0) {
1146		dprintf("Host name for your address (%s) unknown\n",
1147			inet_ntoa(f->sin_addr));
1148		return (inet_ntoa(f->sin_addr));
1149	}
1150	if ((p = strchr(hp->h_name, '.')) &&
1151	    strcasecmp(p + 1, LocalDomain) == 0)
1152		*p = '\0';
1153	return (hp->h_name);
1154}
1155
1156void
1157domark(signo)
1158	int signo;
1159{
1160	struct filed *f;
1161	dq_t q;
1162
1163	now = time((time_t *)NULL);
1164	MarkSeq += TIMERINTVL;
1165	if (MarkSeq >= MarkInterval) {
1166		logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
1167		MarkSeq = 0;
1168	}
1169
1170	for (f = Files; f; f = f->f_next) {
1171		if (f->f_prevcount && now >= REPEATTIME(f)) {
1172			dprintf("flush %s: repeated %d times, %d sec.\n",
1173			    TypeNames[f->f_type], f->f_prevcount,
1174			    repeatinterval[f->f_repeatcount]);
1175			fprintlog(f, 0, (char *)NULL);
1176			BACKOFF(f);
1177		}
1178	}
1179
1180	/* Walk the dead queue, and see if we should signal somebody. */
1181	for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
1182		switch (q->dq_timeout) {
1183		case 0:
1184			/* Already signalled once, try harder now. */
1185			if (kill(q->dq_pid, SIGKILL) != 0)
1186				(void)deadq_remove(q->dq_pid);
1187			break;
1188
1189		case 1:
1190			/*
1191			 * Timed out on dead queue, send terminate
1192			 * signal.  Note that we leave the removal
1193			 * from the dead queue to reapchild(), which
1194			 * will also log the event (unless the process
1195			 * didn't even really exist, in case we simply
1196			 * drop it from the dead queue).
1197			 */
1198			if (kill(q->dq_pid, SIGTERM) != 0)
1199				(void)deadq_remove(q->dq_pid);
1200			/* FALLTHROUGH */
1201
1202		default:
1203			q->dq_timeout--;
1204		}
1205
1206	(void)alarm(TIMERINTVL);
1207}
1208
1209/*
1210 * Print syslogd errors some place.
1211 */
1212void
1213logerror(type)
1214	const char *type;
1215{
1216	char buf[512];
1217
1218	if (errno)
1219		(void)snprintf(buf,
1220		    sizeof buf, "syslogd: %s: %s", type, strerror(errno));
1221	else
1222		(void)snprintf(buf, sizeof buf, "syslogd: %s", type);
1223	errno = 0;
1224	dprintf("%s\n", buf);
1225	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
1226}
1227
1228void
1229die(signo)
1230	int signo;
1231{
1232	struct filed *f;
1233	int was_initialized;
1234	char buf[100];
1235	int i;
1236
1237	was_initialized = Initialized;
1238	Initialized = 0;	/* Don't log SIGCHLDs. */
1239	for (f = Files; f != NULL; f = f->f_next) {
1240		/* flush any pending output */
1241		if (f->f_prevcount)
1242			fprintlog(f, 0, (char *)NULL);
1243		if (f->f_type == F_PIPE)
1244			(void)close(f->f_file);
1245	}
1246	Initialized = was_initialized;
1247	if (signo) {
1248		dprintf("syslogd: exiting on signal %d\n", signo);
1249		(void)sprintf(buf, "exiting on signal %d", signo);
1250		errno = 0;
1251		logerror(buf);
1252	}
1253	for (i = 0; i < nfunix; i++)
1254		if (funixn[i] && funix[i] != -1)
1255			(void)unlink(funixn[i]);
1256	exit(1);
1257}
1258
1259/*
1260 *  INIT -- Initialize syslogd from configuration table
1261 */
1262void
1263init(signo)
1264	int signo;
1265{
1266	int i;
1267	FILE *cf;
1268	struct filed *f, *next, **nextp;
1269	char *p;
1270	char cline[LINE_MAX];
1271 	char prog[NAME_MAX+1];
1272	char host[MAXHOSTNAMELEN+1];
1273
1274	dprintf("init\n");
1275
1276	/*
1277	 *  Close all open log files.
1278	 */
1279	Initialized = 0;
1280	for (f = Files; f != NULL; f = next) {
1281		/* flush any pending output */
1282		if (f->f_prevcount)
1283			fprintlog(f, 0, (char *)NULL);
1284
1285		switch (f->f_type) {
1286		case F_FILE:
1287		case F_FORW:
1288		case F_CONSOLE:
1289		case F_TTY:
1290			(void)close(f->f_file);
1291			break;
1292		case F_PIPE:
1293			(void)close(f->f_file);
1294			if (f->f_un.f_pipe.f_pid > 0)
1295				deadq_enter(f->f_un.f_pipe.f_pid,
1296					    f->f_un.f_pipe.f_pname);
1297			f->f_un.f_pipe.f_pid = 0;
1298			break;
1299		}
1300		next = f->f_next;
1301		if (f->f_program) free(f->f_program);
1302		if (f->f_host) free(f->f_host);
1303		free((char *)f);
1304	}
1305	Files = NULL;
1306	nextp = &Files;
1307
1308	/* open the configuration file */
1309	if ((cf = fopen(ConfFile, "r")) == NULL) {
1310		dprintf("cannot open %s\n", ConfFile);
1311		*nextp = (struct filed *)calloc(1, sizeof(*f));
1312		cfline("*.ERR\t/dev/console", *nextp, "*", "*");
1313		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
1314		cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
1315		Initialized = 1;
1316		return;
1317	}
1318
1319	/*
1320	 *  Foreach line in the conf table, open that file.
1321	 */
1322	f = NULL;
1323	strcpy(host, "*");
1324	strcpy(prog, "*");
1325	while (fgets(cline, sizeof(cline), cf) != NULL) {
1326		/*
1327		 * check for end-of-section, comments, strip off trailing
1328		 * spaces and newline character. #!prog is treated specially:
1329		 * following lines apply only to that program.
1330		 */
1331		for (p = cline; isspace(*p); ++p)
1332			continue;
1333		if (*p == 0)
1334			continue;
1335		if (*p == '#') {
1336			p++;
1337			if (*p != '!' && *p != '+' && *p != '-')
1338				continue;
1339		}
1340		if (*p == '+' || *p == '-') {
1341			host[0] = *p++;
1342			while (isspace(*p)) p++;
1343			if ((!*p) || (*p == '*')) {
1344				strcpy(host, "*");
1345				continue;
1346			}
1347			if (*p == '@')
1348				p = LocalHostName;
1349			for (i = 1; i < MAXHOSTNAMELEN; i++) {
1350				if (!isalnum(*p) && *p != '.' && *p != '-')
1351					break;
1352				host[i] = *p++;
1353			}
1354			host[i] = '\0';
1355			continue;
1356		}
1357		if (*p == '!') {
1358			p++;
1359			while (isspace(*p)) p++;
1360			if ((!*p) || (*p == '*')) {
1361				strcpy(prog, "*");
1362				continue;
1363			}
1364			for (i = 0; i < NAME_MAX; i++) {
1365				if (!isalnum(p[i]))
1366					break;
1367				prog[i] = p[i];
1368			}
1369			prog[i] = 0;
1370			continue;
1371		}
1372		for (p = strchr(cline, '\0'); isspace(*--p);)
1373			continue;
1374		*++p = '\0';
1375		f = (struct filed *)calloc(1, sizeof(*f));
1376		*nextp = f;
1377		nextp = &f->f_next;
1378		cfline(cline, f, prog, host);
1379	}
1380
1381	/* close the configuration file */
1382	(void)fclose(cf);
1383
1384	Initialized = 1;
1385
1386	if (Debug) {
1387		for (f = Files; f; f = f->f_next) {
1388			for (i = 0; i <= LOG_NFACILITIES; i++)
1389				if (f->f_pmask[i] == INTERNAL_NOPRI)
1390					printf("X ");
1391				else
1392					printf("%d ", f->f_pmask[i]);
1393			printf("%s: ", TypeNames[f->f_type]);
1394			switch (f->f_type) {
1395			case F_FILE:
1396				printf("%s", f->f_un.f_fname);
1397				break;
1398
1399			case F_CONSOLE:
1400			case F_TTY:
1401				printf("%s%s", _PATH_DEV, f->f_un.f_fname);
1402				break;
1403
1404			case F_FORW:
1405				printf("%s", f->f_un.f_forw.f_hname);
1406				break;
1407
1408			case F_PIPE:
1409				printf("%s", f->f_un.f_pipe.f_pname);
1410				break;
1411
1412			case F_USERS:
1413				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1414					printf("%s, ", f->f_un.f_uname[i]);
1415				break;
1416			}
1417			if (f->f_program)
1418				printf(" (%s)", f->f_program);
1419			printf("\n");
1420		}
1421	}
1422
1423	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1424	dprintf("syslogd: restarted\n");
1425}
1426
1427/*
1428 * Crack a configuration file line
1429 */
1430void
1431cfline(line, f, prog, host)
1432	char *line;
1433	struct filed *f;
1434	char *prog;
1435	char *host;
1436{
1437	struct hostent *hp;
1438	int i, pri;
1439	char *bp, *p, *q;
1440	char buf[MAXLINE], ebuf[100];
1441
1442	dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
1443
1444	errno = 0;	/* keep strerror() stuff out of logerror messages */
1445
1446	/* clear out file entry */
1447	memset(f, 0, sizeof(*f));
1448	for (i = 0; i <= LOG_NFACILITIES; i++)
1449		f->f_pmask[i] = INTERNAL_NOPRI;
1450
1451	/* save hostname if any */
1452	if (host && *host == '*')
1453		host = NULL;
1454	if (host)
1455		f->f_host = strdup(host);
1456
1457	/* save program name if any */
1458	if (prog && *prog == '*')
1459		prog = NULL;
1460	if (prog)
1461		f->f_program = strdup(prog);
1462
1463	/* scan through the list of selectors */
1464	for (p = line; *p && *p != '\t' && *p != ' ';) {
1465		int pri_done;
1466		int pri_cmp;
1467
1468		/* find the end of this facility name list */
1469		for (q = p; *q && *q != '\t' && *q != ' ' && *q++ != '.'; )
1470			continue;
1471
1472		/* get the priority comparison */
1473		pri_cmp = 0;
1474		pri_done = 0;
1475		while (!pri_done) {
1476			switch (*q) {
1477			case '<':
1478				pri_cmp |= PRI_LT;
1479				q++;
1480				break;
1481			case '=':
1482				pri_cmp |= PRI_EQ;
1483				q++;
1484				break;
1485			case '>':
1486				pri_cmp |= PRI_GT;
1487				q++;
1488				break;
1489			default:
1490				pri_done++;
1491				break;
1492			}
1493		}
1494		if (!pri_cmp)
1495			pri_cmp = (UniquePriority)
1496				  ? (PRI_EQ)
1497				  : (PRI_EQ | PRI_GT)
1498				  ;
1499
1500		/* collect priority name */
1501		for (bp = buf; *q && !strchr("\t,; ", *q); )
1502			*bp++ = *q++;
1503		*bp = '\0';
1504
1505		/* skip cruft */
1506		while (strchr(",;", *q))
1507			q++;
1508
1509		/* decode priority name */
1510		if (*buf == '*')
1511			pri = LOG_PRIMASK + 1;
1512		else {
1513			pri = decode(buf, prioritynames);
1514			if (pri < 0) {
1515				(void)snprintf(ebuf, sizeof ebuf,
1516				    "unknown priority name \"%s\"", buf);
1517				logerror(ebuf);
1518				return;
1519			}
1520		}
1521
1522		/* scan facilities */
1523		while (*p && !strchr("\t.; ", *p)) {
1524			for (bp = buf; *p && !strchr("\t,;. ", *p); )
1525				*bp++ = *p++;
1526			*bp = '\0';
1527
1528			if (*buf == '*')
1529				for (i = 0; i < LOG_NFACILITIES; i++) {
1530					f->f_pmask[i] = pri;
1531					f->f_pcmp[i] = pri_cmp;
1532				}
1533			else {
1534				i = decode(buf, facilitynames);
1535				if (i < 0) {
1536					(void)snprintf(ebuf, sizeof ebuf,
1537					    "unknown facility name \"%s\"",
1538					    buf);
1539					logerror(ebuf);
1540					return;
1541				}
1542				f->f_pmask[i >> 3] = pri;
1543				f->f_pcmp[i >> 3] = pri_cmp;
1544			}
1545			while (*p == ',' || *p == ' ')
1546				p++;
1547		}
1548
1549		p = q;
1550	}
1551
1552	/* skip to action part */
1553	while (*p == '\t' || *p == ' ')
1554		p++;
1555
1556	switch (*p)
1557	{
1558	case '@':
1559		(void)strncpy(f->f_un.f_forw.f_hname, ++p,
1560			sizeof(f->f_un.f_forw.f_hname)-1);
1561		f->f_un.f_forw.f_hname[sizeof(f->f_un.f_forw.f_hname)-1] = '\0';
1562		hp = gethostbyname(f->f_un.f_forw.f_hname);
1563		if (hp == NULL) {
1564
1565			logerror(hstrerror(h_errno));
1566			break;
1567		}
1568		memset(&f->f_un.f_forw.f_addr, 0,
1569			 sizeof(f->f_un.f_forw.f_addr));
1570		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1571		f->f_un.f_forw.f_addr.sin_port = LogPort;
1572		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
1573		f->f_type = F_FORW;
1574		break;
1575
1576	case '/':
1577		if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1578			f->f_type = F_UNUSED;
1579			logerror(p);
1580			break;
1581		}
1582		if (isatty(f->f_file)) {
1583			if (strcmp(p, ctty) == 0)
1584				f->f_type = F_CONSOLE;
1585			else
1586				f->f_type = F_TTY;
1587			(void)strcpy(f->f_un.f_fname, p + sizeof _PATH_DEV - 1);
1588		} else {
1589			(void)strcpy(f->f_un.f_fname, p);
1590			f->f_type = F_FILE;
1591		}
1592		break;
1593
1594	case '|':
1595		f->f_un.f_pipe.f_pid = 0;
1596		(void)strcpy(f->f_un.f_pipe.f_pname, p + 1);
1597		f->f_type = F_PIPE;
1598		break;
1599
1600	case '*':
1601		f->f_type = F_WALL;
1602		break;
1603
1604	default:
1605		for (i = 0; i < MAXUNAMES && *p; i++) {
1606			for (q = p; *q && *q != ','; )
1607				q++;
1608			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1609			if ((q - p) > UT_NAMESIZE)
1610				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1611			else
1612				f->f_un.f_uname[i][q - p] = '\0';
1613			while (*q == ',' || *q == ' ')
1614				q++;
1615			p = q;
1616		}
1617		f->f_type = F_USERS;
1618		break;
1619	}
1620}
1621
1622
1623/*
1624 *  Decode a symbolic name to a numeric value
1625 */
1626int
1627decode(name, codetab)
1628	const char *name;
1629	CODE *codetab;
1630{
1631	CODE *c;
1632	char *p, buf[40];
1633
1634	if (isdigit(*name))
1635		return (atoi(name));
1636
1637	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1638		if (isupper(*name))
1639			*p = tolower(*name);
1640		else
1641			*p = *name;
1642	}
1643	*p = '\0';
1644	for (c = codetab; c->c_name; c++)
1645		if (!strcmp(buf, c->c_name))
1646			return (c->c_val);
1647
1648	return (-1);
1649}
1650
1651/*
1652 * fork off and become a daemon, but wait for the child to come online
1653 * before returing to the parent, or we get disk thrashing at boot etc.
1654 * Set a timer so we don't hang forever if it wedges.
1655 */
1656int
1657waitdaemon(nochdir, noclose, maxwait)
1658	int nochdir, noclose, maxwait;
1659{
1660	int fd;
1661	int status;
1662	pid_t pid, childpid;
1663
1664	switch (childpid = fork()) {
1665	case -1:
1666		return (-1);
1667	case 0:
1668		break;
1669	default:
1670		signal(SIGALRM, timedout);
1671		alarm(maxwait);
1672		while ((pid = wait3(&status, 0, NULL)) != -1) {
1673			if (WIFEXITED(status))
1674				errx(1, "child pid %d exited with return code %d",
1675					pid, WEXITSTATUS(status));
1676			if (WIFSIGNALED(status))
1677				errx(1, "child pid %d exited on signal %d%s",
1678					pid, WTERMSIG(status),
1679					WCOREDUMP(status) ? " (core dumped)" :
1680					"");
1681			if (pid == childpid)	/* it's gone... */
1682				break;
1683		}
1684		exit(0);
1685	}
1686
1687	if (setsid() == -1)
1688		return (-1);
1689
1690	if (!nochdir)
1691		(void)chdir("/");
1692
1693	if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
1694		(void)dup2(fd, STDIN_FILENO);
1695		(void)dup2(fd, STDOUT_FILENO);
1696		(void)dup2(fd, STDERR_FILENO);
1697		if (fd > 2)
1698			(void)close (fd);
1699	}
1700	return (getppid());
1701}
1702
1703/*
1704 * We get a SIGALRM from the child when it's running and finished doing it's
1705 * fsync()'s or O_SYNC writes for all the boot messages.
1706 *
1707 * We also get a signal from the kernel if the timer expires, so check to
1708 * see what happened.
1709 */
1710void
1711timedout(sig)
1712	int sig __unused;
1713{
1714	int left;
1715	left = alarm(0);
1716	signal(SIGALRM, SIG_DFL);
1717	if (left == 0)
1718		errx(1, "timed out waiting for child");
1719	else
1720		exit(0);
1721}
1722
1723/*
1724 * Add `s' to the list of allowable peer addresses to accept messages
1725 * from.
1726 *
1727 * `s' is a string in the form:
1728 *
1729 *    [*]domainname[:{servicename|portnumber|*}]
1730 *
1731 * or
1732 *
1733 *    netaddr/maskbits[:{servicename|portnumber|*}]
1734 *
1735 * Returns -1 on error, 0 if the argument was valid.
1736 */
1737int
1738allowaddr(s)
1739	char *s;
1740{
1741	char *cp1, *cp2;
1742	struct allowedpeer ap;
1743	struct servent *se;
1744	regex_t re;
1745	int i;
1746
1747	if ((cp1 = strrchr(s, ':'))) {
1748		/* service/port provided */
1749		*cp1++ = '\0';
1750		if (strlen(cp1) == 1 && *cp1 == '*')
1751			/* any port allowed */
1752			ap.port = htons(0);
1753		else if ((se = getservbyname(cp1, "udp")))
1754			ap.port = se->s_port;
1755		else {
1756			ap.port = htons((int)strtol(cp1, &cp2, 0));
1757			if (*cp2 != '\0')
1758				return -1; /* port not numeric */
1759		}
1760	} else {
1761		if ((se = getservbyname("syslog", "udp")))
1762			ap.port = se->s_port;
1763		else
1764			/* sanity, should not happen */
1765			ap.port = htons(514);
1766	}
1767
1768	/* the regexp's are ugly, but the cleanest way */
1769
1770	if (regcomp(&re, "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+(/[0-9]+)?$",
1771		    REG_EXTENDED))
1772		/* if RE compilation fails, that's an internal error */
1773		abort();
1774	if (regexec(&re, s, 0, 0, 0) == 0) {
1775		/* arg `s' is numeric */
1776		ap.isnumeric = 1;
1777		if ((cp1 = strchr(s, '/')) != NULL) {
1778			*cp1++ = '\0';
1779			i = atoi(cp1);
1780			if (i < 0 || i > 32)
1781				return -1;
1782			/* convert masklen to netmask */
1783			ap.a_mask.s_addr = htonl(~((1 << (32 - i)) - 1));
1784		}
1785		if (ascii2addr(AF_INET, s, &ap.a_addr) == -1)
1786			return -1;
1787		if (cp1 == NULL) {
1788			/* use default netmask */
1789			if (IN_CLASSA(ntohl(ap.a_addr.s_addr)))
1790				ap.a_mask.s_addr = htonl(IN_CLASSA_NET);
1791			else if (IN_CLASSB(ntohl(ap.a_addr.s_addr)))
1792				ap.a_mask.s_addr = htonl(IN_CLASSB_NET);
1793			else
1794				ap.a_mask.s_addr = htonl(IN_CLASSC_NET);
1795		}
1796	} else {
1797		/* arg `s' is domain name */
1798		ap.isnumeric = 0;
1799		ap.a_name = s;
1800	}
1801	regfree(&re);
1802
1803	if (Debug) {
1804		printf("allowaddr: rule %d: ", NumAllowed);
1805		if (ap.isnumeric) {
1806			printf("numeric, ");
1807			printf("addr = %s, ",
1808			       addr2ascii(AF_INET, &ap.a_addr, sizeof(struct in_addr), 0));
1809			printf("mask = %s; ",
1810			       addr2ascii(AF_INET, &ap.a_mask, sizeof(struct in_addr), 0));
1811		} else
1812			printf("domainname = %s; ", ap.a_name);
1813		printf("port = %d\n", ntohs(ap.port));
1814	}
1815
1816	if ((AllowedPeers = realloc(AllowedPeers,
1817				    ++NumAllowed * sizeof(struct allowedpeer)))
1818	    == NULL) {
1819		fprintf(stderr, "Out of memory!\n");
1820		exit(EX_OSERR);
1821	}
1822	memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
1823	return 0;
1824}
1825
1826/*
1827 * Validate that the remote peer has permission to log to us.
1828 */
1829int
1830validate(sin, hname)
1831	struct sockaddr_in *sin;
1832	const char *hname;
1833{
1834	int i;
1835	size_t l1, l2;
1836	char *cp, name[MAXHOSTNAMELEN];
1837	struct allowedpeer *ap;
1838
1839	if (NumAllowed == 0)
1840		/* traditional behaviour, allow everything */
1841		return 1;
1842
1843	strlcpy(name, hname, sizeof name);
1844	if (strchr(name, '.') == NULL) {
1845		strlcat(name, ".", sizeof name);
1846		strlcat(name, LocalDomain, sizeof name);
1847	}
1848	dprintf("validate: dgram from IP %s, port %d, name %s;\n",
1849		addr2ascii(AF_INET, &sin->sin_addr, sizeof(struct in_addr), 0),
1850		ntohs(sin->sin_port), name);
1851
1852	/* now, walk down the list */
1853	for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
1854		if (ntohs(ap->port) != 0 && ap->port != sin->sin_port) {
1855			dprintf("rejected in rule %d due to port mismatch.\n", i);
1856			continue;
1857		}
1858
1859		if (ap->isnumeric) {
1860			if ((sin->sin_addr.s_addr & ap->a_mask.s_addr)
1861			    != ap->a_addr.s_addr) {
1862				dprintf("rejected in rule %d due to IP mismatch.\n", i);
1863				continue;
1864			}
1865		} else {
1866			cp = ap->a_name;
1867			l1 = strlen(name);
1868			if (*cp == '*') {
1869				/* allow wildmatch */
1870				cp++;
1871				l2 = strlen(cp);
1872				if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) {
1873					dprintf("rejected in rule %d due to name mismatch.\n", i);
1874					continue;
1875				}
1876			} else {
1877				/* exact match */
1878				l2 = strlen(cp);
1879				if (l2 != l1 || memcmp(cp, name, l1) != 0) {
1880					dprintf("rejected in rule %d due to name mismatch.\n", i);
1881					continue;
1882				}
1883			}
1884		}
1885		dprintf("accepted in rule %d.\n", i);
1886		return 1;	/* hooray! */
1887	}
1888	return 0;
1889}
1890
1891/*
1892 * Fairly similar to popen(3), but returns an open descriptor, as
1893 * opposed to a FILE *.
1894 */
1895int
1896p_open(prog, pid)
1897	char *prog;
1898	pid_t *pid;
1899{
1900	int pfd[2], nulldesc, i;
1901	sigset_t omask, mask;
1902	char *argv[4]; /* sh -c cmd NULL */
1903	char errmsg[200];
1904
1905	if (pipe(pfd) == -1)
1906		return -1;
1907	if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1)
1908		/* we are royally screwed anyway */
1909		return -1;
1910
1911	sigemptyset(&mask);
1912	sigaddset(&mask, SIGALRM);
1913	sigaddset(&mask, SIGHUP);
1914	sigprocmask(SIG_BLOCK, &mask, &omask);
1915	switch ((*pid = fork())) {
1916	case -1:
1917		sigprocmask(SIG_SETMASK, &omask, 0);
1918		close(nulldesc);
1919		return -1;
1920
1921	case 0:
1922		argv[0] = "sh";
1923		argv[1] = "-c";
1924		argv[2] = prog;
1925		argv[3] = NULL;
1926
1927		alarm(0);
1928		(void)setsid();	/* Avoid catching SIGHUPs. */
1929
1930		/*
1931		 * Throw away pending signals, and reset signal
1932		 * behaviour to standard values.
1933		 */
1934		signal(SIGALRM, SIG_IGN);
1935		signal(SIGHUP, SIG_IGN);
1936		sigprocmask(SIG_SETMASK, &omask, 0);
1937		signal(SIGPIPE, SIG_DFL);
1938		signal(SIGQUIT, SIG_DFL);
1939		signal(SIGALRM, SIG_DFL);
1940		signal(SIGHUP, SIG_DFL);
1941
1942		dup2(pfd[0], STDIN_FILENO);
1943		dup2(nulldesc, STDOUT_FILENO);
1944		dup2(nulldesc, STDERR_FILENO);
1945		for (i = getdtablesize(); i > 2; i--)
1946			(void) close(i);
1947
1948		(void) execvp(_PATH_BSHELL, argv);
1949		_exit(255);
1950	}
1951
1952	sigprocmask(SIG_SETMASK, &omask, 0);
1953	close(nulldesc);
1954	close(pfd[0]);
1955	/*
1956	 * Avoid blocking on a hung pipe.  With O_NONBLOCK, we are
1957	 * supposed to get an EWOULDBLOCK on writev(2), which is
1958	 * caught by the logic above anyway, which will in turn close
1959	 * the pipe, and fork a new logging subprocess if necessary.
1960	 * The stale subprocess will be killed some time later unless
1961	 * it terminated itself due to closing its input pipe (so we
1962	 * get rid of really dead puppies).
1963	 */
1964	if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
1965		/* This is bad. */
1966		(void)snprintf(errmsg, sizeof errmsg,
1967			       "Warning: cannot change pipe to PID %d to "
1968			       "non-blocking behaviour.",
1969			       (int)*pid);
1970		logerror(errmsg);
1971	}
1972	return pfd[1];
1973}
1974
1975void
1976deadq_enter(pid, name)
1977	pid_t pid;
1978	const char *name;
1979{
1980	dq_t p;
1981	int status;
1982
1983	/*
1984	 * Be paranoid, if we can't signal the process, don't enter it
1985	 * into the dead queue (perhaps it's already dead).  If possible,
1986	 * we try to fetch and log the child's status.
1987	 */
1988	if (kill(pid, 0) != 0) {
1989		if (waitpid(pid, &status, WNOHANG) > 0)
1990			log_deadchild(pid, status, name);
1991		return;
1992	}
1993
1994	p = malloc(sizeof(struct deadq_entry));
1995	if (p == 0) {
1996		errno = 0;
1997		logerror("panic: out of virtual memory!");
1998		exit(1);
1999	}
2000
2001	p->dq_pid = pid;
2002	p->dq_timeout = DQ_TIMO_INIT;
2003	TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
2004}
2005
2006int
2007deadq_remove(pid)
2008	pid_t pid;
2009{
2010	dq_t q;
2011
2012	for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
2013		if (q->dq_pid == pid) {
2014			TAILQ_REMOVE(&deadq_head, q, dq_entries);
2015				free(q);
2016				return 1;
2017		}
2018
2019	return 0;
2020}
2021
2022void
2023log_deadchild(pid, status, name)
2024	pid_t pid;
2025	int status;
2026	const char *name;
2027{
2028	int code;
2029	char buf[256];
2030	const char *reason;
2031
2032	errno = 0; /* Keep strerror() stuff out of logerror messages. */
2033	if (WIFSIGNALED(status)) {
2034		reason = "due to signal";
2035		code = WTERMSIG(status);
2036	} else {
2037		reason = "with status";
2038		code = WEXITSTATUS(status);
2039		if (code == 0)
2040			return;
2041	}
2042	(void)snprintf(buf, sizeof buf,
2043		       "Logging subprocess %d (%s) exited %s %d.",
2044		       pid, name, reason, code);
2045	logerror(buf);
2046}
2047