xref: /original-bsd/usr.sbin/syslogd/syslogd.c (revision e58c8952)
1 /*
2  * Copyright (c) 1983, 1988, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * %sccs.include.redist.c%
6  */
7 
8 #ifndef lint
9 static char copyright[] =
10 "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
11 	The Regents of the University of California.  All rights reserved.\n";
12 #endif /* not lint */
13 
14 #ifndef lint
15 static char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 04/04/94";
16 #endif /* not lint */
17 
18 /*
19  *  syslogd -- log system messages
20  *
21  * This program implements a system log. It takes a series of lines.
22  * Each line may have a priority, signified as "<n>" as
23  * the first characters of the line.  If this is
24  * not present, a default priority is used.
25  *
26  * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
27  * cause it to reread its configuration file.
28  *
29  * Defined Constants:
30  *
31  * MAXLINE -- the maximimum line length that can be handled.
32  * DEFUPRI -- the default priority for user messages
33  * DEFSPRI -- the default priority for kernel messages
34  *
35  * Author: Eric Allman
36  * extensive changes by Ralph Campbell
37  * more extensive changes by Eric Allman (again)
38  */
39 
40 #define	MAXLINE		1024		/* maximum line length */
41 #define	MAXSVLINE	120		/* maximum saved line length */
42 #define DEFUPRI		(LOG_USER|LOG_NOTICE)
43 #define DEFSPRI		(LOG_KERN|LOG_CRIT)
44 #define TIMERINTVL	30		/* interval for checking flush, mark */
45 
46 #include <sys/param.h>
47 #include <sys/ioctl.h>
48 #include <sys/stat.h>
49 #include <sys/wait.h>
50 #include <sys/socket.h>
51 #include <sys/msgbuf.h>
52 #include <sys/uio.h>
53 #include <sys/un.h>
54 #include <sys/time.h>
55 #include <sys/resource.h>
56 
57 #include <netinet/in.h>
58 #include <netdb.h>
59 #include <arpa/inet.h>
60 
61 #include <ctype.h>
62 #include <errno.h>
63 #include <fcntl.h>
64 #include <setjmp.h>
65 #include <signal.h>
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69 #include <unistd.h>
70 #include <utmp.h>
71 #include "pathnames.h"
72 
73 #define SYSLOG_NAMES
74 #include <sys/syslog.h>
75 
76 char	*LogName = _PATH_LOG;
77 char	*ConfFile = _PATH_LOGCONF;
78 char	*PidFile = _PATH_LOGPID;
79 char	ctty[] = _PATH_CONSOLE;
80 
81 #define FDMASK(fd)	(1 << (fd))
82 
83 #define	dprintf		if (Debug) printf
84 
85 #define MAXUNAMES	20	/* maximum number of user names */
86 
87 /*
88  * Flags to logmsg().
89  */
90 
91 #define IGN_CONS	0x001	/* don't print on console */
92 #define SYNC_FILE	0x002	/* do fsync on file after printing */
93 #define ADDDATE		0x004	/* add a date to the message */
94 #define MARK		0x008	/* this message is a mark */
95 
96 /*
97  * This structure represents the files that will have log
98  * copies printed.
99  */
100 
101 struct filed {
102 	struct	filed *f_next;		/* next in linked list */
103 	short	f_type;			/* entry type, see below */
104 	short	f_file;			/* file descriptor */
105 	time_t	f_time;			/* time this was last written */
106 	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
107 	union {
108 		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
109 		struct {
110 			char	f_hname[MAXHOSTNAMELEN+1];
111 			struct sockaddr_in	f_addr;
112 		} f_forw;		/* forwarding address */
113 		char	f_fname[MAXPATHLEN];
114 	} f_un;
115 	char	f_prevline[MAXSVLINE];		/* last message logged */
116 	char	f_lasttime[16];			/* time of last occurrence */
117 	char	f_prevhost[MAXHOSTNAMELEN+1];	/* host from which recd. */
118 	int	f_prevpri;			/* pri of f_prevline */
119 	int	f_prevlen;			/* length of f_prevline */
120 	int	f_prevcount;			/* repetition cnt of prevline */
121 	int	f_repeatcount;			/* number of "repeated" msgs */
122 };
123 
124 /*
125  * Intervals at which we flush out "message repeated" messages,
126  * in seconds after previous message is logged.  After each flush,
127  * we move to the next interval until we reach the largest.
128  */
129 int	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
130 #define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
131 #define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
132 #define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
133 				 (f)->f_repeatcount = MAXREPEAT; \
134 			}
135 
136 /* values for f_type */
137 #define F_UNUSED	0		/* unused entry */
138 #define F_FILE		1		/* regular file */
139 #define F_TTY		2		/* terminal */
140 #define F_CONSOLE	3		/* console terminal */
141 #define F_FORW		4		/* remote machine */
142 #define F_USERS		5		/* list of users */
143 #define F_WALL		6		/* everyone logged on */
144 
145 char	*TypeNames[7] = {
146 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
147 	"FORW",		"USERS",	"WALL"
148 };
149 
150 struct	filed *Files;
151 struct	filed consfile;
152 
153 int	Debug;			/* debug flag */
154 char	LocalHostName[MAXHOSTNAMELEN+1];	/* our hostname */
155 char	*LocalDomain;		/* our local domain name */
156 int	InetInuse = 0;		/* non-zero if INET sockets are being used */
157 int	finet;			/* Internet datagram socket */
158 int	LogPort;		/* port number for INET connections */
159 int	Initialized = 0;	/* set when we have initialized ourselves */
160 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
161 int	MarkSeq = 0;		/* mark sequence number */
162 
163 void	cfline __P((char *, struct filed *));
164 char   *cvthname __P((struct sockaddr_in *));
165 int	decode __P((const char *, CODE *));
166 void	die __P((int));
167 void	domark __P((int));
168 void	fprintlog __P((struct filed *, int, char *));
169 void	init __P((int));
170 void	logerror __P((char *));
171 void	logmsg __P((int, char *, char *, int));
172 void	printline __P((char *, char *));
173 void	printsys __P((char *));
174 void	reapchild __P((int));
175 char   *ttymsg __P((struct iovec *, int, char *, int));
176 void	usage __P((void));
177 void	wallmsg __P((struct filed *, struct iovec *));
178 
179 int
180 main(argc, argv)
181 	int argc;
182 	char *argv[];
183 {
184 	int ch, funix, i, inetm, fklog, klogm, len;
185 	struct sockaddr_un sunx, fromunix;
186 	struct sockaddr_in sin, frominet;
187 	FILE *fp;
188 	char *p, line[MSG_BSIZE + 1];
189 
190 	while ((ch = getopt(argc, argv, "df:m:p:")) != EOF)
191 		switch(ch) {
192 		case 'd':		/* debug */
193 			Debug++;
194 			break;
195 		case 'f':		/* configuration file */
196 			ConfFile = optarg;
197 			break;
198 		case 'm':		/* mark interval */
199 			MarkInterval = atoi(optarg) * 60;
200 			break;
201 		case 'p':		/* path */
202 			LogName = optarg;
203 			break;
204 		case '?':
205 		default:
206 			usage();
207 		}
208 	if ((argc -= optind) != 0)
209 		usage();
210 
211 	if (!Debug)
212 		(void)daemon(0, 0);
213 	else
214 		setlinebuf(stdout);
215 
216 	consfile.f_type = F_CONSOLE;
217 	(void)strcpy(consfile.f_un.f_fname, ctty);
218 	(void)gethostname(LocalHostName, sizeof(LocalHostName));
219 	if ((p = strchr(LocalHostName, '.')) != NULL) {
220 		*p++ = '\0';
221 		LocalDomain = p;
222 	} else
223 		LocalDomain = "";
224 	(void)signal(SIGTERM, die);
225 	(void)signal(SIGINT, Debug ? die : SIG_IGN);
226 	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
227 	(void)signal(SIGCHLD, reapchild);
228 	(void)signal(SIGALRM, domark);
229 	(void)alarm(TIMERINTVL);
230 	(void)unlink(LogName);
231 
232 #ifndef SUN_LEN
233 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
234 #endif
235 	memset(&sunx, 0, sizeof(sunx));
236 	sunx.sun_family = AF_UNIX;
237 	(void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
238 	funix = socket(AF_UNIX, SOCK_DGRAM, 0);
239 	if (funix < 0 ||
240 	    bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
241 	    chmod(LogName, 0666) < 0) {
242 		(void) sprintf(line, "cannot create %s", LogName);
243 		logerror(line);
244 		dprintf("cannot create %s (%d)\n", LogName, errno);
245 		die(0);
246 	}
247 	finet = socket(AF_INET, SOCK_DGRAM, 0);
248 	inetm = 0;
249 	if (finet >= 0) {
250 		struct servent *sp;
251 
252 		sp = getservbyname("syslog", "udp");
253 		if (sp == NULL) {
254 			errno = 0;
255 			logerror("syslog/udp: unknown service");
256 			die(0);
257 		}
258 		memset(&sin, 0, sizeof(sin));
259 		sin.sin_family = AF_INET;
260 		sin.sin_port = LogPort = sp->s_port;
261 		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
262 			logerror("bind");
263 			if (!Debug)
264 				die(0);
265 		} else {
266 			inetm = FDMASK(finet);
267 			InetInuse = 1;
268 		}
269 	}
270 	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
271 		klogm = FDMASK(fklog);
272 	else {
273 		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
274 		klogm = 0;
275 	}
276 
277 	/* tuck my process id away */
278 	fp = fopen(PidFile, "w");
279 	if (fp != NULL) {
280 		fprintf(fp, "%d\n", getpid());
281 		(void) fclose(fp);
282 	}
283 
284 	dprintf("off & running....\n");
285 
286 	init(0);
287 	(void)signal(SIGHUP, init);
288 
289 	for (;;) {
290 		int nfds, readfds = FDMASK(funix) | inetm | klogm;
291 
292 		dprintf("readfds = %#x\n", readfds);
293 		nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
294 		    (fd_set *)NULL, (struct timeval *)NULL);
295 		if (nfds == 0)
296 			continue;
297 		if (nfds < 0) {
298 			if (errno != EINTR)
299 				logerror("select");
300 			continue;
301 		}
302 		dprintf("got a message (%d, %#x)\n", nfds, readfds);
303 		if (readfds & klogm) {
304 			i = read(fklog, line, sizeof(line) - 1);
305 			if (i > 0) {
306 				line[i] = '\0';
307 				printsys(line);
308 			} else if (i < 0 && errno != EINTR) {
309 				logerror("klog");
310 				fklog = -1;
311 				klogm = 0;
312 			}
313 		}
314 		if (readfds & FDMASK(funix)) {
315 			len = sizeof(fromunix);
316 			i = recvfrom(funix, line, MAXLINE, 0,
317 			    (struct sockaddr *)&fromunix, &len);
318 			if (i > 0) {
319 				line[i] = '\0';
320 				printline(LocalHostName, line);
321 			} else if (i < 0 && errno != EINTR)
322 				logerror("recvfrom unix");
323 		}
324 		if (readfds & inetm) {
325 			len = sizeof(frominet);
326 			i = recvfrom(finet, line, MAXLINE, 0,
327 			    (struct sockaddr *)&frominet, &len);
328 			if (i > 0) {
329 				line[i] = '\0';
330 				printline(cvthname(&frominet), line);
331 			} else if (i < 0 && errno != EINTR)
332 				logerror("recvfrom inet");
333 		}
334 	}
335 }
336 
337 void
338 usage()
339 {
340 
341 	(void)fprintf(stderr,
342 	    "usage: syslogd [-f conffile] [-m markinterval] [-p logpath]\n");
343 	exit(1);
344 }
345 
346 /*
347  * Take a raw input line, decode the message, and print the message
348  * on the appropriate log files.
349  */
350 void
351 printline(hname, msg)
352 	char *hname;
353 	char *msg;
354 {
355 	int c, pri;
356 	char *p, *q, line[MAXLINE + 1];
357 
358 	/* test for special codes */
359 	pri = DEFUPRI;
360 	p = msg;
361 	if (*p == '<') {
362 		pri = 0;
363 		while (isdigit(*++p))
364 			pri = 10 * pri + (*p - '0');
365 		if (*p == '>')
366 			++p;
367 	}
368 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
369 		pri = DEFUPRI;
370 
371 	/* don't allow users to log kernel messages */
372 	if (LOG_FAC(pri) == LOG_KERN)
373 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
374 
375 	q = line;
376 
377 	while ((c = *p++ & 0177) != '\0' &&
378 	    q < &line[sizeof(line) - 1])
379 		if (iscntrl(c))
380 			if (c == '\n')
381 				*q++ = ' ';
382 			else if (c == '\t')
383 				*q++ = '\t';
384 			else {
385 				*q++ = '^';
386 				*q++ = c ^ 0100;
387 			}
388 		else
389 			*q++ = c;
390 	*q = '\0';
391 
392 	logmsg(pri, line, hname, 0);
393 }
394 
395 /*
396  * Take a raw input line from /dev/klog, split and format similar to syslog().
397  */
398 void
399 printsys(msg)
400 	char *msg;
401 {
402 	int c, pri, flags;
403 	char *lp, *p, *q, line[MAXLINE + 1];
404 
405 	(void)strcpy(line, "vmunix: ");
406 	lp = line + strlen(line);
407 	for (p = msg; *p != '\0'; ) {
408 		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
409 		pri = DEFSPRI;
410 		if (*p == '<') {
411 			pri = 0;
412 			while (isdigit(*++p))
413 				pri = 10 * pri + (*p - '0');
414 			if (*p == '>')
415 				++p;
416 		} else {
417 			/* kernel printf's come out on console */
418 			flags |= IGN_CONS;
419 		}
420 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
421 			pri = DEFSPRI;
422 		q = lp;
423 		while (*p != '\0' && (c = *p++) != '\n' &&
424 		    q < &line[MAXLINE])
425 			*q++ = c;
426 		*q = '\0';
427 		logmsg(pri, line, LocalHostName, flags);
428 	}
429 }
430 
431 time_t	now;
432 
433 /*
434  * Log a message to the appropriate log files, users, etc. based on
435  * the priority.
436  */
437 void
438 logmsg(pri, msg, from, flags)
439 	int pri;
440 	char *msg, *from;
441 	int flags;
442 {
443 	struct filed *f;
444 	int fac, msglen, omask, prilev;
445 	char *timestamp;
446 
447 	dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
448 	    pri, flags, from, msg);
449 
450 	omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
451 
452 	/*
453 	 * Check to see if msg looks non-standard.
454 	 */
455 	msglen = strlen(msg);
456 	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
457 	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
458 		flags |= ADDDATE;
459 
460 	(void)time(&now);
461 	if (flags & ADDDATE)
462 		timestamp = ctime(&now) + 4;
463 	else {
464 		timestamp = msg;
465 		msg += 16;
466 		msglen -= 16;
467 	}
468 
469 	/* extract facility and priority level */
470 	if (flags & MARK)
471 		fac = LOG_NFACILITIES;
472 	else
473 		fac = LOG_FAC(pri);
474 	prilev = LOG_PRI(pri);
475 
476 	/* log the message to the particular outputs */
477 	if (!Initialized) {
478 		f = &consfile;
479 		f->f_file = open(ctty, O_WRONLY, 0);
480 
481 		if (f->f_file >= 0) {
482 			fprintlog(f, flags, msg);
483 			(void)close(f->f_file);
484 		}
485 		(void)sigsetmask(omask);
486 		return;
487 	}
488 	for (f = Files; f; f = f->f_next) {
489 		/* skip messages that are incorrect priority */
490 		if (f->f_pmask[fac] < prilev ||
491 		    f->f_pmask[fac] == INTERNAL_NOPRI)
492 			continue;
493 
494 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
495 			continue;
496 
497 		/* don't output marks to recently written files */
498 		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
499 			continue;
500 
501 		/*
502 		 * suppress duplicate lines to this file
503 		 */
504 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
505 		    !strcmp(msg, f->f_prevline) &&
506 		    !strcmp(from, f->f_prevhost)) {
507 			(void)strncpy(f->f_lasttime, timestamp, 15);
508 			f->f_prevcount++;
509 			dprintf("msg repeated %d times, %ld sec of %d\n",
510 			    f->f_prevcount, now - f->f_time,
511 			    repeatinterval[f->f_repeatcount]);
512 			/*
513 			 * If domark would have logged this by now,
514 			 * flush it now (so we don't hold isolated messages),
515 			 * but back off so we'll flush less often
516 			 * in the future.
517 			 */
518 			if (now > REPEATTIME(f)) {
519 				fprintlog(f, flags, (char *)NULL);
520 				BACKOFF(f);
521 			}
522 		} else {
523 			/* new line, save it */
524 			if (f->f_prevcount)
525 				fprintlog(f, 0, (char *)NULL);
526 			f->f_repeatcount = 0;
527 			(void)strncpy(f->f_lasttime, timestamp, 15);
528 			(void)strncpy(f->f_prevhost, from,
529 					sizeof(f->f_prevhost));
530 			if (msglen < MAXSVLINE) {
531 				f->f_prevlen = msglen;
532 				f->f_prevpri = pri;
533 				(void)strcpy(f->f_prevline, msg);
534 				fprintlog(f, flags, (char *)NULL);
535 			} else {
536 				f->f_prevline[0] = 0;
537 				f->f_prevlen = 0;
538 				fprintlog(f, flags, msg);
539 			}
540 		}
541 	}
542 	(void)sigsetmask(omask);
543 }
544 
545 void
546 fprintlog(f, flags, msg)
547 	struct filed *f;
548 	int flags;
549 	char *msg;
550 {
551 	struct iovec iov[6];
552 	struct iovec *v;
553 	int l;
554 	char line[MAXLINE + 1], repbuf[80], greetings[200];
555 
556 	v = iov;
557 	if (f->f_type == F_WALL) {
558 		v->iov_base = greetings;
559 		v->iov_len = sprintf(greetings,
560 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
561 		    f->f_prevhost, ctime(&now));
562 		v++;
563 		v->iov_base = "";
564 		v->iov_len = 0;
565 		v++;
566 	} else {
567 		v->iov_base = f->f_lasttime;
568 		v->iov_len = 15;
569 		v++;
570 		v->iov_base = " ";
571 		v->iov_len = 1;
572 		v++;
573 	}
574 	v->iov_base = f->f_prevhost;
575 	v->iov_len = strlen(v->iov_base);
576 	v++;
577 	v->iov_base = " ";
578 	v->iov_len = 1;
579 	v++;
580 
581 	if (msg) {
582 		v->iov_base = msg;
583 		v->iov_len = strlen(msg);
584 	} else if (f->f_prevcount > 1) {
585 		v->iov_base = repbuf;
586 		v->iov_len = sprintf(repbuf, "last message repeated %d times",
587 		    f->f_prevcount);
588 	} else {
589 		v->iov_base = f->f_prevline;
590 		v->iov_len = f->f_prevlen;
591 	}
592 	v++;
593 
594 	dprintf("Logging to %s", TypeNames[f->f_type]);
595 	f->f_time = now;
596 
597 	switch (f->f_type) {
598 	case F_UNUSED:
599 		dprintf("\n");
600 		break;
601 
602 	case F_FORW:
603 		dprintf(" %s\n", f->f_un.f_forw.f_hname);
604 		l = sprintf(line, "<%d>%.15s %s", f->f_prevpri,
605 		    iov[0].iov_base, iov[4].iov_base);
606 		if (l > MAXLINE)
607 			l = MAXLINE;
608 		if (sendto(finet, line, l, 0,
609 		    (struct sockaddr *)&f->f_un.f_forw.f_addr,
610 		    sizeof(f->f_un.f_forw.f_addr)) != l) {
611 			int e = errno;
612 			(void)close(f->f_file);
613 			f->f_type = F_UNUSED;
614 			errno = e;
615 			logerror("sendto");
616 		}
617 		break;
618 
619 	case F_CONSOLE:
620 		if (flags & IGN_CONS) {
621 			dprintf(" (ignored)\n");
622 			break;
623 		}
624 		/* FALLTHROUGH */
625 
626 	case F_TTY:
627 	case F_FILE:
628 		dprintf(" %s\n", f->f_un.f_fname);
629 		if (f->f_type != F_FILE) {
630 			v->iov_base = "\r\n";
631 			v->iov_len = 2;
632 		} else {
633 			v->iov_base = "\n";
634 			v->iov_len = 1;
635 		}
636 	again:
637 		if (writev(f->f_file, iov, 6) < 0) {
638 			int e = errno;
639 			(void)close(f->f_file);
640 			/*
641 			 * Check for errors on TTY's due to loss of tty
642 			 */
643 			if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
644 				f->f_file = open(f->f_un.f_fname,
645 				    O_WRONLY|O_APPEND, 0);
646 				if (f->f_file < 0) {
647 					f->f_type = F_UNUSED;
648 					logerror(f->f_un.f_fname);
649 				} else
650 					goto again;
651 			} else {
652 				f->f_type = F_UNUSED;
653 				errno = e;
654 				logerror(f->f_un.f_fname);
655 			}
656 		} else if (flags & SYNC_FILE)
657 			(void)fsync(f->f_file);
658 		break;
659 
660 	case F_USERS:
661 	case F_WALL:
662 		dprintf("\n");
663 		v->iov_base = "\r\n";
664 		v->iov_len = 2;
665 		wallmsg(f, iov);
666 		break;
667 	}
668 	f->f_prevcount = 0;
669 }
670 
671 /*
672  *  WALLMSG -- Write a message to the world at large
673  *
674  *	Write the specified message to either the entire
675  *	world, or a list of approved users.
676  */
677 void
678 wallmsg(f, iov)
679 	struct filed *f;
680 	struct iovec *iov;
681 {
682 	static int reenter;			/* avoid calling ourselves */
683 	FILE *uf;
684 	struct utmp ut;
685 	int i;
686 	char *p;
687 	char line[sizeof(ut.ut_line) + 1];
688 
689 	if (reenter++)
690 		return;
691 	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
692 		logerror(_PATH_UTMP);
693 		reenter = 0;
694 		return;
695 	}
696 	/* NOSTRICT */
697 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
698 		if (ut.ut_name[0] == '\0')
699 			continue;
700 		strncpy(line, ut.ut_line, sizeof(ut.ut_line));
701 		line[sizeof(ut.ut_line)] = '\0';
702 		if (f->f_type == F_WALL) {
703 			if ((p = ttymsg(iov, 6, line, 60*5)) != NULL) {
704 				errno = 0;	/* already in msg */
705 				logerror(p);
706 			}
707 			continue;
708 		}
709 		/* should we send the message to this user? */
710 		for (i = 0; i < MAXUNAMES; i++) {
711 			if (!f->f_un.f_uname[i][0])
712 				break;
713 			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
714 			    UT_NAMESIZE)) {
715 				if ((p = ttymsg(iov, 6, line, 60*5)) != NULL) {
716 					errno = 0;	/* already in msg */
717 					logerror(p);
718 				}
719 				break;
720 			}
721 		}
722 	}
723 	(void)fclose(uf);
724 	reenter = 0;
725 }
726 
727 void
728 reapchild(signo)
729 	int signo;
730 {
731 	union wait status;
732 
733 	while (wait3((int *)&status, WNOHANG, (struct rusage *)NULL) > 0)
734 		;
735 }
736 
737 /*
738  * Return a printable representation of a host address.
739  */
740 char *
741 cvthname(f)
742 	struct sockaddr_in *f;
743 {
744 	struct hostent *hp;
745 	char *p;
746 
747 	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
748 
749 	if (f->sin_family != AF_INET) {
750 		dprintf("Malformed from address\n");
751 		return ("???");
752 	}
753 	hp = gethostbyaddr((char *)&f->sin_addr,
754 	    sizeof(struct in_addr), f->sin_family);
755 	if (hp == 0) {
756 		dprintf("Host name for your address (%s) unknown\n",
757 			inet_ntoa(f->sin_addr));
758 		return (inet_ntoa(f->sin_addr));
759 	}
760 	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
761 		*p = '\0';
762 	return (hp->h_name);
763 }
764 
765 void
766 domark(signo)
767 	int signo;
768 {
769 	struct filed *f;
770 
771 	now = time((time_t *)NULL);
772 	MarkSeq += TIMERINTVL;
773 	if (MarkSeq >= MarkInterval) {
774 		logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
775 		MarkSeq = 0;
776 	}
777 
778 	for (f = Files; f; f = f->f_next) {
779 		if (f->f_prevcount && now >= REPEATTIME(f)) {
780 			dprintf("flush %s: repeated %d times, %d sec.\n",
781 			    TypeNames[f->f_type], f->f_prevcount,
782 			    repeatinterval[f->f_repeatcount]);
783 			fprintlog(f, 0, (char *)NULL);
784 			BACKOFF(f);
785 		}
786 	}
787 	(void)alarm(TIMERINTVL);
788 }
789 
790 /*
791  * Print syslogd errors some place.
792  */
793 void
794 logerror(type)
795 	char *type;
796 {
797 	char buf[100];
798 
799 	if (errno)
800 		(void)snprintf(buf,
801 		    sizeof(buf), "syslogd: %s: %s", type, strerror(errno));
802 	else
803 		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
804 	errno = 0;
805 	dprintf("%s\n", buf);
806 	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
807 }
808 
809 void
810 die(signo)
811 	int signo;
812 {
813 	struct filed *f;
814 	char buf[100];
815 
816 	for (f = Files; f != NULL; f = f->f_next) {
817 		/* flush any pending output */
818 		if (f->f_prevcount)
819 			fprintlog(f, 0, (char *)NULL);
820 	}
821 	if (signo) {
822 		dprintf("syslogd: exiting on signal %d\n", signo);
823 		(void)sprintf(buf, "exiting on signal %d", signo);
824 		errno = 0;
825 		logerror(buf);
826 	}
827 	(void)unlink(LogName);
828 	exit(0);
829 }
830 
831 /*
832  *  INIT -- Initialize syslogd from configuration table
833  */
834 void
835 init(signo)
836 	int signo;
837 {
838 	int i;
839 	FILE *cf;
840 	struct filed *f, *next, **nextp;
841 	char *p;
842 	char cline[LINE_MAX];
843 
844 	dprintf("init\n");
845 
846 	/*
847 	 *  Close all open log files.
848 	 */
849 	Initialized = 0;
850 	for (f = Files; f != NULL; f = next) {
851 		/* flush any pending output */
852 		if (f->f_prevcount)
853 			fprintlog(f, 0, (char *)NULL);
854 
855 		switch (f->f_type) {
856 		case F_FILE:
857 		case F_TTY:
858 		case F_CONSOLE:
859 		case F_FORW:
860 			(void)close(f->f_file);
861 			break;
862 		}
863 		next = f->f_next;
864 		free((char *)f);
865 	}
866 	Files = NULL;
867 	nextp = &Files;
868 
869 	/* open the configuration file */
870 	if ((cf = fopen(ConfFile, "r")) == NULL) {
871 		dprintf("cannot open %s\n", ConfFile);
872 		*nextp = (struct filed *)calloc(1, sizeof(*f));
873 		cfline("*.ERR\t/dev/console", *nextp);
874 		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
875 		cfline("*.PANIC\t*", (*nextp)->f_next);
876 		Initialized = 1;
877 		return;
878 	}
879 
880 	/*
881 	 *  Foreach line in the conf table, open that file.
882 	 */
883 	f = NULL;
884 	while (fgets(cline, sizeof(cline), cf) != NULL) {
885 		/*
886 		 * check for end-of-section, comments, strip off trailing
887 		 * spaces and newline character.
888 		 */
889 		for (p = cline; isspace(*p); ++p)
890 			continue;
891 		if (*p == NULL || *p == '#')
892 			continue;
893 		for (p = strchr(cline, '\0'); isspace(*--p);)
894 			continue;
895 		*++p = '\0';
896 		f = (struct filed *)calloc(1, sizeof(*f));
897 		*nextp = f;
898 		nextp = &f->f_next;
899 		cfline(cline, f);
900 	}
901 
902 	/* close the configuration file */
903 	(void)fclose(cf);
904 
905 	Initialized = 1;
906 
907 	if (Debug) {
908 		for (f = Files; f; f = f->f_next) {
909 			for (i = 0; i <= LOG_NFACILITIES; i++)
910 				if (f->f_pmask[i] == INTERNAL_NOPRI)
911 					printf("X ");
912 				else
913 					printf("%d ", f->f_pmask[i]);
914 			printf("%s: ", TypeNames[f->f_type]);
915 			switch (f->f_type) {
916 			case F_FILE:
917 			case F_TTY:
918 			case F_CONSOLE:
919 				printf("%s", f->f_un.f_fname);
920 				break;
921 
922 			case F_FORW:
923 				printf("%s", f->f_un.f_forw.f_hname);
924 				break;
925 
926 			case F_USERS:
927 				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
928 					printf("%s, ", f->f_un.f_uname[i]);
929 				break;
930 			}
931 			printf("\n");
932 		}
933 	}
934 
935 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
936 	dprintf("syslogd: restarted\n");
937 }
938 
939 /*
940  * Crack a configuration file line
941  */
942 void
943 cfline(line, f)
944 	char *line;
945 	struct filed *f;
946 {
947 	struct hostent *hp;
948 	int i, pri;
949 	char *bp, *p, *q;
950 	char buf[MAXLINE], ebuf[100];
951 
952 	dprintf("cfline(%s)\n", line);
953 
954 	errno = 0;	/* keep strerror() stuff out of logerror messages */
955 
956 	/* clear out file entry */
957 	memset(f, 0, sizeof(*f));
958 	for (i = 0; i <= LOG_NFACILITIES; i++)
959 		f->f_pmask[i] = INTERNAL_NOPRI;
960 
961 	/* scan through the list of selectors */
962 	for (p = line; *p && *p != '\t';) {
963 
964 		/* find the end of this facility name list */
965 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
966 			continue;
967 
968 		/* collect priority name */
969 		for (bp = buf; *q && !strchr("\t,;", *q); )
970 			*bp++ = *q++;
971 		*bp = '\0';
972 
973 		/* skip cruft */
974 		while (strchr(", ;", *q))
975 			q++;
976 
977 		/* decode priority name */
978 		if (*buf == '*')
979 			pri = LOG_PRIMASK + 1;
980 		else {
981 			pri = decode(buf, prioritynames);
982 			if (pri < 0) {
983 				(void)sprintf(ebuf,
984 				    "unknown priority name \"%s\"", buf);
985 				logerror(ebuf);
986 				return;
987 			}
988 		}
989 
990 		/* scan facilities */
991 		while (*p && !strchr("\t.;", *p)) {
992 			for (bp = buf; *p && !strchr("\t,;.", *p); )
993 				*bp++ = *p++;
994 			*bp = '\0';
995 			if (*buf == '*')
996 				for (i = 0; i < LOG_NFACILITIES; i++)
997 					f->f_pmask[i] = pri;
998 			else {
999 				i = decode(buf, facilitynames);
1000 				if (i < 0) {
1001 					(void)sprintf(ebuf,
1002 					    "unknown facility name \"%s\"",
1003 					    buf);
1004 					logerror(ebuf);
1005 					return;
1006 				}
1007 				f->f_pmask[i >> 3] = pri;
1008 			}
1009 			while (*p == ',' || *p == ' ')
1010 				p++;
1011 		}
1012 
1013 		p = q;
1014 	}
1015 
1016 	/* skip to action part */
1017 	while (*p == '\t')
1018 		p++;
1019 
1020 	switch (*p)
1021 	{
1022 	case '@':
1023 		if (!InetInuse)
1024 			break;
1025 		(void)strcpy(f->f_un.f_forw.f_hname, ++p);
1026 		hp = gethostbyname(p);
1027 		if (hp == NULL) {
1028 			extern int h_errno;
1029 
1030 			logerror(hstrerror(h_errno));
1031 			break;
1032 		}
1033 		memset(&f->f_un.f_forw.f_addr, 0,
1034 			 sizeof(f->f_un.f_forw.f_addr));
1035 		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1036 		f->f_un.f_forw.f_addr.sin_port = LogPort;
1037 		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
1038 		f->f_type = F_FORW;
1039 		break;
1040 
1041 	case '/':
1042 		(void)strcpy(f->f_un.f_fname, p);
1043 		if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1044 			f->f_file = F_UNUSED;
1045 			logerror(p);
1046 			break;
1047 		}
1048 		if (isatty(f->f_file))
1049 			f->f_type = F_TTY;
1050 		else
1051 			f->f_type = F_FILE;
1052 		if (strcmp(p, ctty) == 0)
1053 			f->f_type = F_CONSOLE;
1054 		break;
1055 
1056 	case '*':
1057 		f->f_type = F_WALL;
1058 		break;
1059 
1060 	default:
1061 		for (i = 0; i < MAXUNAMES && *p; i++) {
1062 			for (q = p; *q && *q != ','; )
1063 				q++;
1064 			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1065 			if ((q - p) > UT_NAMESIZE)
1066 				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1067 			else
1068 				f->f_un.f_uname[i][q - p] = '\0';
1069 			while (*q == ',' || *q == ' ')
1070 				q++;
1071 			p = q;
1072 		}
1073 		f->f_type = F_USERS;
1074 		break;
1075 	}
1076 }
1077 
1078 
1079 /*
1080  *  Decode a symbolic name to a numeric value
1081  */
1082 int
1083 decode(name, codetab)
1084 	const char *name;
1085 	CODE *codetab;
1086 {
1087 	CODE *c;
1088 	char *p, buf[40];
1089 
1090 	if (isdigit(*name))
1091 		return (atoi(name));
1092 
1093 	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1094 		if (isupper(*name))
1095 			*p = tolower(*name);
1096 		else
1097 			*p = *name;
1098 	}
1099 	*p = '\0';
1100 	for (c = codetab; c->c_name; c++)
1101 		if (!strcmp(buf, c->c_name))
1102 			return (c->c_val);
1103 
1104 	return (-1);
1105 }
1106