syslogd.c revision 25155
1/*
2 * Copyright (c) 1983, 1988, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static const char copyright[] =
36"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38/*
39static char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";
40*/
41static const char rcsid[] =
42	"$Id: syslogd.c,v 1.23 1997/04/26 00:00:33 pst Exp $";
43#endif /* not lint */
44
45/*
46 *  syslogd -- log system messages
47 *
48 * This program implements a system log. It takes a series of lines.
49 * Each line may have a priority, signified as "<n>" as
50 * the first characters of the line.  If this is
51 * not present, a default priority is used.
52 *
53 * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
54 * cause it to reread its configuration file.
55 *
56 * Defined Constants:
57 *
58 * MAXLINE -- the maximimum line length that can be handled.
59 * DEFUPRI -- the default priority for user messages
60 * DEFSPRI -- the default priority for kernel messages
61 *
62 * Author: Eric Allman
63 * extensive changes by Ralph Campbell
64 * more extensive changes by Eric Allman (again)
65 * Extension to log by program name as well as facility and priority
66 *   by Peter da Silva.
67 */
68
69#define	MAXLINE		1024		/* maximum line length */
70#define	MAXSVLINE	120		/* maximum saved line length */
71#define DEFUPRI		(LOG_USER|LOG_NOTICE)
72#define DEFSPRI		(LOG_KERN|LOG_CRIT)
73#define TIMERINTVL	30		/* interval for checking flush, mark */
74#define TTYMSGTIME	1		/* timed out passed to ttymsg */
75
76#include <sys/param.h>
77#include <sys/ioctl.h>
78#include <sys/stat.h>
79#include <sys/wait.h>
80#include <sys/socket.h>
81#include <sys/msgbuf.h>
82#include <sys/queue.h>
83#include <sys/uio.h>
84#include <sys/un.h>
85#include <sys/time.h>
86#include <sys/resource.h>
87#include <sys/syslimits.h>
88#include <paths.h>
89
90#include <netinet/in.h>
91#include <netdb.h>
92#include <arpa/inet.h>
93
94#include <ctype.h>
95#include <errno.h>
96#include <fcntl.h>
97#include <setjmp.h>
98#include <signal.h>
99#include <stdio.h>
100#include <stdlib.h>
101#include <string.h>
102#include <unistd.h>
103#include <utmp.h>
104#include "pathnames.h"
105
106#define SYSLOG_NAMES
107#include <sys/syslog.h>
108
109const char	*LogName = _PATH_LOG;
110const char	*ConfFile = _PATH_LOGCONF;
111const char	*PidFile = _PATH_LOGPID;
112const char	ctty[] = _PATH_CONSOLE;
113
114#define FDMASK(fd)	(1 << (fd))
115
116#define	dprintf		if (Debug) printf
117
118#define MAXUNAMES	20	/* maximum number of user names */
119
120/*
121 * Flags to logmsg().
122 */
123
124#define IGN_CONS	0x001	/* don't print on console */
125#define SYNC_FILE	0x002	/* do fsync on file after printing */
126#define ADDDATE		0x004	/* add a date to the message */
127#define MARK		0x008	/* this message is a mark */
128
129/*
130 * This structure represents the files that will have log
131 * copies printed.
132 */
133
134struct filed {
135	struct	filed *f_next;		/* next in linked list */
136	short	f_type;			/* entry type, see below */
137	short	f_file;			/* file descriptor */
138	time_t	f_time;			/* time this was last written */
139	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
140	char	*f_program;		/* program this applies to */
141	union {
142		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
143		struct {
144			char	f_hname[MAXHOSTNAMELEN+1];
145			struct sockaddr_in	f_addr;
146		} f_forw;		/* forwarding address */
147		char	f_fname[MAXPATHLEN];
148		struct {
149			char	f_pname[MAXPATHLEN];
150			pid_t	f_pid;
151		} f_pipe;
152	} f_un;
153	char	f_prevline[MAXSVLINE];		/* last message logged */
154	char	f_lasttime[16];			/* time of last occurrence */
155	char	f_prevhost[MAXHOSTNAMELEN+1];	/* host from which recd. */
156	int	f_prevpri;			/* pri of f_prevline */
157	int	f_prevlen;			/* length of f_prevline */
158	int	f_prevcount;			/* repetition cnt of prevline */
159	int	f_repeatcount;			/* number of "repeated" msgs */
160};
161
162/*
163 * Queue of about-to-be dead processes we should watch out for.
164 */
165
166TAILQ_HEAD(stailhead, deadq_entry) deadq_head;
167struct stailhead *deadq_headp;
168
169struct deadq_entry {
170	pid_t				dq_pid;
171	int				dq_timeout;
172	TAILQ_ENTRY(deadq_entry)	dq_entries;
173};
174
175/*
176 * The timeout to apply to processes waiting on the dead queue.  Unit
177 * of measure is `mark intervals', i.e. 20 minutes by default.
178 * Processes on the dead queue will be terminated after that time.
179 */
180
181#define DQ_TIMO_INIT	2
182
183typedef struct deadq_entry *dq_t;
184
185
186/*
187 * Intervals at which we flush out "message repeated" messages,
188 * in seconds after previous message is logged.  After each flush,
189 * we move to the next interval until we reach the largest.
190 */
191int	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
192#define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
193#define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
194#define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
195				 (f)->f_repeatcount = MAXREPEAT; \
196			}
197
198/* values for f_type */
199#define F_UNUSED	0		/* unused entry */
200#define F_FILE		1		/* regular file */
201#define F_TTY		2		/* terminal */
202#define F_CONSOLE	3		/* console terminal */
203#define F_FORW		4		/* remote machine */
204#define F_USERS		5		/* list of users */
205#define F_WALL		6		/* everyone logged on */
206#define F_PIPE		7		/* pipe to program */
207
208char	*TypeNames[8] = {
209	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
210	"FORW",		"USERS",	"WALL",		"PIPE"
211};
212
213struct	filed *Files;
214struct	filed consfile;
215
216int	Debug;			/* debug flag */
217char	LocalHostName[MAXHOSTNAMELEN+1];	/* our hostname */
218char	*LocalDomain;		/* our local domain name */
219int	finet;			/* Internet datagram socket */
220int	LogPort;		/* port number for INET connections */
221int	Initialized = 0;	/* set when we have initialized ourselves */
222int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
223int	MarkSeq = 0;		/* mark sequence number */
224int	SecureMode = 0;		/* when true, speak only unix domain socks */
225
226int     created_lsock = 0;      /* Flag if local socket created */
227char	bootfile[MAXLINE+1];	/* booted kernel file */
228
229void	cfline __P((char *, struct filed *, char *));
230char   *cvthname __P((struct sockaddr_in *));
231void	deadq_enter __P((pid_t));
232int	decode __P((const char *, CODE *));
233void	die __P((int));
234void	domark __P((int));
235void	fprintlog __P((struct filed *, int, char *));
236void	init __P((int));
237void	logerror __P((const char *));
238void	logmsg __P((int, char *, char *, int));
239void	printline __P((char *, char *));
240void	printsys __P((char *));
241int	p_open __P((char *, pid_t *));
242void	reapchild __P((int));
243char   *ttymsg __P((struct iovec *, int, char *, int));
244void	usage __P((void));
245void	wallmsg __P((struct filed *, struct iovec *));
246int	waitdaemon __P((int, int, int));
247void	timedout __P((int));
248
249int
250main(argc, argv)
251	int argc;
252	char *argv[];
253{
254	int ch, funix, i, inetm, fklog, klogm, len;
255	struct sockaddr_un sunx, fromunix;
256	struct sockaddr_in sin, frominet;
257	FILE *fp;
258	char *p, line[MSG_BSIZE + 1];
259	struct timeval tv, *tvp;
260	pid_t ppid;
261
262	while ((ch = getopt(argc, argv, "dsf:m:p:")) != -1)
263		switch(ch) {
264		case 'd':		/* debug */
265			Debug++;
266			break;
267		case 'f':		/* configuration file */
268			ConfFile = optarg;
269			break;
270		case 'm':		/* mark interval */
271			MarkInterval = atoi(optarg) * 60;
272			break;
273		case 'p':		/* path */
274			LogName = optarg;
275			break;
276		case 's':		/* no network mode */
277			SecureMode++;
278			break;
279		case '?':
280		default:
281			usage();
282		}
283	if ((argc -= optind) != 0)
284		usage();
285
286	if (!Debug) {
287		ppid = waitdaemon(0, 0, 30);
288		if (ppid < 0)
289			err(1, "could not become daemon");
290	} else
291		setlinebuf(stdout);
292
293	consfile.f_type = F_CONSOLE;
294	(void)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1);
295	(void)gethostname(LocalHostName, sizeof(LocalHostName));
296	if ((p = strchr(LocalHostName, '.')) != NULL) {
297		*p++ = '\0';
298		LocalDomain = p;
299	} else
300		LocalDomain = "";
301	(void)strcpy(bootfile, getbootfile());
302	(void)signal(SIGTERM, die);
303	(void)signal(SIGINT, Debug ? die : SIG_IGN);
304	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
305	(void)signal(SIGCHLD, reapchild);
306	(void)signal(SIGALRM, domark);
307	(void)signal(SIGPIPE, SIG_IGN);	/* We'll catch EPIPE instead. */
308	(void)alarm(TIMERINTVL);
309
310	TAILQ_INIT(&deadq_head);
311
312#ifndef SUN_LEN
313#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
314#endif
315	memset(&sunx, 0, sizeof(sunx));
316	sunx.sun_family = AF_UNIX;
317	(void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
318	(void)unlink(LogName);
319	funix = socket(AF_UNIX, SOCK_DGRAM, 0);
320	if (funix < 0 ||
321	    bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
322	    chmod(LogName, 0666) < 0) {
323		(void) sprintf(line, "cannot create %s", LogName);
324		logerror(line);
325		dprintf("cannot create %s (%d)\n", LogName, errno);
326		die(0);
327	} else
328		created_lsock = 1;
329
330	inetm = 0;
331	finet = socket(AF_INET, SOCK_DGRAM, 0);
332	if (finet >= 0) {
333		struct servent *sp;
334
335		sp = getservbyname("syslog", "udp");
336		if (sp == NULL) {
337			errno = 0;
338			logerror("syslog/udp: unknown service");
339			die(0);
340		}
341		memset(&sin, 0, sizeof(sin));
342		sin.sin_family = AF_INET;
343		sin.sin_port = LogPort = sp->s_port;
344
345		if (!SecureMode) {
346		    if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
347			    logerror("bind");
348			    if (!Debug)
349				    die(0);
350		    } else {
351			    inetm = FDMASK(finet);
352		    }
353		}
354	}
355	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
356		klogm = FDMASK(fklog);
357	else {
358		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
359		klogm = 0;
360	}
361
362	/* tuck my process id away */
363	fp = fopen(PidFile, "w");
364	if (fp != NULL) {
365		fprintf(fp, "%d\n", getpid());
366		(void) fclose(fp);
367	}
368
369	dprintf("off & running....\n");
370
371	init(0);
372	(void)signal(SIGHUP, init);
373
374	tvp = &tv;
375	tv.tv_sec = tv.tv_usec = 0;
376
377	for (;;) {
378		int nfds, readfds = FDMASK(funix) | inetm | klogm;
379
380		dprintf("readfds = %#x\n", readfds);
381		nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
382		    (fd_set *)NULL, tvp);
383		if (nfds == 0) {
384			if (tvp) {
385				tvp = NULL;
386				if (ppid != 1)
387					kill(ppid, SIGALRM);
388			}
389			continue;
390		}
391		if (nfds < 0) {
392			if (errno != EINTR)
393				logerror("select");
394			continue;
395		}
396		dprintf("got a message (%d, %#x)\n", nfds, readfds);
397		if (readfds & klogm) {
398			i = read(fklog, line, sizeof(line) - 1);
399			if (i > 0) {
400				line[i] = '\0';
401				printsys(line);
402			} else if (i < 0 && errno != EINTR) {
403				logerror("klog");
404				fklog = -1;
405				klogm = 0;
406			}
407		}
408		if (readfds & FDMASK(funix)) {
409			len = sizeof(fromunix);
410			i = recvfrom(funix, line, MAXLINE, 0,
411			    (struct sockaddr *)&fromunix, &len);
412			if (i > 0) {
413				line[i] = '\0';
414				printline(LocalHostName, line);
415			} else if (i < 0 && errno != EINTR)
416				logerror("recvfrom unix");
417		}
418		if (readfds & inetm) {
419			len = sizeof(frominet);
420			i = recvfrom(finet, line, MAXLINE, 0,
421			    (struct sockaddr *)&frominet, &len);
422			if (i > 0) {
423				line[i] = '\0';
424				printline(cvthname(&frominet), line);
425			} else if (i < 0 && errno != EINTR)
426				logerror("recvfrom inet");
427		}
428	}
429}
430
431void
432usage()
433{
434
435	fprintf(stderr,
436		"usage: syslogd [-ds] [-f conffile] [-m markinterval]"
437		" [-p logpath]\n");
438	exit(1);
439}
440
441/*
442 * Take a raw input line, decode the message, and print the message
443 * on the appropriate log files.
444 */
445void
446printline(hname, msg)
447	char *hname;
448	char *msg;
449{
450	int c, pri;
451	char *p, *q, line[MAXLINE + 1];
452
453	/* test for special codes */
454	pri = DEFUPRI;
455	p = msg;
456	if (*p == '<') {
457		pri = 0;
458		while (isdigit(*++p))
459			pri = 10 * pri + (*p - '0');
460		if (*p == '>')
461			++p;
462	}
463	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
464		pri = DEFUPRI;
465
466	/* don't allow users to log kernel messages */
467	if (LOG_FAC(pri) == LOG_KERN)
468		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
469
470	q = line;
471
472	while ((c = *p++ & 0177) != '\0' &&
473	    q < &line[sizeof(line) - 1])
474		if (iscntrl(c))
475			if (c == '\n')
476				*q++ = ' ';
477			else if (c == '\t')
478				*q++ = '\t';
479			else {
480				*q++ = '^';
481				*q++ = c ^ 0100;
482			}
483		else
484			*q++ = c;
485	*q = '\0';
486
487	logmsg(pri, line, hname, 0);
488}
489
490/*
491 * Take a raw input line from /dev/klog, split and format similar to syslog().
492 */
493void
494printsys(msg)
495	char *msg;
496{
497	int c, pri, flags;
498	char *lp, *p, *q, line[MAXLINE + 1];
499
500	(void)strcpy(line, bootfile);
501	(void)strcat(line, ": ");
502	lp = line + strlen(line);
503	for (p = msg; *p != '\0'; ) {
504		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
505		pri = DEFSPRI;
506		if (*p == '<') {
507			pri = 0;
508			while (isdigit(*++p))
509				pri = 10 * pri + (*p - '0');
510			if (*p == '>')
511				++p;
512		} else {
513			/* kernel printf's come out on console */
514			flags |= IGN_CONS;
515		}
516		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
517			pri = DEFSPRI;
518		q = lp;
519		while (*p != '\0' && (c = *p++) != '\n' &&
520		    q < &line[MAXLINE])
521			*q++ = c;
522		*q = '\0';
523		logmsg(pri, line, LocalHostName, flags);
524	}
525}
526
527time_t	now;
528
529/*
530 * Log a message to the appropriate log files, users, etc. based on
531 * the priority.
532 */
533void
534logmsg(pri, msg, from, flags)
535	int pri;
536	char *msg, *from;
537	int flags;
538{
539	struct filed *f;
540	int fac, msglen, omask, prilev;
541	char *timestamp;
542 	char prog[NAME_MAX+1];
543 	int i;
544
545	dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
546	    pri, flags, from, msg);
547
548	omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
549
550	/*
551	 * Check to see if msg looks non-standard.
552	 */
553	msglen = strlen(msg);
554	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
555	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
556		flags |= ADDDATE;
557
558	(void)time(&now);
559	if (flags & ADDDATE)
560		timestamp = ctime(&now) + 4;
561	else {
562		timestamp = msg;
563		msg += 16;
564		msglen -= 16;
565	}
566
567	/* skip leading blanks */
568	while(isspace(*msg)) {
569		msg++;
570		msglen--;
571	}
572
573	/* extract facility and priority level */
574	if (flags & MARK)
575		fac = LOG_NFACILITIES;
576	else
577		fac = LOG_FAC(pri);
578	prilev = LOG_PRI(pri);
579
580	/* extract program name */
581	for(i = 0; i < NAME_MAX; i++) {
582		if(!isalnum(msg[i]))
583			break;
584		prog[i] = msg[i];
585	}
586	prog[i] = 0;
587
588	/* log the message to the particular outputs */
589	if (!Initialized) {
590		f = &consfile;
591		f->f_file = open(ctty, O_WRONLY, 0);
592
593		if (f->f_file >= 0) {
594			fprintlog(f, flags, msg);
595			(void)close(f->f_file);
596		}
597		(void)sigsetmask(omask);
598		return;
599	}
600	for (f = Files; f; f = f->f_next) {
601		/* skip messages that are incorrect priority */
602		if (f->f_pmask[fac] < prilev ||
603		    f->f_pmask[fac] == INTERNAL_NOPRI)
604			continue;
605		/* skip messages with the incorrect program name */
606		if(f->f_program)
607			if(strcmp(prog, f->f_program) != 0)
608				continue;
609
610		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
611			continue;
612
613		/* don't output marks to recently written files */
614		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
615			continue;
616
617		/*
618		 * suppress duplicate lines to this file
619		 */
620		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
621		    !strcmp(msg, f->f_prevline) &&
622		    !strcmp(from, f->f_prevhost)) {
623			(void)strncpy(f->f_lasttime, timestamp, 15);
624			f->f_prevcount++;
625			dprintf("msg repeated %d times, %ld sec of %d\n",
626			    f->f_prevcount, now - f->f_time,
627			    repeatinterval[f->f_repeatcount]);
628			/*
629			 * If domark would have logged this by now,
630			 * flush it now (so we don't hold isolated messages),
631			 * but back off so we'll flush less often
632			 * in the future.
633			 */
634			if (now > REPEATTIME(f)) {
635				fprintlog(f, flags, (char *)NULL);
636				BACKOFF(f);
637			}
638		} else {
639			/* new line, save it */
640			if (f->f_prevcount)
641				fprintlog(f, 0, (char *)NULL);
642			f->f_repeatcount = 0;
643			f->f_prevpri = pri;
644			(void)strncpy(f->f_lasttime, timestamp, 15);
645			(void)strncpy(f->f_prevhost, from,
646					sizeof(f->f_prevhost));
647			if (msglen < MAXSVLINE) {
648				f->f_prevlen = msglen;
649				(void)strcpy(f->f_prevline, msg);
650				fprintlog(f, flags, (char *)NULL);
651			} else {
652				f->f_prevline[0] = 0;
653				f->f_prevlen = 0;
654				fprintlog(f, flags, msg);
655			}
656		}
657	}
658	(void)sigsetmask(omask);
659}
660
661void
662fprintlog(f, flags, msg)
663	struct filed *f;
664	int flags;
665	char *msg;
666{
667	struct iovec iov[6];
668	struct iovec *v;
669	int l;
670	char line[MAXLINE + 1], repbuf[80], greetings[200];
671	char *msgret;
672	dq_t q;
673
674	v = iov;
675	if (f->f_type == F_WALL) {
676		v->iov_base = greetings;
677		v->iov_len = sprintf(greetings,
678		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
679		    f->f_prevhost, ctime(&now));
680		v++;
681		v->iov_base = "";
682		v->iov_len = 0;
683		v++;
684	} else {
685		v->iov_base = f->f_lasttime;
686		v->iov_len = 15;
687		v++;
688		v->iov_base = " ";
689		v->iov_len = 1;
690		v++;
691	}
692	v->iov_base = f->f_prevhost;
693	v->iov_len = strlen(v->iov_base);
694	v++;
695	v->iov_base = " ";
696	v->iov_len = 1;
697	v++;
698
699	if (msg) {
700		v->iov_base = msg;
701		v->iov_len = strlen(msg);
702	} else if (f->f_prevcount > 1) {
703		v->iov_base = repbuf;
704		v->iov_len = sprintf(repbuf, "last message repeated %d times",
705		    f->f_prevcount);
706	} else {
707		v->iov_base = f->f_prevline;
708		v->iov_len = f->f_prevlen;
709	}
710	v++;
711
712	dprintf("Logging to %s", TypeNames[f->f_type]);
713	f->f_time = now;
714
715	switch (f->f_type) {
716	case F_UNUSED:
717		dprintf("\n");
718		break;
719
720	case F_FORW:
721		dprintf(" %s\n", f->f_un.f_forw.f_hname);
722		l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
723		    iov[0].iov_base, iov[4].iov_base);
724		if (l > MAXLINE)
725			l = MAXLINE;
726		if ((finet >= 0) &&
727		     (sendto(finet, line, l, 0,
728			     (struct sockaddr *)&f->f_un.f_forw.f_addr,
729			     sizeof(f->f_un.f_forw.f_addr)) != l)) {
730			int e = errno;
731			(void)close(f->f_file);
732			f->f_type = F_UNUSED;
733			errno = e;
734			logerror("sendto");
735		}
736		break;
737
738	case F_FILE:
739		dprintf(" %s\n", f->f_un.f_fname);
740		v->iov_base = "\n";
741		v->iov_len = 1;
742		if (writev(f->f_file, iov, 6) < 0) {
743			int e = errno;
744			(void)close(f->f_file);
745			f->f_type = F_UNUSED;
746			errno = e;
747			logerror(f->f_un.f_fname);
748		} else if (flags & SYNC_FILE)
749			(void)fsync(f->f_file);
750		break;
751
752	case F_PIPE:
753		dprintf(" %s\n", f->f_un.f_pipe.f_pname);
754		v->iov_base = "\n";
755		v->iov_len = 1;
756		if (f->f_un.f_pipe.f_pid == 0) {
757			if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
758						&f->f_un.f_pipe.f_pid)) < 0) {
759				f->f_type = F_UNUSED;
760				logerror(f->f_un.f_pipe.f_pname);
761				break;
762			}
763		}
764		if (writev(f->f_file, iov, 6) < 0) {
765			int e = errno;
766			(void)close(f->f_file);
767			if (f->f_un.f_pipe.f_pid > 0)
768				deadq_enter(f->f_un.f_pipe.f_pid);
769			f->f_un.f_pipe.f_pid = 0;
770			errno = e;
771			logerror(f->f_un.f_pipe.f_pname);
772		}
773		break;
774
775	case F_CONSOLE:
776		if (flags & IGN_CONS) {
777			dprintf(" (ignored)\n");
778			break;
779		}
780		/* FALLTHROUGH */
781
782	case F_TTY:
783		dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
784		v->iov_base = "\r\n";
785		v->iov_len = 2;
786
787		errno = 0;	/* ttymsg() only sometimes returns an errno */
788		if ((msgret = ttymsg(iov, 6, f->f_un.f_fname, 10))) {
789			f->f_type = F_UNUSED;
790			logerror(msgret);
791		}
792		break;
793
794	case F_USERS:
795	case F_WALL:
796		dprintf("\n");
797		v->iov_base = "\r\n";
798		v->iov_len = 2;
799		wallmsg(f, iov);
800		break;
801	}
802	f->f_prevcount = 0;
803}
804
805/*
806 *  WALLMSG -- Write a message to the world at large
807 *
808 *	Write the specified message to either the entire
809 *	world, or a list of approved users.
810 */
811void
812wallmsg(f, iov)
813	struct filed *f;
814	struct iovec *iov;
815{
816	static int reenter;			/* avoid calling ourselves */
817	FILE *uf;
818	struct utmp ut;
819	int i;
820	char *p;
821	char line[sizeof(ut.ut_line) + 1];
822
823	if (reenter++)
824		return;
825	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
826		logerror(_PATH_UTMP);
827		reenter = 0;
828		return;
829	}
830	/* NOSTRICT */
831	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
832		if (ut.ut_name[0] == '\0')
833			continue;
834		strncpy(line, ut.ut_line, sizeof(ut.ut_line));
835		line[sizeof(ut.ut_line)] = '\0';
836		if (f->f_type == F_WALL) {
837			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
838				errno = 0;	/* already in msg */
839				logerror(p);
840			}
841			continue;
842		}
843		/* should we send the message to this user? */
844		for (i = 0; i < MAXUNAMES; i++) {
845			if (!f->f_un.f_uname[i][0])
846				break;
847			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
848			    UT_NAMESIZE)) {
849				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
850								!= NULL) {
851					errno = 0;	/* already in msg */
852					logerror(p);
853				}
854				break;
855			}
856		}
857	}
858	(void)fclose(uf);
859	reenter = 0;
860}
861
862void
863reapchild(signo)
864	int signo;
865{
866	int status, code;
867	pid_t pid;
868	struct filed *f;
869	char buf[256];
870	const char *reason;
871	dq_t q;
872
873	while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
874		if (!Initialized)
875			/* Don't tell while we are initting. */
876			continue;
877
878		/* First, look if it's a process from the dead queue. */
879		for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
880			if (q->dq_pid == pid) {
881				TAILQ_REMOVE(&deadq_head, q, dq_entries);
882				free(q);
883				goto oncemore;
884			}
885
886		/* Now, look in list of active processes. */
887		for (f = Files; f; f = f->f_next)
888			if (f->f_type == F_PIPE &&
889			    f->f_un.f_pipe.f_pid == pid) {
890				(void)close(f->f_file);
891
892				errno = 0; /* Keep strerror() stuff out of logerror messages. */
893				f->f_un.f_pipe.f_pid = 0;
894				if (WIFSIGNALED(status)) {
895					reason = "due to signal";
896					code = WTERMSIG(status);
897				} else {
898					reason = "with status";
899					code = WEXITSTATUS(status);
900					if (code == 0)
901						goto oncemore; /* Exited OK. */
902				}
903				(void)snprintf(buf, sizeof buf,
904				"Logging subprocess %d (%s) exited %s %d.",
905					       pid, f->f_un.f_pipe.f_pname,
906					       reason, code);
907				logerror(buf);
908				break;
909			}
910	  oncemore:
911	}
912}
913
914/*
915 * Return a printable representation of a host address.
916 */
917char *
918cvthname(f)
919	struct sockaddr_in *f;
920{
921	struct hostent *hp;
922	char *p;
923
924	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
925
926	if (f->sin_family != AF_INET) {
927		dprintf("Malformed from address\n");
928		return ("???");
929	}
930	hp = gethostbyaddr((char *)&f->sin_addr,
931	    sizeof(struct in_addr), f->sin_family);
932	if (hp == 0) {
933		dprintf("Host name for your address (%s) unknown\n",
934			inet_ntoa(f->sin_addr));
935		return (inet_ntoa(f->sin_addr));
936	}
937	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
938		*p = '\0';
939	return (hp->h_name);
940}
941
942void
943domark(signo)
944	int signo;
945{
946	struct filed *f;
947	dq_t q;
948
949	now = time((time_t *)NULL);
950	MarkSeq += TIMERINTVL;
951	if (MarkSeq >= MarkInterval) {
952		logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
953		MarkSeq = 0;
954	}
955
956	for (f = Files; f; f = f->f_next) {
957		if (f->f_prevcount && now >= REPEATTIME(f)) {
958			dprintf("flush %s: repeated %d times, %d sec.\n",
959			    TypeNames[f->f_type], f->f_prevcount,
960			    repeatinterval[f->f_repeatcount]);
961			fprintlog(f, 0, (char *)NULL);
962			BACKOFF(f);
963		}
964	}
965
966	/* Walk the dead queue, and see if we should signal somebody. */
967	for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
968		switch (q->dq_timeout) {
969		case 0:
970			/* Already signalled once, try harder now. */
971			kill(q->dq_pid, SIGKILL);
972			break;
973
974		case 1:
975			/*
976			 * Timed out on dead queue, send terminate
977			 * signal.  Note that we leave the removal
978			 * from the dead queue to reapchild(), which
979			 * will also log the event.
980			 */
981			kill(q->dq_pid, SIGTERM);
982			/* FALLTROUGH */
983
984		default:
985			q->dq_timeout--;
986		}
987
988	(void)alarm(TIMERINTVL);
989}
990
991/*
992 * Print syslogd errors some place.
993 */
994void
995logerror(type)
996	const char *type;
997{
998	char buf[512];
999
1000	if (errno)
1001		(void)snprintf(buf,
1002		    sizeof(buf), "syslogd: %s: %s", type, strerror(errno));
1003	else
1004		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
1005	errno = 0;
1006	dprintf("%s\n", buf);
1007	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
1008}
1009
1010void
1011die(signo)
1012	int signo;
1013{
1014	struct filed *f;
1015	int was_initialized;
1016	char buf[100];
1017
1018	was_initialized = Initialized;
1019	Initialized = 0;	/* Don't log SIGCHLDs. */
1020	for (f = Files; f != NULL; f = f->f_next) {
1021		/* flush any pending output */
1022		if (f->f_prevcount)
1023			fprintlog(f, 0, (char *)NULL);
1024		if (f->f_type == F_PIPE)
1025			(void)close(f->f_file);
1026	}
1027	Initialized = was_initialized;
1028	if (signo) {
1029		dprintf("syslogd: exiting on signal %d\n", signo);
1030		(void)sprintf(buf, "exiting on signal %d", signo);
1031		errno = 0;
1032		logerror(buf);
1033	}
1034	if (created_lsock)
1035		(void)unlink(LogName);
1036	exit(1);
1037}
1038
1039/*
1040 *  INIT -- Initialize syslogd from configuration table
1041 */
1042void
1043init(signo)
1044	int signo;
1045{
1046	int i;
1047	FILE *cf;
1048	struct filed *f, *next, **nextp;
1049	char *p;
1050	char cline[LINE_MAX];
1051 	char prog[NAME_MAX+1];
1052
1053	dprintf("init\n");
1054
1055	/*
1056	 *  Close all open log files.
1057	 */
1058	Initialized = 0;
1059	for (f = Files; f != NULL; f = next) {
1060		/* flush any pending output */
1061		if (f->f_prevcount)
1062			fprintlog(f, 0, (char *)NULL);
1063
1064		switch (f->f_type) {
1065		case F_FILE:
1066		case F_FORW:
1067		case F_CONSOLE:
1068		case F_TTY:
1069			(void)close(f->f_file);
1070			break;
1071		case F_PIPE:
1072			(void)close(f->f_file);
1073			if (f->f_un.f_pipe.f_pid > 0)
1074				deadq_enter(f->f_un.f_pipe.f_pid);
1075			f->f_un.f_pipe.f_pid = 0;
1076			break;
1077		}
1078		next = f->f_next;
1079		if(f->f_program) free(f->f_program);
1080		free((char *)f);
1081	}
1082	Files = NULL;
1083	nextp = &Files;
1084
1085	/* open the configuration file */
1086	if ((cf = fopen(ConfFile, "r")) == NULL) {
1087		dprintf("cannot open %s\n", ConfFile);
1088		*nextp = (struct filed *)calloc(1, sizeof(*f));
1089		cfline("*.ERR\t/dev/console", *nextp, "*");
1090		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
1091		cfline("*.PANIC\t*", (*nextp)->f_next, "*");
1092		Initialized = 1;
1093		return;
1094	}
1095
1096	/*
1097	 *  Foreach line in the conf table, open that file.
1098	 */
1099	f = NULL;
1100	strcpy(prog, "*");
1101	while (fgets(cline, sizeof(cline), cf) != NULL) {
1102		/*
1103		 * check for end-of-section, comments, strip off trailing
1104		 * spaces and newline character. #!prog is treated specially:
1105		 * following lines apply only to that program.
1106		 */
1107		for (p = cline; isspace(*p); ++p)
1108			continue;
1109		if (*p == 0)
1110			continue;
1111		if(*p == '#') {
1112			p++;
1113			if(*p!='!')
1114				continue;
1115		}
1116		if(*p=='!') {
1117			p++;
1118			while(isspace(*p)) p++;
1119			if(!*p) {
1120				strcpy(prog, "*");
1121				continue;
1122			}
1123			for(i = 0; i < NAME_MAX; i++) {
1124				if(!isalnum(p[i]))
1125					break;
1126				prog[i] = p[i];
1127			}
1128			prog[i] = 0;
1129			continue;
1130		}
1131		for (p = strchr(cline, '\0'); isspace(*--p);)
1132			continue;
1133		*++p = '\0';
1134		f = (struct filed *)calloc(1, sizeof(*f));
1135		*nextp = f;
1136		nextp = &f->f_next;
1137		cfline(cline, f, prog);
1138	}
1139
1140	/* close the configuration file */
1141	(void)fclose(cf);
1142
1143	Initialized = 1;
1144
1145	if (Debug) {
1146		for (f = Files; f; f = f->f_next) {
1147			for (i = 0; i <= LOG_NFACILITIES; i++)
1148				if (f->f_pmask[i] == INTERNAL_NOPRI)
1149					printf("X ");
1150				else
1151					printf("%d ", f->f_pmask[i]);
1152			printf("%s: ", TypeNames[f->f_type]);
1153			switch (f->f_type) {
1154			case F_FILE:
1155				printf("%s", f->f_un.f_fname);
1156				break;
1157
1158			case F_CONSOLE:
1159			case F_TTY:
1160				printf("%s%s", _PATH_DEV, f->f_un.f_fname);
1161				break;
1162
1163			case F_FORW:
1164				printf("%s", f->f_un.f_forw.f_hname);
1165				break;
1166
1167			case F_PIPE:
1168				printf("%s", f->f_un.f_pipe.f_pname);
1169				break;
1170
1171			case F_USERS:
1172				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1173					printf("%s, ", f->f_un.f_uname[i]);
1174				break;
1175			}
1176			if(f->f_program) {
1177				printf(" (%s)", f->f_program);
1178			}
1179			printf("\n");
1180		}
1181	}
1182
1183	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1184	dprintf("syslogd: restarted\n");
1185}
1186
1187/*
1188 * Crack a configuration file line
1189 */
1190void
1191cfline(line, f, prog)
1192	char *line;
1193	struct filed *f;
1194	char *prog;
1195{
1196	struct hostent *hp;
1197	int i, pri;
1198	char *bp, *p, *q;
1199	char buf[MAXLINE], ebuf[100];
1200
1201	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1202
1203	errno = 0;	/* keep strerror() stuff out of logerror messages */
1204
1205	/* clear out file entry */
1206	memset(f, 0, sizeof(*f));
1207	for (i = 0; i <= LOG_NFACILITIES; i++)
1208		f->f_pmask[i] = INTERNAL_NOPRI;
1209
1210	/* save program name if any */
1211	if(prog && *prog=='*') prog = NULL;
1212	if(prog) {
1213		f->f_program = calloc(1, strlen(prog)+1);
1214		if(f->f_program) {
1215			strcpy(f->f_program, prog);
1216		}
1217	}
1218
1219	/* scan through the list of selectors */
1220	for (p = line; *p && *p != '\t';) {
1221
1222		/* find the end of this facility name list */
1223		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1224			continue;
1225
1226		/* collect priority name */
1227		for (bp = buf; *q && !strchr("\t,;", *q); )
1228			*bp++ = *q++;
1229		*bp = '\0';
1230
1231		/* skip cruft */
1232		while (strchr(", ;", *q))
1233			q++;
1234
1235		/* decode priority name */
1236		if (*buf == '*')
1237			pri = LOG_PRIMASK + 1;
1238		else {
1239			pri = decode(buf, prioritynames);
1240			if (pri < 0) {
1241				(void)sprintf(ebuf,
1242				    "unknown priority name \"%s\"", buf);
1243				logerror(ebuf);
1244				return;
1245			}
1246		}
1247
1248		/* scan facilities */
1249		while (*p && !strchr("\t.;", *p)) {
1250			for (bp = buf; *p && !strchr("\t,;.", *p); )
1251				*bp++ = *p++;
1252			*bp = '\0';
1253			if (*buf == '*')
1254				for (i = 0; i < LOG_NFACILITIES; i++)
1255					f->f_pmask[i] = pri;
1256			else {
1257				i = decode(buf, facilitynames);
1258				if (i < 0) {
1259					(void)sprintf(ebuf,
1260					    "unknown facility name \"%s\"",
1261					    buf);
1262					logerror(ebuf);
1263					return;
1264				}
1265				f->f_pmask[i >> 3] = pri;
1266			}
1267			while (*p == ',' || *p == ' ')
1268				p++;
1269		}
1270
1271		p = q;
1272	}
1273
1274	/* skip to action part */
1275	while (*p == '\t')
1276		p++;
1277
1278	switch (*p)
1279	{
1280	case '@':
1281		(void)strcpy(f->f_un.f_forw.f_hname, ++p);
1282		hp = gethostbyname(p);
1283		if (hp == NULL) {
1284			extern int h_errno;
1285
1286			logerror(hstrerror(h_errno));
1287			break;
1288		}
1289		memset(&f->f_un.f_forw.f_addr, 0,
1290			 sizeof(f->f_un.f_forw.f_addr));
1291		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1292		f->f_un.f_forw.f_addr.sin_port = LogPort;
1293		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
1294		f->f_type = F_FORW;
1295		break;
1296
1297	case '/':
1298		if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1299			f->f_type = F_UNUSED;
1300			logerror(p);
1301			break;
1302		}
1303		if (isatty(f->f_file)) {
1304			if (strcmp(p, ctty) == 0)
1305				f->f_type = F_CONSOLE;
1306			else
1307				f->f_type = F_TTY;
1308			(void)strcpy(f->f_un.f_fname, p + sizeof _PATH_DEV - 1);
1309		} else {
1310			(void)strcpy(f->f_un.f_fname, p);
1311			f->f_type = F_FILE;
1312		}
1313		break;
1314
1315	case '|':
1316		f->f_un.f_pipe.f_pid = 0;
1317		(void)strcpy(f->f_un.f_pipe.f_pname, p + 1);
1318		f->f_type = F_PIPE;
1319		break;
1320
1321	case '*':
1322		f->f_type = F_WALL;
1323		break;
1324
1325	default:
1326		for (i = 0; i < MAXUNAMES && *p; i++) {
1327			for (q = p; *q && *q != ','; )
1328				q++;
1329			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1330			if ((q - p) > UT_NAMESIZE)
1331				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1332			else
1333				f->f_un.f_uname[i][q - p] = '\0';
1334			while (*q == ',' || *q == ' ')
1335				q++;
1336			p = q;
1337		}
1338		f->f_type = F_USERS;
1339		break;
1340	}
1341}
1342
1343
1344/*
1345 *  Decode a symbolic name to a numeric value
1346 */
1347int
1348decode(name, codetab)
1349	const char *name;
1350	CODE *codetab;
1351{
1352	CODE *c;
1353	char *p, buf[40];
1354
1355	if (isdigit(*name))
1356		return (atoi(name));
1357
1358	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1359		if (isupper(*name))
1360			*p = tolower(*name);
1361		else
1362			*p = *name;
1363	}
1364	*p = '\0';
1365	for (c = codetab; c->c_name; c++)
1366		if (!strcmp(buf, c->c_name))
1367			return (c->c_val);
1368
1369	return (-1);
1370}
1371
1372/*
1373 * fork off and become a daemon, but wait for the child to come online
1374 * before returing to the parent, or we get disk thrashing at boot etc.
1375 * Set a timer so we don't hang forever if it wedges.
1376 */
1377int
1378waitdaemon(nochdir, noclose, maxwait)
1379	int nochdir, noclose, maxwait;
1380{
1381	int fd;
1382	int status;
1383	pid_t pid, childpid;
1384
1385	switch (childpid = fork()) {
1386	case -1:
1387		return (-1);
1388	case 0:
1389		break;
1390	default:
1391		signal(SIGALRM, timedout);
1392		alarm(maxwait);
1393		while ((pid = wait3(&status, 0, NULL)) != -1) {
1394			if (WIFEXITED(status))
1395				errx(1, "child pid %d exited with return code %d",
1396					pid, WEXITSTATUS(status));
1397			if (WIFSIGNALED(status))
1398				errx(1, "child pid %d exited on signal %d%s",
1399					pid, WTERMSIG(status),
1400					WCOREDUMP(status) ? " (core dumped)" :
1401					"");
1402			if (pid == childpid)	/* it's gone... */
1403				break;
1404		}
1405		exit(0);
1406	}
1407
1408	if (setsid() == -1)
1409		return (-1);
1410
1411	if (!nochdir)
1412		(void)chdir("/");
1413
1414	if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
1415		(void)dup2(fd, STDIN_FILENO);
1416		(void)dup2(fd, STDOUT_FILENO);
1417		(void)dup2(fd, STDERR_FILENO);
1418		if (fd > 2)
1419			(void)close (fd);
1420	}
1421	return (getppid());
1422}
1423
1424/*
1425 * We get a SIGALRM from the child when it's running and finished doing it's
1426 * fsync()'s or O_SYNC writes for all the boot messages.
1427 *
1428 * We also get a signal from the kernel if the timer expires, so check to
1429 * see what happened.
1430 */
1431void
1432timedout(sig)
1433	int sig __unused;
1434{
1435	int left;
1436	left = alarm(0);
1437	signal(SIGALRM, SIG_DFL);
1438	if (left == 0)
1439		errx(1, "timed out waiting for child");
1440	else
1441		exit(0);
1442}
1443
1444/*
1445 * Fairly similar to popen(3), but returns an open descriptor, as
1446 * opposed to a FILE *.
1447 */
1448int
1449p_open(prog, pid)
1450	char *prog;
1451	pid_t *pid;
1452{
1453	int pfd[2], nulldesc, i;
1454	sigset_t omask, mask;
1455	char *argv[4]; /* sh -c cmd NULL */
1456	char errmsg[200];
1457
1458	if (pipe(pfd) == -1)
1459		return -1;
1460	if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1)
1461		/* we are royally screwed anyway */
1462		return -1;
1463
1464	mask = sigmask(SIGALRM) | sigmask(SIGHUP);
1465	sigprocmask(SIG_BLOCK, &omask, &mask);
1466	switch ((*pid = fork())) {
1467	case -1:
1468		sigprocmask(SIG_SETMASK, 0, &omask);
1469		close(nulldesc);
1470		return -1;
1471
1472	case 0:
1473		argv[0] = "sh";
1474		argv[1] = "-c";
1475		argv[2] = prog;
1476		argv[3] = NULL;
1477
1478		alarm(0);
1479		(void)setsid();	/* Avoid catching SIGHUPs. */
1480
1481		/*
1482		 * Throw away pending signals, and reset signal
1483		 * behaviour to standard values.
1484		 */
1485		signal(SIGALRM, SIG_IGN);
1486		signal(SIGHUP, SIG_IGN);
1487		sigprocmask(SIG_SETMASK, 0, &omask);
1488		signal(SIGPIPE, SIG_DFL);
1489		signal(SIGQUIT, SIG_DFL);
1490		signal(SIGALRM, SIG_DFL);
1491		signal(SIGHUP, SIG_DFL);
1492
1493		dup2(pfd[0], STDIN_FILENO);
1494		dup2(nulldesc, STDOUT_FILENO);
1495		dup2(nulldesc, STDERR_FILENO);
1496		for (i = getdtablesize(); i > 2; i--)
1497			(void) close(i);
1498
1499		(void) execvp(_PATH_BSHELL, argv);
1500		_exit(255);
1501	}
1502
1503	sigprocmask(SIG_SETMASK, 0, &omask);
1504	close(nulldesc);
1505	close(pfd[0]);
1506	/*
1507	 * Avoid blocking on a hung pipe.  With O_NONBLOCK, we are
1508	 * supposed to get an EWOULDBLOCK on writev(2), which is
1509	 * caught by the logic above anyway, which will in turn close
1510	 * the pipe, and fork a new logging subprocess if necessary.
1511	 * The stale subprocess will be killed some time later unless
1512	 * it terminated itself due to closing its input pipe (so we
1513	 * get rid of really dead puppies).
1514	 */
1515	if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
1516		/* This is bad. */
1517		(void)snprintf(errmsg, sizeof errmsg,
1518			       "Warning: cannot change pipe to PID %d to "
1519			       "non-blocking behaviour.",
1520			       (int)*pid);
1521		logerror(errmsg);
1522	}
1523	return pfd[1];
1524}
1525
1526void
1527deadq_enter(pid)
1528	pid_t pid;
1529{
1530	dq_t p;
1531
1532	p = malloc(sizeof(struct deadq_entry));
1533	if (p == 0) {
1534		errno = 0;
1535		logerror("panic: out of virtual memory!");
1536		exit(1);
1537	}
1538
1539	p->dq_pid = pid;
1540	p->dq_timeout = DQ_TIMO_INIT;
1541	TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
1542}
1543