syslogd.c revision 19854
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.12 1996/10/28 08:25:13 joerg 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/uio.h>
83#include <sys/un.h>
84#include <sys/time.h>
85#include <sys/resource.h>
86#include <sys/syslimits.h>
87#include <paths.h>
88
89#include <netinet/in.h>
90#include <netdb.h>
91#include <arpa/inet.h>
92
93#include <ctype.h>
94#include <errno.h>
95#include <fcntl.h>
96#include <setjmp.h>
97#include <signal.h>
98#include <stdio.h>
99#include <stdlib.h>
100#include <string.h>
101#include <unistd.h>
102#include <utmp.h>
103#include "pathnames.h"
104
105#define SYSLOG_NAMES
106#include <sys/syslog.h>
107
108const char	*LogName = _PATH_LOG;
109const char	*ConfFile = _PATH_LOGCONF;
110const char	*PidFile = _PATH_LOGPID;
111const char	ctty[] = _PATH_CONSOLE;
112
113#define FDMASK(fd)	(1 << (fd))
114
115#define	dprintf		if (Debug) printf
116
117#define MAXUNAMES	20	/* maximum number of user names */
118
119/*
120 * Flags to logmsg().
121 */
122
123#define IGN_CONS	0x001	/* don't print on console */
124#define SYNC_FILE	0x002	/* do fsync on file after printing */
125#define ADDDATE		0x004	/* add a date to the message */
126#define MARK		0x008	/* this message is a mark */
127
128/*
129 * This structure represents the files that will have log
130 * copies printed.
131 */
132
133struct filed {
134	struct	filed *f_next;		/* next in linked list */
135	short	f_type;			/* entry type, see below */
136	short	f_file;			/* file descriptor */
137	time_t	f_time;			/* time this was last written */
138	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
139	char	*f_program;		/* program this applies to */
140	union {
141		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
142		struct {
143			char	f_hname[MAXHOSTNAMELEN+1];
144			struct sockaddr_in	f_addr;
145		} f_forw;		/* forwarding address */
146		char	f_fname[MAXPATHLEN];
147	} f_un;
148	char	f_prevline[MAXSVLINE];		/* last message logged */
149	char	f_lasttime[16];			/* time of last occurrence */
150	char	f_prevhost[MAXHOSTNAMELEN+1];	/* host from which recd. */
151	int	f_prevpri;			/* pri of f_prevline */
152	int	f_prevlen;			/* length of f_prevline */
153	int	f_prevcount;			/* repetition cnt of prevline */
154	int	f_repeatcount;			/* number of "repeated" msgs */
155};
156
157/*
158 * Intervals at which we flush out "message repeated" messages,
159 * in seconds after previous message is logged.  After each flush,
160 * we move to the next interval until we reach the largest.
161 */
162int	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
163#define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
164#define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
165#define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
166				 (f)->f_repeatcount = MAXREPEAT; \
167			}
168
169/* values for f_type */
170#define F_UNUSED	0		/* unused entry */
171#define F_FILE		1		/* regular file */
172#define F_TTY		2		/* terminal */
173#define F_CONSOLE	3		/* console terminal */
174#define F_FORW		4		/* remote machine */
175#define F_USERS		5		/* list of users */
176#define F_WALL		6		/* everyone logged on */
177
178char	*TypeNames[7] = {
179	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
180	"FORW",		"USERS",	"WALL"
181};
182
183struct	filed *Files;
184struct	filed consfile;
185
186int	Debug;			/* debug flag */
187char	LocalHostName[MAXHOSTNAMELEN+1];	/* our hostname */
188char	*LocalDomain;		/* our local domain name */
189int	InetInuse = 0;		/* non-zero if INET sockets are being used */
190int	finet;			/* Internet datagram socket */
191int	LogPort;		/* port number for INET connections */
192int	Initialized = 0;	/* set when we have initialized ourselves */
193int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
194int	MarkSeq = 0;		/* mark sequence number */
195int	SecureMode = 0;		/* when true, speak only unix domain socks */
196
197int     created_lsock = 0;      /* Flag if local socket created */
198char	bootfile[MAXLINE+1];	/* booted kernel file */
199
200void	cfline __P((char *, struct filed *, char *));
201char   *cvthname __P((struct sockaddr_in *));
202int	decode __P((const char *, CODE *));
203void	die __P((int));
204void	domark __P((int));
205void	fprintlog __P((struct filed *, int, char *));
206void	init __P((int));
207void	logerror __P((const char *));
208void	logmsg __P((int, char *, char *, int));
209void	printline __P((char *, char *));
210void	printsys __P((char *));
211void	reapchild __P((int));
212char   *ttymsg __P((struct iovec *, int, char *, int));
213void	usage __P((void));
214void	wallmsg __P((struct filed *, struct iovec *));
215int	waitdaemon __P((int, int, int));
216void	timedout __P((int));
217
218int
219main(argc, argv)
220	int argc;
221	char *argv[];
222{
223	int ch, funix, i, inetm, fklog, klogm, len;
224	struct sockaddr_un sunx, fromunix;
225	struct sockaddr_in sin, frominet;
226	FILE *fp;
227	char *p, line[MSG_BSIZE + 1];
228	struct timeval tv, *tvp;
229	pid_t ppid;
230
231	while ((ch = getopt(argc, argv, "dsf:Im:p:")) != EOF)
232		switch(ch) {
233		case 'd':		/* debug */
234			Debug++;
235			break;
236		case 'f':		/* configuration file */
237			ConfFile = optarg;
238			break;
239		case 'm':		/* mark interval */
240			MarkInterval = atoi(optarg) * 60;
241			break;
242		case 'p':		/* path */
243			LogName = optarg;
244			break;
245		case 'I':		/* backwards compatible w/FreeBSD */
246		case 's':		/* no network mode */
247			SecureMode++;
248			break;
249		case '?':
250		default:
251			usage();
252		}
253	if ((argc -= optind) != 0)
254		usage();
255
256	if (!Debug) {
257		ppid = waitdaemon(0, 0, 30);
258		if (ppid < 0)
259			err(1, "could not become daemon");
260	} else
261		setlinebuf(stdout);
262
263	consfile.f_type = F_CONSOLE;
264	(void)strcpy(consfile.f_un.f_fname, ctty);
265	(void)gethostname(LocalHostName, sizeof(LocalHostName));
266	if ((p = strchr(LocalHostName, '.')) != NULL) {
267		*p++ = '\0';
268		LocalDomain = p;
269	} else
270		LocalDomain = "";
271	(void)strcpy(bootfile, getbootfile());
272	(void)signal(SIGTERM, die);
273	(void)signal(SIGINT, Debug ? die : SIG_IGN);
274	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
275	(void)signal(SIGCHLD, reapchild);
276	(void)signal(SIGALRM, domark);
277	(void)alarm(TIMERINTVL);
278
279#ifndef SUN_LEN
280#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
281#endif
282	memset(&sunx, 0, sizeof(sunx));
283	sunx.sun_family = AF_UNIX;
284	(void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
285	funix = socket(AF_UNIX, SOCK_DGRAM, 0);
286	if (funix < 0 ||
287	    bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
288	    chmod(LogName, 0666) < 0) {
289		(void) sprintf(line, "cannot create %s", LogName);
290		logerror(line);
291		dprintf("cannot create %s (%d)\n", LogName, errno);
292		die(0);
293	} else
294		created_lsock = 1;
295
296	if (!SecureMode)
297		finet = socket(AF_INET, SOCK_DGRAM, 0);
298	else
299		finet = -1;
300
301	inetm = 0;
302	if (finet >= 0) {
303		struct servent *sp;
304
305		sp = getservbyname("syslog", "udp");
306		if (sp == NULL) {
307			errno = 0;
308			logerror("syslog/udp: unknown service");
309			die(0);
310		}
311		memset(&sin, 0, sizeof(sin));
312		sin.sin_family = AF_INET;
313		sin.sin_port = LogPort = sp->s_port;
314		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
315			logerror("bind");
316			if (!Debug)
317				die(0);
318		} else {
319			inetm = FDMASK(finet);
320			InetInuse = 1;
321		}
322	}
323	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
324		klogm = FDMASK(fklog);
325	else {
326		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
327		klogm = 0;
328	}
329
330	/* tuck my process id away */
331	fp = fopen(PidFile, "w");
332	if (fp != NULL) {
333		fprintf(fp, "%d\n", getpid());
334		(void) fclose(fp);
335	}
336
337	dprintf("off & running....\n");
338
339	init(0);
340	(void)signal(SIGHUP, init);
341
342	tvp = &tv;
343	tv.tv_sec = tv.tv_usec = 0;
344
345	for (;;) {
346		int nfds, readfds = FDMASK(funix) | inetm | klogm;
347
348		dprintf("readfds = %#x\n", readfds);
349		nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
350		    (fd_set *)NULL, tvp);
351		if (nfds == 0) {
352			if (tvp) {
353				tvp = NULL;
354				if (ppid != 1)
355					kill(ppid, SIGALRM);
356			}
357			continue;
358		}
359		if (nfds < 0) {
360			if (errno != EINTR)
361				logerror("select");
362			continue;
363		}
364		dprintf("got a message (%d, %#x)\n", nfds, readfds);
365		if (readfds & klogm) {
366			i = read(fklog, line, sizeof(line) - 1);
367			if (i > 0) {
368				line[i] = '\0';
369				printsys(line);
370			} else if (i < 0 && errno != EINTR) {
371				logerror("klog");
372				fklog = -1;
373				klogm = 0;
374			}
375		}
376		if (readfds & FDMASK(funix)) {
377			len = sizeof(fromunix);
378			i = recvfrom(funix, line, MAXLINE, 0,
379			    (struct sockaddr *)&fromunix, &len);
380			if (i > 0) {
381				line[i] = '\0';
382				printline(LocalHostName, line);
383			} else if (i < 0 && errno != EINTR)
384				logerror("recvfrom unix");
385		}
386		if (readfds & inetm) {
387			len = sizeof(frominet);
388			i = recvfrom(finet, line, MAXLINE, 0,
389			    (struct sockaddr *)&frominet, &len);
390			if (i > 0) {
391				line[i] = '\0';
392				printline(cvthname(&frominet), line);
393			} else if (i < 0 && errno != EINTR)
394				logerror("recvfrom inet");
395		}
396	}
397}
398
399void
400usage()
401{
402
403	fprintf(stderr,
404		"usage: syslogd [-ds] [-f conffile] [-m markinterval]"
405		" [-p logpath]\n");
406	exit(1);
407}
408
409/*
410 * Take a raw input line, decode the message, and print the message
411 * on the appropriate log files.
412 */
413void
414printline(hname, msg)
415	char *hname;
416	char *msg;
417{
418	int c, pri;
419	char *p, *q, line[MAXLINE + 1];
420
421	/* test for special codes */
422	pri = DEFUPRI;
423	p = msg;
424	if (*p == '<') {
425		pri = 0;
426		while (isdigit(*++p))
427			pri = 10 * pri + (*p - '0');
428		if (*p == '>')
429			++p;
430	}
431	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
432		pri = DEFUPRI;
433
434	/* don't allow users to log kernel messages */
435	if (LOG_FAC(pri) == LOG_KERN)
436		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
437
438	q = line;
439
440	while ((c = *p++ & 0177) != '\0' &&
441	    q < &line[sizeof(line) - 1])
442		if (iscntrl(c))
443			if (c == '\n')
444				*q++ = ' ';
445			else if (c == '\t')
446				*q++ = '\t';
447			else {
448				*q++ = '^';
449				*q++ = c ^ 0100;
450			}
451		else
452			*q++ = c;
453	*q = '\0';
454
455	logmsg(pri, line, hname, 0);
456}
457
458/*
459 * Take a raw input line from /dev/klog, split and format similar to syslog().
460 */
461void
462printsys(msg)
463	char *msg;
464{
465	int c, pri, flags;
466	char *lp, *p, *q, line[MAXLINE + 1];
467
468	(void)strcpy(line, bootfile);
469	(void)strcat(line, ": ");
470	lp = line + strlen(line);
471	for (p = msg; *p != '\0'; ) {
472		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
473		pri = DEFSPRI;
474		if (*p == '<') {
475			pri = 0;
476			while (isdigit(*++p))
477				pri = 10 * pri + (*p - '0');
478			if (*p == '>')
479				++p;
480		} else {
481			/* kernel printf's come out on console */
482			flags |= IGN_CONS;
483		}
484		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
485			pri = DEFSPRI;
486		q = lp;
487		while (*p != '\0' && (c = *p++) != '\n' &&
488		    q < &line[MAXLINE])
489			*q++ = c;
490		*q = '\0';
491		logmsg(pri, line, LocalHostName, flags);
492	}
493}
494
495time_t	now;
496
497/*
498 * Log a message to the appropriate log files, users, etc. based on
499 * the priority.
500 */
501void
502logmsg(pri, msg, from, flags)
503	int pri;
504	char *msg, *from;
505	int flags;
506{
507	struct filed *f;
508	int fac, msglen, omask, prilev;
509	char *timestamp;
510 	char prog[NAME_MAX+1];
511 	int i;
512
513	dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
514	    pri, flags, from, msg);
515
516	omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
517
518	/*
519	 * Check to see if msg looks non-standard.
520	 */
521	msglen = strlen(msg);
522	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
523	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
524		flags |= ADDDATE;
525
526	(void)time(&now);
527	if (flags & ADDDATE)
528		timestamp = ctime(&now) + 4;
529	else {
530		timestamp = msg;
531		msg += 16;
532		msglen -= 16;
533	}
534
535	/* skip leading blanks */
536	while(isspace(*msg)) {
537		msg++;
538		msglen--;
539	}
540
541	/* extract facility and priority level */
542	if (flags & MARK)
543		fac = LOG_NFACILITIES;
544	else
545		fac = LOG_FAC(pri);
546	prilev = LOG_PRI(pri);
547
548	/* extract program name */
549	for(i = 0; i < NAME_MAX; i++) {
550		if(!isalnum(msg[i]))
551			break;
552		prog[i] = msg[i];
553	}
554	prog[i] = 0;
555
556	/* log the message to the particular outputs */
557	if (!Initialized) {
558		f = &consfile;
559		f->f_file = open(ctty, O_WRONLY, 0);
560
561		if (f->f_file >= 0) {
562			fprintlog(f, flags, msg);
563			(void)close(f->f_file);
564		}
565		(void)sigsetmask(omask);
566		return;
567	}
568	for (f = Files; f; f = f->f_next) {
569		/* skip messages that are incorrect priority */
570		if (f->f_pmask[fac] < prilev ||
571		    f->f_pmask[fac] == INTERNAL_NOPRI)
572			continue;
573		/* skip messages with the incorrect program name */
574		if(f->f_program)
575			if(strcmp(prog, f->f_program) != 0)
576				continue;
577
578		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
579			continue;
580
581		/* don't output marks to recently written files */
582		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
583			continue;
584
585		/*
586		 * suppress duplicate lines to this file
587		 */
588		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
589		    !strcmp(msg, f->f_prevline) &&
590		    !strcmp(from, f->f_prevhost)) {
591			(void)strncpy(f->f_lasttime, timestamp, 15);
592			f->f_prevcount++;
593			dprintf("msg repeated %d times, %ld sec of %d\n",
594			    f->f_prevcount, now - f->f_time,
595			    repeatinterval[f->f_repeatcount]);
596			/*
597			 * If domark would have logged this by now,
598			 * flush it now (so we don't hold isolated messages),
599			 * but back off so we'll flush less often
600			 * in the future.
601			 */
602			if (now > REPEATTIME(f)) {
603				fprintlog(f, flags, (char *)NULL);
604				BACKOFF(f);
605			}
606		} else {
607			/* new line, save it */
608			if (f->f_prevcount)
609				fprintlog(f, 0, (char *)NULL);
610			f->f_repeatcount = 0;
611			f->f_prevpri = pri;
612			(void)strncpy(f->f_lasttime, timestamp, 15);
613			(void)strncpy(f->f_prevhost, from,
614					sizeof(f->f_prevhost));
615			if (msglen < MAXSVLINE) {
616				f->f_prevlen = msglen;
617				(void)strcpy(f->f_prevline, msg);
618				fprintlog(f, flags, (char *)NULL);
619			} else {
620				f->f_prevline[0] = 0;
621				f->f_prevlen = 0;
622				fprintlog(f, flags, msg);
623			}
624		}
625	}
626	(void)sigsetmask(omask);
627}
628
629void
630fprintlog(f, flags, msg)
631	struct filed *f;
632	int flags;
633	char *msg;
634{
635	struct iovec iov[6];
636	struct iovec *v;
637	int l;
638	char line[MAXLINE + 1], repbuf[80], greetings[200];
639	char *msgret;
640
641	v = iov;
642	if (f->f_type == F_WALL) {
643		v->iov_base = greetings;
644		v->iov_len = sprintf(greetings,
645		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
646		    f->f_prevhost, ctime(&now));
647		v++;
648		v->iov_base = "";
649		v->iov_len = 0;
650		v++;
651	} else {
652		v->iov_base = f->f_lasttime;
653		v->iov_len = 15;
654		v++;
655		v->iov_base = " ";
656		v->iov_len = 1;
657		v++;
658	}
659	v->iov_base = f->f_prevhost;
660	v->iov_len = strlen(v->iov_base);
661	v++;
662	v->iov_base = " ";
663	v->iov_len = 1;
664	v++;
665
666	if (msg) {
667		v->iov_base = msg;
668		v->iov_len = strlen(msg);
669	} else if (f->f_prevcount > 1) {
670		v->iov_base = repbuf;
671		v->iov_len = sprintf(repbuf, "last message repeated %d times",
672		    f->f_prevcount);
673	} else {
674		v->iov_base = f->f_prevline;
675		v->iov_len = f->f_prevlen;
676	}
677	v++;
678
679	dprintf("Logging to %s", TypeNames[f->f_type]);
680	f->f_time = now;
681
682	switch (f->f_type) {
683	case F_UNUSED:
684		dprintf("\n");
685		break;
686
687	case F_FORW:
688		dprintf(" %s\n", f->f_un.f_forw.f_hname);
689		l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
690		    iov[0].iov_base, iov[4].iov_base);
691		if (l > MAXLINE)
692			l = MAXLINE;
693		if ((finet >= 0) &&
694		     (sendto(finet, line, l, 0,
695			     (struct sockaddr *)&f->f_un.f_forw.f_addr,
696			     sizeof(f->f_un.f_forw.f_addr)) != l)) {
697			int e = errno;
698			(void)close(f->f_file);
699			f->f_type = F_UNUSED;
700			errno = e;
701			logerror("sendto");
702		}
703		break;
704
705	case F_CONSOLE:
706		if (flags & IGN_CONS) {
707			dprintf(" (ignored)\n");
708			break;
709		}
710		/* FALLTHROUGH */
711
712	case F_FILE:
713		dprintf(" %s\n", f->f_un.f_fname);
714		v->iov_base = "\n";
715		v->iov_len = 1;
716		if (writev(f->f_file, iov, 6) < 0) {
717			int e = errno;
718			(void)close(f->f_file);
719			f->f_type = F_UNUSED;
720			errno = e;
721			logerror(f->f_un.f_fname);
722		} else if (flags & SYNC_FILE)
723			(void)fsync(f->f_file);
724		break;
725
726	case F_TTY:
727		dprintf(" %s\n", f->f_un.f_fname);
728		v->iov_base = "\r\n";
729		v->iov_len = 2;
730
731		errno = 0;	/* ttymsg() only sometimes returns an errno */
732		if ((msgret = ttymsg(iov, 6, f->f_un.f_fname, 10))) {
733			int e = errno;
734			(void)close(f->f_file);
735			f->f_type = F_UNUSED;
736			errno = e;
737			logerror(msgret);
738		}
739		break;
740
741	case F_USERS:
742	case F_WALL:
743		dprintf("\n");
744		v->iov_base = "\r\n";
745		v->iov_len = 2;
746		wallmsg(f, iov);
747		break;
748	}
749	f->f_prevcount = 0;
750}
751
752/*
753 *  WALLMSG -- Write a message to the world at large
754 *
755 *	Write the specified message to either the entire
756 *	world, or a list of approved users.
757 */
758void
759wallmsg(f, iov)
760	struct filed *f;
761	struct iovec *iov;
762{
763	static int reenter;			/* avoid calling ourselves */
764	FILE *uf;
765	struct utmp ut;
766	int i;
767	char *p;
768	char line[sizeof(ut.ut_line) + 1];
769
770	if (reenter++)
771		return;
772	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
773		logerror(_PATH_UTMP);
774		reenter = 0;
775		return;
776	}
777	/* NOSTRICT */
778	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
779		if (ut.ut_name[0] == '\0')
780			continue;
781		strncpy(line, ut.ut_line, sizeof(ut.ut_line));
782		line[sizeof(ut.ut_line)] = '\0';
783		if (f->f_type == F_WALL) {
784			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
785				errno = 0;	/* already in msg */
786				logerror(p);
787			}
788			continue;
789		}
790		/* should we send the message to this user? */
791		for (i = 0; i < MAXUNAMES; i++) {
792			if (!f->f_un.f_uname[i][0])
793				break;
794			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
795			    UT_NAMESIZE)) {
796				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
797								!= NULL) {
798					errno = 0;	/* already in msg */
799					logerror(p);
800				}
801				break;
802			}
803		}
804	}
805	(void)fclose(uf);
806	reenter = 0;
807}
808
809void
810reapchild(signo)
811	int signo;
812{
813	int status;
814
815	while (wait3(&status, WNOHANG, (struct rusage *)NULL) > 0)
816		;
817}
818
819/*
820 * Return a printable representation of a host address.
821 */
822char *
823cvthname(f)
824	struct sockaddr_in *f;
825{
826	struct hostent *hp;
827	char *p;
828
829	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
830
831	if (f->sin_family != AF_INET) {
832		dprintf("Malformed from address\n");
833		return ("???");
834	}
835	hp = gethostbyaddr((char *)&f->sin_addr,
836	    sizeof(struct in_addr), f->sin_family);
837	if (hp == 0) {
838		dprintf("Host name for your address (%s) unknown\n",
839			inet_ntoa(f->sin_addr));
840		return (inet_ntoa(f->sin_addr));
841	}
842	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
843		*p = '\0';
844	return (hp->h_name);
845}
846
847void
848domark(signo)
849	int signo;
850{
851	struct filed *f;
852
853	now = time((time_t *)NULL);
854	MarkSeq += TIMERINTVL;
855	if (MarkSeq >= MarkInterval) {
856		logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
857		MarkSeq = 0;
858	}
859
860	for (f = Files; f; f = f->f_next) {
861		if (f->f_prevcount && now >= REPEATTIME(f)) {
862			dprintf("flush %s: repeated %d times, %d sec.\n",
863			    TypeNames[f->f_type], f->f_prevcount,
864			    repeatinterval[f->f_repeatcount]);
865			fprintlog(f, 0, (char *)NULL);
866			BACKOFF(f);
867		}
868	}
869	(void)alarm(TIMERINTVL);
870}
871
872/*
873 * Print syslogd errors some place.
874 */
875void
876logerror(type)
877	const char *type;
878{
879	char buf[100];
880
881	if (errno)
882		(void)snprintf(buf,
883		    sizeof(buf), "syslogd: %s: %s", type, strerror(errno));
884	else
885		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
886	errno = 0;
887	dprintf("%s\n", buf);
888	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
889}
890
891void
892die(signo)
893	int signo;
894{
895	struct filed *f;
896	char buf[100];
897
898	for (f = Files; f != NULL; f = f->f_next) {
899		/* flush any pending output */
900		if (f->f_prevcount)
901			fprintlog(f, 0, (char *)NULL);
902	}
903	if (signo) {
904		dprintf("syslogd: exiting on signal %d\n", signo);
905		(void)sprintf(buf, "exiting on signal %d", signo);
906		errno = 0;
907		logerror(buf);
908	}
909	if (created_lsock)
910		(void)unlink(LogName);
911	exit(1);
912}
913
914/*
915 *  INIT -- Initialize syslogd from configuration table
916 */
917void
918init(signo)
919	int signo;
920{
921	int i;
922	FILE *cf;
923	struct filed *f, *next, **nextp;
924	char *p;
925	char cline[LINE_MAX];
926 	char prog[NAME_MAX+1];
927
928	dprintf("init\n");
929
930	/*
931	 *  Close all open log files.
932	 */
933	Initialized = 0;
934	for (f = Files; f != NULL; f = next) {
935		/* flush any pending output */
936		if (f->f_prevcount)
937			fprintlog(f, 0, (char *)NULL);
938
939		switch (f->f_type) {
940		case F_FILE:
941		case F_TTY:
942		case F_CONSOLE:
943		case F_FORW:
944			(void)close(f->f_file);
945			break;
946		}
947		next = f->f_next;
948		if(f->f_program) free(f->f_program);
949		free((char *)f);
950	}
951	Files = NULL;
952	nextp = &Files;
953
954	/* open the configuration file */
955	if ((cf = fopen(ConfFile, "r")) == NULL) {
956		dprintf("cannot open %s\n", ConfFile);
957		*nextp = (struct filed *)calloc(1, sizeof(*f));
958		cfline("*.ERR\t/dev/console", *nextp, "*");
959		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
960		cfline("*.PANIC\t*", (*nextp)->f_next, "*");
961		Initialized = 1;
962		return;
963	}
964
965	/*
966	 *  Foreach line in the conf table, open that file.
967	 */
968	f = NULL;
969	strcpy(prog, "*");
970	while (fgets(cline, sizeof(cline), cf) != NULL) {
971		/*
972		 * check for end-of-section, comments, strip off trailing
973		 * spaces and newline character. #!prog is treated specially:
974		 * following lines apply only to that program.
975		 */
976		for (p = cline; isspace(*p); ++p)
977			continue;
978		if (*p == 0)
979			continue;
980		if(*p == '#') {
981			p++;
982			if(*p!='!')
983				continue;
984		}
985		if(*p=='!') {
986			p++;
987			while(isspace(*p)) p++;
988			if(!*p) {
989				strcpy(prog, "*");
990				continue;
991			}
992			for(i = 0; i < NAME_MAX; i++) {
993				if(!isalnum(p[i]))
994					break;
995				prog[i] = p[i];
996			}
997			prog[i] = 0;
998			continue;
999		}
1000		for (p = strchr(cline, '\0'); isspace(*--p);)
1001			continue;
1002		*++p = '\0';
1003		f = (struct filed *)calloc(1, sizeof(*f));
1004		*nextp = f;
1005		nextp = &f->f_next;
1006		cfline(cline, f, prog);
1007	}
1008
1009	/* close the configuration file */
1010	(void)fclose(cf);
1011
1012	Initialized = 1;
1013
1014	if (Debug) {
1015		for (f = Files; f; f = f->f_next) {
1016			for (i = 0; i <= LOG_NFACILITIES; i++)
1017				if (f->f_pmask[i] == INTERNAL_NOPRI)
1018					printf("X ");
1019				else
1020					printf("%d ", f->f_pmask[i]);
1021			printf("%s: ", TypeNames[f->f_type]);
1022			switch (f->f_type) {
1023			case F_FILE:
1024			case F_TTY:
1025			case F_CONSOLE:
1026				printf("%s", f->f_un.f_fname);
1027				break;
1028
1029			case F_FORW:
1030				printf("%s", f->f_un.f_forw.f_hname);
1031				break;
1032
1033			case F_USERS:
1034				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1035					printf("%s, ", f->f_un.f_uname[i]);
1036				break;
1037			}
1038			if(f->f_program) {
1039				printf(" (%s)", f->f_program);
1040			}
1041			printf("\n");
1042		}
1043	}
1044
1045	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1046	dprintf("syslogd: restarted\n");
1047}
1048
1049/*
1050 * Crack a configuration file line
1051 */
1052void
1053cfline(line, f, prog)
1054	char *line;
1055	struct filed *f;
1056	char *prog;
1057{
1058	struct hostent *hp;
1059	int i, pri;
1060	char *bp, *p, *q;
1061	char buf[MAXLINE], ebuf[100];
1062
1063	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1064
1065	errno = 0;	/* keep strerror() stuff out of logerror messages */
1066
1067	/* clear out file entry */
1068	memset(f, 0, sizeof(*f));
1069	for (i = 0; i <= LOG_NFACILITIES; i++)
1070		f->f_pmask[i] = INTERNAL_NOPRI;
1071
1072	/* save program name if any */
1073	if(prog && *prog=='*') prog = NULL;
1074	if(prog) {
1075		f->f_program = calloc(1, strlen(prog)+1);
1076		if(f->f_program) {
1077			strcpy(f->f_program, prog);
1078		}
1079	}
1080
1081	/* scan through the list of selectors */
1082	for (p = line; *p && *p != '\t';) {
1083
1084		/* find the end of this facility name list */
1085		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1086			continue;
1087
1088		/* collect priority name */
1089		for (bp = buf; *q && !strchr("\t,;", *q); )
1090			*bp++ = *q++;
1091		*bp = '\0';
1092
1093		/* skip cruft */
1094		while (strchr(", ;", *q))
1095			q++;
1096
1097		/* decode priority name */
1098		if (*buf == '*')
1099			pri = LOG_PRIMASK + 1;
1100		else {
1101			pri = decode(buf, prioritynames);
1102			if (pri < 0) {
1103				(void)sprintf(ebuf,
1104				    "unknown priority name \"%s\"", buf);
1105				logerror(ebuf);
1106				return;
1107			}
1108		}
1109
1110		/* scan facilities */
1111		while (*p && !strchr("\t.;", *p)) {
1112			for (bp = buf; *p && !strchr("\t,;.", *p); )
1113				*bp++ = *p++;
1114			*bp = '\0';
1115			if (*buf == '*')
1116				for (i = 0; i < LOG_NFACILITIES; i++)
1117					f->f_pmask[i] = pri;
1118			else {
1119				i = decode(buf, facilitynames);
1120				if (i < 0) {
1121					(void)sprintf(ebuf,
1122					    "unknown facility name \"%s\"",
1123					    buf);
1124					logerror(ebuf);
1125					return;
1126				}
1127				f->f_pmask[i >> 3] = pri;
1128			}
1129			while (*p == ',' || *p == ' ')
1130				p++;
1131		}
1132
1133		p = q;
1134	}
1135
1136	/* skip to action part */
1137	while (*p == '\t')
1138		p++;
1139
1140	switch (*p)
1141	{
1142	case '@':
1143		if (!InetInuse)
1144			break;
1145		(void)strcpy(f->f_un.f_forw.f_hname, ++p);
1146		hp = gethostbyname(p);
1147		if (hp == NULL) {
1148			extern int h_errno;
1149
1150			logerror(hstrerror(h_errno));
1151			break;
1152		}
1153		memset(&f->f_un.f_forw.f_addr, 0,
1154			 sizeof(f->f_un.f_forw.f_addr));
1155		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1156		f->f_un.f_forw.f_addr.sin_port = LogPort;
1157		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
1158		f->f_type = F_FORW;
1159		break;
1160
1161	case '/':
1162		(void)strcpy(f->f_un.f_fname, p);
1163		if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1164			f->f_file = F_UNUSED;
1165			logerror(p);
1166			break;
1167		}
1168		if (isatty(f->f_file))
1169			f->f_type = F_TTY;
1170		else
1171			f->f_type = F_FILE;
1172		if (strcmp(p, ctty) == 0)
1173			f->f_type = F_CONSOLE;
1174		break;
1175
1176	case '*':
1177		f->f_type = F_WALL;
1178		break;
1179
1180	default:
1181		for (i = 0; i < MAXUNAMES && *p; i++) {
1182			for (q = p; *q && *q != ','; )
1183				q++;
1184			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1185			if ((q - p) > UT_NAMESIZE)
1186				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1187			else
1188				f->f_un.f_uname[i][q - p] = '\0';
1189			while (*q == ',' || *q == ' ')
1190				q++;
1191			p = q;
1192		}
1193		f->f_type = F_USERS;
1194		break;
1195	}
1196}
1197
1198
1199/*
1200 *  Decode a symbolic name to a numeric value
1201 */
1202int
1203decode(name, codetab)
1204	const char *name;
1205	CODE *codetab;
1206{
1207	CODE *c;
1208	char *p, buf[40];
1209
1210	if (isdigit(*name))
1211		return (atoi(name));
1212
1213	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1214		if (isupper(*name))
1215			*p = tolower(*name);
1216		else
1217			*p = *name;
1218	}
1219	*p = '\0';
1220	for (c = codetab; c->c_name; c++)
1221		if (!strcmp(buf, c->c_name))
1222			return (c->c_val);
1223
1224	return (-1);
1225}
1226
1227/*
1228 * fork off and become a daemon, but wait for the child to come online
1229 * before returing to the parent, or we get disk thrashing at boot etc.
1230 * Set a timer so we don't hang forever if it wedges.
1231 */
1232int
1233waitdaemon(nochdir, noclose, maxwait)
1234	int nochdir, noclose, maxwait;
1235{
1236	int fd;
1237	int status;
1238	pid_t pid, childpid;
1239
1240	switch (childpid = fork()) {
1241	case -1:
1242		return (-1);
1243	case 0:
1244		break;
1245	default:
1246		signal(SIGALRM, timedout);
1247		alarm(maxwait);
1248		while ((pid = wait3(&status, 0, NULL)) != -1) {
1249			if (WIFEXITED(status))
1250				errx(1, "child pid %d exited with return code %d",
1251					pid, WEXITSTATUS(status));
1252			if (WIFSIGNALED(status))
1253				errx(1, "child pid %d exited on signal %d%s",
1254					pid, WTERMSIG(status),
1255					WCOREDUMP(status) ? " (core dumped)" :
1256					"");
1257			if (pid == childpid)	/* it's gone... */
1258				break;
1259		}
1260		exit(0);
1261	}
1262
1263	if (setsid() == -1)
1264		return (-1);
1265
1266	if (!nochdir)
1267		(void)chdir("/");
1268
1269	if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
1270		(void)dup2(fd, STDIN_FILENO);
1271		(void)dup2(fd, STDOUT_FILENO);
1272		(void)dup2(fd, STDERR_FILENO);
1273		if (fd > 2)
1274			(void)close (fd);
1275	}
1276	return (getppid());
1277}
1278
1279/*
1280 * We get a SIGALRM from the child when it's running and finished doing it's
1281 * fsync()'s or O_SYNC writes for all the boot messages.
1282 *
1283 * We also get a signal from the kernel if the timer expires, so check to
1284 * see what happened.
1285 */
1286void
1287timedout(sig)
1288	int sig __unused;
1289{
1290	int left;
1291	left = alarm(0);
1292	signal(SIGALRM, SIG_DFL);
1293	if (left == 0)
1294		errx(1, "timed out waiting for child");
1295	else
1296		exit(0);
1297}
1298