xref: /openbsd-src/usr.sbin/syslogd/syslogd.c (revision 3a3fbb3f2e2521ab7c4a56b7ff7462ebd9095ec5)
1 /*	$OpenBSD: syslogd.c,v 1.48 2001/12/02 02:23:45 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 1983, 1988, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #ifndef lint
37 static char copyright[] =
38 "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
39 	The Regents of the University of California.  All rights reserved.\n";
40 #endif /* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";
45 #else
46 static char rcsid[] = "$OpenBSD: syslogd.c,v 1.48 2001/12/02 02:23:45 deraadt Exp $";
47 #endif
48 #endif /* not lint */
49 
50 /*
51  *  syslogd -- log system messages
52  *
53  * This program implements a system log. It takes a series of lines.
54  * Each line may have a priority, signified as "<n>" as
55  * the first characters of the line.  If this is
56  * not present, a default priority is used.
57  *
58  * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
59  * cause it to reread its configuration file.
60  *
61  * Defined Constants:
62  *
63  * MAXLINE -- the maximimum line length that can be handled.
64  * DEFUPRI -- the default priority for user messages
65  * DEFSPRI -- the default priority for kernel messages
66  *
67  * Author: Eric Allman
68  * extensive changes by Ralph Campbell
69  * more extensive changes by Eric Allman (again)
70  */
71 
72 #define	MAXLINE		1024		/* maximum line length */
73 #define	MAXSVLINE	120		/* maximum saved line length */
74 #define DEFUPRI		(LOG_USER|LOG_NOTICE)
75 #define DEFSPRI		(LOG_KERN|LOG_CRIT)
76 #define TIMERINTVL	30		/* interval for checking flush, mark */
77 #define TTYMSGTIME	1		/* timeout passed to ttymsg */
78 
79 #include <sys/param.h>
80 #include <sys/ioctl.h>
81 #include <sys/stat.h>
82 #include <sys/wait.h>
83 #include <sys/socket.h>
84 #include <sys/msgbuf.h>
85 #include <sys/uio.h>
86 #include <sys/sysctl.h>
87 #include <sys/un.h>
88 #include <sys/time.h>
89 #include <sys/resource.h>
90 
91 #include <netinet/in.h>
92 #include <netdb.h>
93 #include <arpa/inet.h>
94 
95 #include <ctype.h>
96 #include <errno.h>
97 #include <err.h>
98 #include <fcntl.h>
99 #include <paths.h>
100 #include <signal.h>
101 #include <stdio.h>
102 #include <stdlib.h>
103 #include <string.h>
104 #include <unistd.h>
105 #include <utmp.h>
106 #include <vis.h>
107 
108 #define SYSLOG_NAMES
109 #include <sys/syslog.h>
110 
111 char	*ConfFile = _PATH_LOGCONF;
112 char	*PidFile = _PATH_LOGPID;
113 char	ctty[] = _PATH_CONSOLE;
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 
133 struct 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];
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];	/* 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  */
162 int	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 
178 char	*TypeNames[7] = {
179 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
180 	"FORW",		"USERS",	"WALL"
181 };
182 
183 struct	filed *Files;
184 struct	filed consfile;
185 
186 int	Debug;			/* debug flag */
187 char	LocalHostName[MAXHOSTNAMELEN];	/* our hostname */
188 char	*LocalDomain;		/* our local domain name */
189 int	InetInuse = 0;		/* non-zero if INET sockets are being used */
190 int	finet;			/* Internet datagram socket */
191 int	LogPort;		/* port number for INET connections */
192 int	Initialized = 0;	/* set when we have initialized ourselves */
193 
194 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
195 int	MarkSeq = 0;		/* mark sequence number */
196 
197 volatile sig_atomic_t MarkSet;
198 volatile sig_atomic_t WantDie;
199 volatile sig_atomic_t DoInit;
200 
201 int	SecureMode = 1;		/* when true, speak only unix domain socks */
202 
203 void	cfline __P((char *, struct filed *, char *));
204 char   *cvthname __P((struct sockaddr_in *));
205 int	decode __P((const char *, CODE *));
206 void	dodie __P((int));
207 void	doinit __P((int));
208 void	die __P((int));
209 void	domark __P((int));
210 void	markit __P((void));
211 void	fprintlog __P((struct filed *, int, char *));
212 void	init __P((void));
213 void	logerror __P((char *));
214 void	logmsg __P((int, char *, char *, int));
215 void	printline __P((char *, char *));
216 void	printsys __P((char *));
217 void	reapchild __P((int));
218 char   *ttymsg __P((struct iovec *, int, char *, int));
219 void	usage __P((void));
220 void	wallmsg __P((struct filed *, struct iovec *));
221 int	getmsgbufsize __P((void));
222 
223 #define MAXFUNIX	21
224 
225 int nfunix = 1;
226 char *funixn[MAXFUNIX] = { _PATH_LOG };
227 int funix[MAXFUNIX];
228 
229 int
230 main(argc, argv)
231 	int argc;
232 	char *argv[];
233 {
234 	int ch, i, fklog, len, linesize, fdsrmax = 0;
235 	struct sockaddr_un sunx, fromunix;
236 	struct sockaddr_in sin, frominet;
237 	fd_set *fdsr = NULL;
238 	char *p, *line;
239 	FILE *fp;
240 
241 	while ((ch = getopt(argc, argv, "duf:m:p:a:")) != -1)
242 		switch (ch) {
243 		case 'd':		/* debug */
244 			Debug++;
245 			break;
246 		case 'f':		/* configuration file */
247 			ConfFile = optarg;
248 			break;
249 		case 'm':		/* mark interval */
250 			MarkInterval = atoi(optarg) * 60;
251 			break;
252 		case 'p':		/* path */
253 			funixn[0] = optarg;
254 			break;
255 		case 'u':		/* allow udp input port */
256 			SecureMode = 0;
257 			break;
258 		case 'a':
259 			if (nfunix >= MAXFUNIX)
260 				fprintf(stderr,
261 				    "syslogd: out of descriptors, ignoring %s\n",
262 				    optarg);
263 			else if (strlen(optarg) >= sizeof(sunx.sun_path))
264 				fprintf(stderr,
265 				    "syslogd: path too long, ignoring %s\n",
266 				    optarg);
267 			else
268 				funixn[nfunix++] = optarg;
269 			break;
270 		case '?':
271 		default:
272 			usage();
273 		}
274 	if ((argc -= optind) != 0)
275 		usage();
276 
277 	if (!Debug)
278 		(void)daemon(0, 0);
279 	else
280 		setlinebuf(stdout);
281 
282 	consfile.f_type = F_CONSOLE;
283 	(void)strlcpy(consfile.f_un.f_fname, ctty,
284 	    sizeof(consfile.f_un.f_fname));
285 	(void)gethostname(LocalHostName, sizeof(LocalHostName));
286 	if ((p = strchr(LocalHostName, '.')) != NULL) {
287 		*p++ = '\0';
288 		LocalDomain = p;
289 	} else
290 		LocalDomain = "";
291 
292 	linesize = getmsgbufsize();
293 	if (linesize < MAXLINE)
294 		linesize = MAXLINE;
295 	linesize++;
296 	line = malloc(linesize);
297 
298 	(void)signal(SIGHUP, doinit);
299 	(void)signal(SIGTERM, dodie);
300 	(void)signal(SIGINT, Debug ? dodie : SIG_IGN);
301 	(void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
302 	(void)signal(SIGCHLD, reapchild);
303 	(void)signal(SIGALRM, domark);
304 	(void)alarm(TIMERINTVL);
305 
306 #ifndef SUN_LEN
307 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
308 #endif
309 	for (i = 0; i < nfunix; i++) {
310 		(void)unlink(funixn[i]);
311 
312 		memset(&sunx, 0, sizeof(sunx));
313 		sunx.sun_family = AF_UNIX;
314 		(void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
315 		funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
316 		if (funix[i] < 0 ||
317 		    bind(funix[i], (struct sockaddr *)&sunx,
318 		    SUN_LEN(&sunx)) < 0 ||
319 		    chmod(funixn[i], 0666) < 0) {
320 			(void) snprintf(line, sizeof line, "cannot create %s",
321 			    funixn[i]);
322 			logerror(line);
323 			dprintf("cannot create %s (%d)\n", funixn[i], errno);
324 			if (i == 0)
325 				die(0);
326 		}
327 	}
328 	finet = socket(AF_INET, SOCK_DGRAM, 0);
329 	if (finet >= 0) {
330 		struct servent *sp;
331 
332 		sp = getservbyname("syslog", "udp");
333 		if (sp == NULL) {
334 			errno = 0;
335 			logerror("syslog/udp: unknown service");
336 			die(0);
337 		}
338 		memset(&sin, 0, sizeof(sin));
339 		sin.sin_len = sizeof(sin);
340 		sin.sin_family = AF_INET;
341 		sin.sin_port = LogPort = sp->s_port;
342 		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
343 			logerror("bind");
344 			if (!Debug)
345 				die(0);
346 		} else {
347 			InetInuse = 1;
348 		}
349 	}
350 	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0)
351 		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
352 
353 	/* tuck my process id away */
354 	if (!Debug) {
355 		fp = fopen(PidFile, "w");
356 		if (fp != NULL) {
357 			fprintf(fp, "%d\n", getpid());
358 			(void) fclose(fp);
359 		}
360 	}
361 
362 	dprintf("off & running....\n");
363 
364 	init();
365 
366 	if (fklog != -1 && fklog > fdsrmax)
367 		fdsrmax = fklog;
368 	if (finet != -1 && finet > fdsrmax)
369 		fdsrmax = finet;
370 	for (i = 0; i < nfunix; i++) {
371 		if (funix[i] != -1 && funix[i] > fdsrmax)
372 			fdsrmax = funix[i];
373 	}
374 
375 	fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS),
376 	    sizeof(fd_mask));
377 	if (fdsr == NULL)
378 		errx(1, "calloc fd_set");
379 
380 	for (;;) {
381 		if (MarkSet)
382 			markit();
383 		if (WantDie)
384 			die(WantDie);
385 
386 		if (DoInit) {
387 			init();
388 			DoInit = 0;
389 		}
390 
391 		bzero(fdsr, howmany(fdsrmax+1, NFDBITS) *
392 		    sizeof(fd_mask));
393 
394 		if (fklog != -1)
395 			FD_SET(fklog, fdsr);
396 		if (finet != -1)
397 			FD_SET(finet, fdsr);
398 		for (i = 0; i < nfunix; i++) {
399 			if (funix[i] != -1)
400 				FD_SET(funix[i], fdsr);
401 		}
402 
403 		switch (select(fdsrmax+1, fdsr, NULL, NULL, NULL)) {
404 		case 0:
405 			continue;
406 		case -1:
407 			if (errno != EINTR)
408 				logerror("select");
409 			continue;
410 		}
411 
412 		if (fklog != -1 && FD_ISSET(fklog, fdsr)) {
413 			i = read(fklog, line, linesize - 1);
414 			if (i > 0) {
415 				line[i] = '\0';
416 				printsys(line);
417 			} else if (i < 0 && errno != EINTR) {
418 				logerror("klog");
419 				fklog = -1;
420 			}
421 		}
422 		if (finet != -1 && FD_ISSET(finet, fdsr)) {
423 			len = sizeof(frominet);
424 			i = recvfrom(finet, line, MAXLINE, 0,
425 			    (struct sockaddr *)&frominet, &len);
426 			if (SecureMode) {
427 				/* silently drop it */
428 			} else {
429 				if (i > 0) {
430 					line[i] = '\0';
431 					printline(cvthname(&frominet), line);
432 				} else if (i < 0 && errno != EINTR)
433 					logerror("recvfrom inet");
434 			}
435 		}
436 		for (i = 0; i < nfunix; i++) {
437 			if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) {
438 				len = sizeof(fromunix);
439 				len = recvfrom(funix[i], line, MAXLINE, 0,
440 				    (struct sockaddr *)&fromunix, &len);
441 				if (len > 0) {
442 					line[len] = '\0';
443 					printline(LocalHostName, line);
444 				} else if (len < 0 && errno != EINTR)
445 					logerror("recvfrom unix");
446 			}
447 		}
448 	}
449 	if (fdsr)
450 		free(fdsr);
451 }
452 
453 void
454 usage()
455 {
456 
457 	(void)fprintf(stderr,
458 	    "usage: syslogd [-u] [-f conffile] [-m markinterval] [-p logpath] [-a logpath]\n");
459 	exit(1);
460 }
461 
462 /*
463  * Take a raw input line, decode the message, and print the message
464  * on the appropriate log files.
465  */
466 void
467 printline(hname, msg)
468 	char *hname;
469 	char *msg;
470 {
471 	int pri;
472 	char *p, *q, line[MAXLINE + 1];
473 
474 	/* test for special codes */
475 	pri = DEFUPRI;
476 	p = msg;
477 	if (*p == '<') {
478 		pri = 0;
479 		while (isdigit(*++p))
480 			pri = 10 * pri + (*p - '0');
481 		if (*p == '>')
482 			++p;
483 	}
484 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
485 		pri = DEFUPRI;
486 
487 	/* don't allow users to log kernel messages */
488 	if (LOG_FAC(pri) == LOG_KERN)
489 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
490 
491 	for (q = line; *p && q < &line[sizeof(line) - 4]; p++) {
492 		if (*p == '\n')
493 			*q++ = ' ';
494 		else
495 			q = vis(q, *p, 0, 0);
496 	}
497 	*q = '\0';
498 
499 	logmsg(pri, line, hname, 0);
500 }
501 
502 /*
503  * Take a raw input line from /dev/klog, split and format similar to syslog().
504  */
505 void
506 printsys(msg)
507 	char *msg;
508 {
509 	int c, pri, flags;
510 	char *lp, *p, *q, line[MAXLINE + 1];
511 
512 	snprintf(line, sizeof line, "%s: ", _PATH_UNIX);
513 	lp = line + strlen(line);
514 	for (p = msg; *p != '\0'; ) {
515 		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
516 		pri = DEFSPRI;
517 		if (*p == '<') {
518 			pri = 0;
519 			while (isdigit(*++p))
520 				pri = 10 * pri + (*p - '0');
521 			if (*p == '>')
522 				++p;
523 		} else {
524 			/* kernel printf's come out on console */
525 			flags |= IGN_CONS;
526 		}
527 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
528 			pri = DEFSPRI;
529 
530 		q = lp;
531 		while (*p && (c = *p++) != '\n' && q < &line[sizeof(line) - 4])
532 			q = vis(q, c, 0, 0);
533 
534 		logmsg(pri, line, LocalHostName, flags);
535 	}
536 }
537 
538 time_t	now;
539 
540 /*
541  * Log a message to the appropriate log files, users, etc. based on
542  * the priority.
543  */
544 void
545 logmsg(pri, msg, from, flags)
546 	int pri;
547 	char *msg, *from;
548 	int flags;
549 {
550 	struct filed *f;
551 	int fac, msglen, prilev, i;
552 	sigset_t mask, omask;
553 	char *timestamp;
554  	char prog[NAME_MAX+1];
555 
556 	dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
557 	    pri, flags, from, msg);
558 
559 	sigemptyset(&mask);
560 	sigaddset(&mask, SIGALRM);
561 	sigaddset(&mask, SIGHUP);
562 	sigprocmask(SIG_BLOCK, &mask, &omask);
563 
564 	/*
565 	 * Check to see if msg looks non-standard.
566 	 */
567 	msglen = strlen(msg);
568 	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
569 	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
570 		flags |= ADDDATE;
571 
572 	(void)time(&now);
573 	if (flags & ADDDATE)
574 		timestamp = ctime(&now) + 4;
575 	else {
576 		timestamp = msg;
577 		msg += 16;
578 		msglen -= 16;
579 	}
580 
581 	/* extract facility and priority level */
582 	if (flags & MARK)
583 		fac = LOG_NFACILITIES;
584 	else
585 		fac = LOG_FAC(pri);
586 	prilev = LOG_PRI(pri);
587 
588 	/* extract program name */
589 	for(i = 0; i < NAME_MAX; i++) {
590 		if (!isalnum(msg[i]))
591 			break;
592 		prog[i] = msg[i];
593 	}
594 	prog[i] = 0;
595 
596 	/* log the message to the particular outputs */
597 	if (!Initialized) {
598 		f = &consfile;
599 		f->f_file = open(ctty, O_WRONLY|O_NONBLOCK, 0);
600 
601 		if (f->f_file >= 0) {
602 			fprintlog(f, flags, msg);
603 			(void)close(f->f_file);
604 			f->f_file = -1;
605 		}
606 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
607 		return;
608 	}
609 	for (f = Files; f; f = f->f_next) {
610 		/* skip messages that are incorrect priority */
611 		if (f->f_pmask[fac] < prilev ||
612 		    f->f_pmask[fac] == INTERNAL_NOPRI)
613 			continue;
614 
615 		/* skip messages with the incorrect program name */
616 		if (f->f_program)
617 			if (strcmp(prog, f->f_program) != 0)
618 				continue;
619 
620 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
621 			continue;
622 
623 		/* don't output marks to recently written files */
624 		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
625 			continue;
626 
627 		/*
628 		 * suppress duplicate lines to this file
629 		 */
630 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
631 		    !strcmp(msg, f->f_prevline) &&
632 		    !strcmp(from, f->f_prevhost)) {
633 			strlcpy(f->f_lasttime, timestamp, 16);
634 			f->f_prevcount++;
635 			dprintf("msg repeated %d times, %ld sec of %d\n",
636 			    f->f_prevcount, (long)(now - f->f_time),
637 			    repeatinterval[f->f_repeatcount]);
638 			/*
639 			 * If domark would have logged this by now,
640 			 * flush it now (so we don't hold isolated messages),
641 			 * but back off so we'll flush less often
642 			 * in the future.
643 			 */
644 			if (now > REPEATTIME(f)) {
645 				fprintlog(f, flags, (char *)NULL);
646 				BACKOFF(f);
647 			}
648 		} else {
649 			/* new line, save it */
650 			if (f->f_prevcount)
651 				fprintlog(f, 0, (char *)NULL);
652 			f->f_repeatcount = 0;
653 			f->f_prevpri = pri;
654 			strlcpy(f->f_lasttime, timestamp, 16);
655 			strlcpy(f->f_prevhost, from,
656 			    sizeof(f->f_prevhost));
657 			if (msglen < MAXSVLINE) {
658 				f->f_prevlen = msglen;
659 				strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
660 				fprintlog(f, flags, (char *)NULL);
661 			} else {
662 				f->f_prevline[0] = 0;
663 				f->f_prevlen = 0;
664 				fprintlog(f, flags, msg);
665 			}
666 		}
667 	}
668 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
669 }
670 
671 void
672 fprintlog(f, flags, msg)
673 	struct filed *f;
674 	int flags;
675 	char *msg;
676 {
677 	struct iovec iov[6];
678 	struct iovec *v;
679 	int l;
680 	char line[MAXLINE + 1], repbuf[80], greetings[500];
681 
682 	v = iov;
683 	if (f->f_type == F_WALL) {
684 		v->iov_base = greetings;
685 		v->iov_len = snprintf(greetings, sizeof(greetings),
686 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
687 		    f->f_prevhost, ctime(&now));
688 		if (v->iov_len >= sizeof(greetings))
689 			v->iov_len = sizeof(greetings) - 1;
690 		v++;
691 		v->iov_base = "";
692 		v->iov_len = 0;
693 		v++;
694 	} else {
695 		v->iov_base = f->f_lasttime;
696 		v->iov_len = 15;
697 		v++;
698 		v->iov_base = " ";
699 		v->iov_len = 1;
700 		v++;
701 	}
702 	v->iov_base = f->f_prevhost;
703 	v->iov_len = strlen(v->iov_base);
704 	v++;
705 	v->iov_base = " ";
706 	v->iov_len = 1;
707 	v++;
708 
709 	if (msg) {
710 		v->iov_base = msg;
711 		v->iov_len = strlen(msg);
712 	} else if (f->f_prevcount > 1) {
713 		v->iov_base = repbuf;
714 		v->iov_len = snprintf(repbuf, sizeof repbuf,
715 		    "last message repeated %d times", f->f_prevcount);
716 	} else {
717 		v->iov_base = f->f_prevline;
718 		v->iov_len = f->f_prevlen;
719 	}
720 	v++;
721 
722 	dprintf("Logging to %s", TypeNames[f->f_type]);
723 	f->f_time = now;
724 
725 	switch (f->f_type) {
726 	case F_UNUSED:
727 		dprintf("\n");
728 		break;
729 
730 	case F_FORW:
731 		dprintf(" %s\n", f->f_un.f_forw.f_hname);
732 		l = snprintf(line, sizeof(line) - 1, "<%d>%.15s %s", f->f_prevpri,
733 		    (char *)iov[0].iov_base, (char *)iov[4].iov_base);
734 		if (l > MAXLINE)
735 			l = MAXLINE;
736 		if (sendto(finet, line, l, 0,
737 		    (struct sockaddr *)&f->f_un.f_forw.f_addr,
738 		    sizeof(f->f_un.f_forw.f_addr)) != l) {
739 			f->f_type = F_UNUSED;
740 			logerror("sendto");
741 		}
742 		break;
743 
744 	case F_CONSOLE:
745 		if (flags & IGN_CONS) {
746 			dprintf(" (ignored)\n");
747 			break;
748 		}
749 		/* FALLTHROUGH */
750 
751 	case F_TTY:
752 	case F_FILE:
753 		dprintf(" %s\n", f->f_un.f_fname);
754 		if (f->f_type != F_FILE) {
755 			v->iov_base = "\r\n";
756 			v->iov_len = 2;
757 		} else {
758 			v->iov_base = "\n";
759 			v->iov_len = 1;
760 		}
761 	again:
762 		if (writev(f->f_file, iov, 6) < 0) {
763 			int e = errno;
764 			(void)close(f->f_file);
765 			/*
766 			 * Check for errors on TTY's due to loss of tty
767 			 */
768 			if (e == EAGAIN) {
769 				/*
770 				 * Silently drop messages on blocked write.
771 				 * This can happen when logging to a locked tty.
772 				 */
773 				break;
774 			} else if ((e == EIO || e == EBADF) &&
775 			    f->f_type != F_FILE) {
776 				f->f_file = open(f->f_un.f_fname,
777 				    O_WRONLY|O_APPEND|O_NONBLOCK, 0);
778 				if (f->f_file < 0) {
779 					f->f_type = F_UNUSED;
780 					logerror(f->f_un.f_fname);
781 				} else
782 					goto again;
783 			} else {
784 				f->f_type = F_UNUSED;
785 				f->f_file = -1;
786 				errno = e;
787 				logerror(f->f_un.f_fname);
788 			}
789 		} else if (flags & SYNC_FILE)
790 			(void)fsync(f->f_file);
791 		break;
792 
793 	case F_USERS:
794 	case F_WALL:
795 		dprintf("\n");
796 		v->iov_base = "\r\n";
797 		v->iov_len = 2;
798 		wallmsg(f, iov);
799 		break;
800 	}
801 	f->f_prevcount = 0;
802 }
803 
804 /*
805  *  WALLMSG -- Write a message to the world at large
806  *
807  *	Write the specified message to either the entire
808  *	world, or a list of approved users.
809  */
810 void
811 wallmsg(f, iov)
812 	struct filed *f;
813 	struct iovec *iov;
814 {
815 	static int reenter;			/* avoid calling ourselves */
816 	FILE *uf;
817 	struct utmp ut;
818 	int i;
819 	char *p;
820 	char line[sizeof(ut.ut_line) + 1];
821 
822 	if (reenter++)
823 		return;
824 	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
825 		logerror(_PATH_UTMP);
826 		reenter = 0;
827 		return;
828 	}
829 	/* NOSTRICT */
830 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
831 		if (ut.ut_name[0] == '\0')
832 			continue;
833 		strlcpy(line, ut.ut_line, sizeof(line));
834 		if (f->f_type == F_WALL) {
835 			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
836 				errno = 0;	/* already in msg */
837 				logerror(p);
838 			}
839 			continue;
840 		}
841 		/* should we send the message to this user? */
842 		for (i = 0; i < MAXUNAMES; i++) {
843 			if (!f->f_un.f_uname[i][0])
844 				break;
845 			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
846 			    UT_NAMESIZE)) {
847 				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
848 								!= NULL) {
849 					errno = 0;	/* already in msg */
850 					logerror(p);
851 				}
852 				break;
853 			}
854 		}
855 	}
856 	(void)fclose(uf);
857 	reenter = 0;
858 }
859 
860 void
861 reapchild(signo)
862 	int signo;
863 {
864 	int save_errno = errno;
865 	int status;
866 
867 	while (waitpid(-1, &status, WNOHANG) > 0)
868 		;
869 	errno = save_errno;
870 }
871 
872 /*
873  * Return a printable representation of a host address.
874  */
875 char *
876 cvthname(f)
877 	struct sockaddr_in *f;
878 {
879 	struct hostent *hp;
880 	sigset_t omask, nmask;
881 	char *p;
882 
883 	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
884 
885 	if (f->sin_family != AF_INET) {
886 		dprintf("Malformed from address\n");
887 		return ("???");
888 	}
889 	sigemptyset(&nmask);
890 	sigaddset(&nmask, SIGHUP);
891 	sigprocmask(SIG_BLOCK, &nmask, &omask);
892 	hp = gethostbyaddr((char *)&f->sin_addr,
893 	    sizeof(struct in_addr), f->sin_family);
894 	sigprocmask(SIG_SETMASK, &omask, NULL);
895 	if (hp == 0) {
896 		dprintf("Host name for your address (%s) unknown\n",
897 			inet_ntoa(f->sin_addr));
898 		return (inet_ntoa(f->sin_addr));
899 	}
900 	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
901 		*p = '\0';
902 	return (hp->h_name);
903 }
904 
905 void
906 dodie(signo)
907 	int signo;
908 {
909 	WantDie = signo;
910 }
911 
912 void
913 domark(signo)
914 	int signo;
915 {
916 	MarkSet = 1;
917 }
918 
919 void
920 doinit(signo)
921 	int signo;
922 {
923 	DoInit = 1;
924 }
925 
926 /*
927  * Print syslogd errors some place.
928  */
929 void
930 logerror(type)
931 	char *type;
932 {
933 	char buf[100];
934 
935 	if (errno)
936 		(void)snprintf(buf, sizeof(buf), "syslogd: %s: %s",
937 		    type, strerror(errno));
938 	else
939 		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
940 	errno = 0;
941 	dprintf("%s\n", buf);
942 	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
943 }
944 
945 void
946 die(signo)
947 	int signo;
948 {
949 	struct filed *f;
950 	int was_initialized = Initialized;
951 	char buf[100];
952 	int i;
953 
954 	Initialized = 0;		/* Don't log SIGCHLDs */
955 	alarm(0);
956 	for (f = Files; f != NULL; f = f->f_next) {
957 		/* flush any pending output */
958 		if (f->f_prevcount)
959 			fprintlog(f, 0, (char *)NULL);
960 	}
961 	Initialized = was_initialized;
962 	if (signo) {
963 		dprintf("syslogd: exiting on signal %d\n", signo);
964 		(void)snprintf(buf, sizeof buf, "exiting on signal %d", signo);
965 		errno = 0;
966 		logerror(buf);
967 	}
968 	for (i = 0; i < nfunix; i++)
969 		if (funixn[i] && funix[i] != -1)
970 			(void)unlink(funixn[i]);
971 	exit(0);
972 }
973 
974 /*
975  *  INIT -- Initialize syslogd from configuration table
976  */
977 void
978 init()
979 {
980 	int i;
981 	FILE *cf;
982 	struct filed *f, *next, **nextp;
983 	char *p;
984 	char cline[LINE_MAX];
985  	char prog[NAME_MAX+1];
986 
987 	dprintf("init\n");
988 
989 	/*
990 	 *  Close all open log files.
991 	 */
992 	Initialized = 0;
993 	for (f = Files; f != NULL; f = next) {
994 		/* flush any pending output */
995 		if (f->f_prevcount)
996 			fprintlog(f, 0, (char *)NULL);
997 
998 		switch (f->f_type) {
999 		case F_FILE:
1000 		case F_TTY:
1001 		case F_CONSOLE:
1002 			(void)close(f->f_file);
1003 			break;
1004 		case F_FORW:
1005 			break;
1006 		}
1007 		next = f->f_next;
1008 		if (f->f_program)
1009 			free(f->f_program);
1010 		free((char *)f);
1011 	}
1012 	Files = NULL;
1013 	nextp = &Files;
1014 
1015 	/* open the configuration file */
1016 	if ((cf = fopen(ConfFile, "r")) == NULL) {
1017 		dprintf("cannot open %s\n", ConfFile);
1018 		*nextp = (struct filed *)calloc(1, sizeof(*f));
1019 		cfline("*.ERR\t/dev/console", *nextp, "*");
1020 		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
1021 		cfline("*.PANIC\t*", (*nextp)->f_next, "*");
1022 		Initialized = 1;
1023 		return;
1024 	}
1025 
1026 	/*
1027 	 *  Foreach line in the conf table, open that file.
1028 	 */
1029 	f = NULL;
1030 	strlcpy(prog, "*", sizeof(prog));
1031 	while (fgets(cline, sizeof(cline), cf) != NULL) {
1032 		/*
1033 		 * check for end-of-section, comments, strip off trailing
1034 		 * spaces and newline character. !prog is treated
1035 		 * specially: the following lines apply only to that program.
1036 		 */
1037 		for (p = cline; isspace(*p); ++p)
1038 			continue;
1039 		if (*p == '\0' || *p == '#')
1040 			continue;
1041 		if (*p == '!') {
1042 			p++;
1043 			while (isspace(*p))
1044 				p++;
1045 			if (!*p) {
1046 				strlcpy(prog, "*", sizeof(prog));
1047 				continue;
1048 			}
1049 			for (i = 0; i < NAME_MAX; i++) {
1050 				if (!isalnum(p[i]))
1051 					break;
1052 				prog[i] = p[i];
1053 			}
1054 			prog[i] = 0;
1055 			continue;
1056 		}
1057 		p = cline + strlen(cline);
1058 		while (p > cline)
1059 			if (!isspace(*--p)) {
1060 				p++;
1061 				break;
1062 			}
1063 		*p = '\0';
1064 		f = (struct filed *)calloc(1, sizeof(*f));
1065 		*nextp = f;
1066 		nextp = &f->f_next;
1067 		cfline(cline, f, prog);
1068 	}
1069 
1070 	/* close the configuration file */
1071 	(void)fclose(cf);
1072 
1073 	Initialized = 1;
1074 
1075 	if (Debug) {
1076 		for (f = Files; f; f = f->f_next) {
1077 			for (i = 0; i <= LOG_NFACILITIES; i++)
1078 				if (f->f_pmask[i] == INTERNAL_NOPRI)
1079 					printf("X ");
1080 				else
1081 					printf("%d ", f->f_pmask[i]);
1082 			printf("%s: ", TypeNames[f->f_type]);
1083 			switch (f->f_type) {
1084 			case F_FILE:
1085 			case F_TTY:
1086 			case F_CONSOLE:
1087 				printf("%s", f->f_un.f_fname);
1088 				break;
1089 
1090 			case F_FORW:
1091 				printf("%s", f->f_un.f_forw.f_hname);
1092 				break;
1093 
1094 			case F_USERS:
1095 				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1096 					printf("%s, ", f->f_un.f_uname[i]);
1097 				break;
1098 			}
1099 			if (f->f_program)
1100 				printf(" (%s)", f->f_program);
1101 			printf("\n");
1102 		}
1103 	}
1104 
1105 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName,
1106 	    ADDDATE);
1107 	dprintf("syslogd: restarted\n");
1108 }
1109 
1110 /*
1111  * Crack a configuration file line
1112  */
1113 void
1114 cfline(line, f, prog)
1115 	char *line;
1116 	struct filed *f;
1117 	char *prog;
1118 {
1119 	struct hostent *hp;
1120 	int i, pri;
1121 	char *bp, *p, *q;
1122 	char buf[MAXLINE], ebuf[100];
1123 
1124 	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1125 
1126 	errno = 0;	/* keep strerror() stuff out of logerror messages */
1127 
1128 	/* clear out file entry */
1129 	memset(f, 0, sizeof(*f));
1130 	for (i = 0; i <= LOG_NFACILITIES; i++)
1131 		f->f_pmask[i] = INTERNAL_NOPRI;
1132 
1133 	/* save program name if any */
1134 	if (!strcmp(prog, "*"))
1135 		prog = NULL;
1136 	else {
1137 		f->f_program = calloc(1, strlen(prog)+1);
1138 		if (f->f_program)
1139 			strlcpy(f->f_program, prog, strlen(prog)+1);
1140 	}
1141 
1142 	/* scan through the list of selectors */
1143 	for (p = line; *p && *p != '\t';) {
1144 
1145 		/* find the end of this facility name list */
1146 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1147 			continue;
1148 
1149 		/* collect priority name */
1150 		for (bp = buf; *q && !strchr("\t,;", *q); )
1151 			*bp++ = *q++;
1152 		*bp = '\0';
1153 
1154 		/* skip cruft */
1155 		while (strchr(", ;", *q))
1156 			q++;
1157 
1158 		/* decode priority name */
1159 		if (*buf == '*')
1160 			pri = LOG_PRIMASK + 1;
1161 		else {
1162 			/* ignore trailing spaces */
1163 			int i;
1164 			for (i=strlen(buf)-1; i >= 0 && buf[i] == ' '; i--) {
1165 				buf[i]='\0';
1166 			}
1167 
1168 			pri = decode(buf, prioritynames);
1169 			if (pri < 0) {
1170 				(void)snprintf(ebuf, sizeof ebuf,
1171 				    "unknown priority name \"%s\"", buf);
1172 				logerror(ebuf);
1173 				return;
1174 			}
1175 		}
1176 
1177 		/* scan facilities */
1178 		while (*p && !strchr("\t.;", *p)) {
1179 			for (bp = buf; *p && !strchr("\t,;.", *p); )
1180 				*bp++ = *p++;
1181 			*bp = '\0';
1182 			if (*buf == '*')
1183 				for (i = 0; i < LOG_NFACILITIES; i++)
1184 					f->f_pmask[i] = pri;
1185 			else {
1186 				i = decode(buf, facilitynames);
1187 				if (i < 0) {
1188 					(void)snprintf(ebuf, sizeof(ebuf),
1189 					    "unknown facility name \"%s\"",
1190 					    buf);
1191 					logerror(ebuf);
1192 					return;
1193 				}
1194 				f->f_pmask[i >> 3] = pri;
1195 			}
1196 			while (*p == ',' || *p == ' ')
1197 				p++;
1198 		}
1199 
1200 		p = q;
1201 	}
1202 
1203 	/* skip to action part */
1204 	while (*p == '\t')
1205 		p++;
1206 
1207 	switch (*p) {
1208 	case '@':
1209 		if (!InetInuse)
1210 			break;
1211 		(void)strlcpy(f->f_un.f_forw.f_hname, ++p,
1212 		    sizeof(f->f_un.f_forw.f_hname));
1213 		hp = gethostbyname(f->f_un.f_forw.f_hname);
1214 		if (hp == NULL) {
1215 			extern int h_errno;
1216 
1217 			logerror((char *)hstrerror(h_errno));
1218 			break;
1219 		}
1220 		memset(&f->f_un.f_forw.f_addr, 0,
1221 		    sizeof(f->f_un.f_forw.f_addr));
1222 		f->f_un.f_forw.f_addr.sin_len = sizeof(f->f_un.f_forw.f_addr);
1223 		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1224 		f->f_un.f_forw.f_addr.sin_port = LogPort;
1225 		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr,
1226 		    hp->h_length);
1227 		f->f_type = F_FORW;
1228 		break;
1229 
1230 	case '/':
1231 		(void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
1232 		f->f_file = open(p, O_WRONLY|O_APPEND|O_NONBLOCK, 0);
1233 		if (f->f_file < 0) {
1234 			f->f_type = F_UNUSED;
1235 			logerror(p);
1236 			break;
1237 		}
1238 		if (isatty(f->f_file)) {
1239 			if (strcmp(p, ctty) == 0)
1240 				f->f_type = F_CONSOLE;
1241 			else
1242 				f->f_type = F_TTY;
1243 		} else {
1244 			f->f_type = F_FILE;
1245 			/* Clear O_NONBLOCK flag on f->f_file */
1246 			if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) {
1247 				i &= ~O_NONBLOCK;
1248 				fcntl(f->f_file, F_SETFL, i);
1249 			}
1250 		}
1251 		break;
1252 
1253 	case '*':
1254 		f->f_type = F_WALL;
1255 		break;
1256 
1257 	default:
1258 		for (i = 0; i < MAXUNAMES && *p; i++) {
1259 			for (q = p; *q && *q != ','; )
1260 				q++;
1261 			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1262 			if ((q - p) > UT_NAMESIZE)
1263 				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1264 			else
1265 				f->f_un.f_uname[i][q - p] = '\0';
1266 			while (*q == ',' || *q == ' ')
1267 				q++;
1268 			p = q;
1269 		}
1270 		f->f_type = F_USERS;
1271 		break;
1272 	}
1273 }
1274 
1275 
1276 /*
1277  * Retrieve the size of the kernel message buffer, via sysctl.
1278  */
1279 int
1280 getmsgbufsize()
1281 {
1282 	int msgbufsize, mib[2];
1283 	size_t size;
1284 
1285 	mib[0] = CTL_KERN;
1286 	mib[1] = KERN_MSGBUFSIZE;
1287 	size = sizeof msgbufsize;
1288 	if (sysctl(mib, 2, &msgbufsize, &size, NULL, 0) == -1) {
1289 		dprintf("couldn't get kern.msgbufsize\n");
1290 		return (0);
1291 	}
1292 	return (msgbufsize);
1293 }
1294 
1295 /*
1296  *  Decode a symbolic name to a numeric value
1297  */
1298 int
1299 decode(name, codetab)
1300 	const char *name;
1301 	CODE *codetab;
1302 {
1303 	CODE *c;
1304 	char *p, buf[40];
1305 
1306 	if (isdigit(*name))
1307 		return (atoi(name));
1308 
1309 	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1310 		if (isupper(*name))
1311 			*p = tolower(*name);
1312 		else
1313 			*p = *name;
1314 	}
1315 	*p = '\0';
1316 	for (c = codetab; c->c_name; c++)
1317 		if (!strcmp(buf, c->c_name))
1318 			return (c->c_val);
1319 
1320 	return (-1);
1321 }
1322 
1323 void
1324 markit(void)
1325 {
1326 	struct filed *f;
1327 
1328 	now = time((time_t *)NULL);
1329 	MarkSeq += TIMERINTVL;
1330 	if (MarkSeq >= MarkInterval) {
1331 		logmsg(LOG_INFO, "-- MARK --",
1332 		    LocalHostName, ADDDATE|MARK);
1333 		MarkSeq = 0;
1334 	}
1335 
1336 	for (f = Files; f; f = f->f_next) {
1337 		if (f->f_prevcount && now >= REPEATTIME(f)) {
1338 			dprintf("flush %s: repeated %d times, %d sec.\n",
1339 			    TypeNames[f->f_type], f->f_prevcount,
1340 			    repeatinterval[f->f_repeatcount]);
1341 			fprintlog(f, 0, (char *)NULL);
1342 			BACKOFF(f);
1343 		}
1344 	}
1345 	MarkSet = 0;
1346 	(void)alarm(TIMERINTVL);
1347 }
1348