xref: /openbsd-src/usr.sbin/syslogd/syslogd.c (revision 47911bd667ac77dc523b8a13ef40b012dbffa741)
1 /*	$OpenBSD: syslogd.c,v 1.58 2002/11/21 07:46:48 cloder 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.58 2002/11/21 07:46:48 cloder 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 maximum 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 int	SecureMode = 1;		/* when true, speak only unix domain socks */
197 int	NoDNS = 0;		/* when true, will refrain from doing DNS lookups */
198 
199 volatile sig_atomic_t MarkSet;
200 volatile sig_atomic_t WantDie;
201 volatile sig_atomic_t DoInit;
202 
203 void	cfline(char *, struct filed *, char *);
204 char   *cvthname(struct sockaddr_in *);
205 int	decode(const char *, CODE *);
206 void	dodie(int);
207 void	doinit(int);
208 void	die(int);
209 void	domark(int);
210 void	markit(void);
211 void	fprintlog(struct filed *, int, char *);
212 void	init(void);
213 void	logerror(char *);
214 void	logmsg(int, char *, char *, int);
215 void	printline(char *, char *);
216 void	printsys(char *);
217 void	reapchild(int);
218 char   *ttymsg(struct iovec *, int, char *, int);
219 void	usage(void);
220 void	wallmsg(struct filed *, struct iovec *);
221 int	getmsgbufsize(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(int argc, char *argv[])
231 {
232 	int ch, i, fklog, linesize, fdsrmax = 0;
233 	struct sockaddr_un sunx, fromunix;
234 	struct sockaddr_in sin, frominet;
235 	socklen_t slen, len;
236 	fd_set *fdsr = NULL;
237 	char *p, *line;
238 	FILE *fp;
239 
240 	while ((ch = getopt(argc, argv, "dnuf:m:p:a:")) != -1)
241 		switch (ch) {
242 		case 'd':		/* debug */
243 			Debug++;
244 			break;
245 		case 'f':		/* configuration file */
246 			ConfFile = optarg;
247 			break;
248 		case 'm':		/* mark interval */
249 			MarkInterval = atoi(optarg) * 60;
250 			break;
251 		case 'n':		/* don't do DNS lookups */
252 			NoDNS = 1;
253 			break;
254 		case 'p':		/* path */
255 			funixn[0] = optarg;
256 			break;
257 		case 'u':		/* allow udp input port */
258 			SecureMode = 0;
259 			break;
260 		case 'a':
261 			if (nfunix >= MAXFUNIX)
262 				fprintf(stderr,
263 				    "syslogd: out of descriptors, ignoring %s\n",
264 				    optarg);
265 			else if (strlen(optarg) >= sizeof(sunx.sun_path))
266 				fprintf(stderr,
267 				    "syslogd: path too long, ignoring %s\n",
268 				    optarg);
269 			else
270 				funixn[nfunix++] = optarg;
271 			break;
272 		case '?':
273 		default:
274 			usage();
275 		}
276 	if ((argc -= optind) != 0)
277 		usage();
278 
279 	if (!Debug)
280 		(void)daemon(0, 0);
281 	else
282 		setlinebuf(stdout);
283 
284 	consfile.f_type = F_CONSOLE;
285 	(void)strlcpy(consfile.f_un.f_fname, ctty,
286 	    sizeof(consfile.f_un.f_fname));
287 	(void)gethostname(LocalHostName, sizeof(LocalHostName));
288 	if ((p = strchr(LocalHostName, '.')) != NULL) {
289 		*p++ = '\0';
290 		LocalDomain = p;
291 	} else
292 		LocalDomain = "";
293 
294 	linesize = getmsgbufsize();
295 	if (linesize < MAXLINE)
296 		linesize = MAXLINE;
297 	linesize++;
298 	line = malloc(linesize);
299 
300 	(void)signal(SIGHUP, doinit);
301 	(void)signal(SIGTERM, dodie);
302 	(void)signal(SIGINT, Debug ? dodie : SIG_IGN);
303 	(void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
304 	(void)signal(SIGCHLD, reapchild);
305 	(void)signal(SIGALRM, domark);
306 	(void)alarm(TIMERINTVL);
307 
308 #ifndef SUN_LEN
309 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
310 #endif
311 	for (i = 0; i < nfunix; i++) {
312 		(void)unlink(funixn[i]);
313 
314 		memset(&sunx, 0, sizeof(sunx));
315 		sunx.sun_family = AF_UNIX;
316 		(void)strlcpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
317 		funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
318 		if (funix[i] < 0 ||
319 		    bind(funix[i], (struct sockaddr *)&sunx,
320 		    SUN_LEN(&sunx)) < 0 ||
321 		    chmod(funixn[i], 0666) < 0) {
322 			(void)snprintf(line, linesize, "cannot create %s",
323 			    funixn[i]);
324 			logerror(line);
325 			dprintf("cannot create %s (%d)\n", funixn[i], errno);
326 			if (i == 0)
327 				die(0);
328 		}
329 		/* double socket receive buffer size */
330 		if (getsockopt(funix[i], SOL_SOCKET, SO_RCVBUF, &len,
331 		    &slen) == 0) {
332 			len *= 2;
333 			(void)setsockopt(funix[i], SOL_SOCKET, SO_RCVBUF, &len,
334 			    slen);
335 		}
336 	}
337 	finet = socket(AF_INET, SOCK_DGRAM, 0);
338 	if (finet >= 0) {
339 		struct servent *sp;
340 
341 		sp = getservbyname("syslog", "udp");
342 		if (sp == NULL) {
343 			errno = 0;
344 			logerror("syslog/udp: unknown service");
345 			die(0);
346 		}
347 		memset(&sin, 0, sizeof(sin));
348 		sin.sin_len = sizeof(sin);
349 		sin.sin_family = AF_INET;
350 		sin.sin_port = LogPort = sp->s_port;
351 		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
352 			logerror("bind");
353 			if (!Debug)
354 				die(0);
355 		} else {
356 			InetInuse = 1;
357 			/* double socket receive buffer size */
358 			if (getsockopt(finet, SOL_SOCKET, SO_RCVBUF, &len,
359 			    &slen) == 0) {
360 				len *= 2;
361 				(void)setsockopt(funix[i], SOL_SOCKET,
362 				    SO_RCVBUF, &len, slen);
363 			}
364 		}
365 	}
366 	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0)
367 		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
368 
369 	/* tuck my process id away */
370 	if (!Debug) {
371 		fp = fopen(PidFile, "w");
372 		if (fp != NULL) {
373 			fprintf(fp, "%ld\n", (long)getpid());
374 			(void) fclose(fp);
375 		}
376 	}
377 
378 	dprintf("off & running....\n");
379 
380 	init();
381 
382 	if (fklog != -1 && fklog > fdsrmax)
383 		fdsrmax = fklog;
384 	if (finet != -1 && finet > fdsrmax)
385 		fdsrmax = finet;
386 	for (i = 0; i < nfunix; i++) {
387 		if (funix[i] != -1 && funix[i] > fdsrmax)
388 			fdsrmax = funix[i];
389 	}
390 
391 	fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS),
392 	    sizeof(fd_mask));
393 	if (fdsr == NULL)
394 		errx(1, "calloc fd_set");
395 
396 	for (;;) {
397 		if (MarkSet)
398 			markit();
399 		if (WantDie)
400 			die(WantDie);
401 
402 		if (DoInit) {
403 			init();
404 			DoInit = 0;
405 		}
406 
407 		bzero(fdsr, howmany(fdsrmax+1, NFDBITS) *
408 		    sizeof(fd_mask));
409 
410 		if (fklog != -1)
411 			FD_SET(fklog, fdsr);
412 		if (finet != -1)
413 			FD_SET(finet, fdsr);
414 		for (i = 0; i < nfunix; i++) {
415 			if (funix[i] != -1)
416 				FD_SET(funix[i], fdsr);
417 		}
418 
419 		switch (select(fdsrmax+1, fdsr, NULL, NULL, NULL)) {
420 		case 0:
421 			continue;
422 		case -1:
423 			if (errno != EINTR)
424 				logerror("select");
425 			continue;
426 		}
427 
428 		if (fklog != -1 && FD_ISSET(fklog, fdsr)) {
429 			i = read(fklog, line, linesize - 1);
430 			if (i > 0) {
431 				line[i] = '\0';
432 				printsys(line);
433 			} else if (i < 0 && errno != EINTR) {
434 				logerror("klog");
435 				fklog = -1;
436 			}
437 		}
438 		if (finet != -1 && FD_ISSET(finet, fdsr)) {
439 			len = sizeof(frominet);
440 			i = recvfrom(finet, line, MAXLINE, 0,
441 			    (struct sockaddr *)&frominet, &len);
442 			if (SecureMode) {
443 				/* silently drop it */
444 			} else {
445 				if (i > 0) {
446 					line[i] = '\0';
447 					printline(cvthname(&frominet), line);
448 				} else if (i < 0 && errno != EINTR)
449 					logerror("recvfrom inet");
450 			}
451 		}
452 
453 		for (i = 0; i < nfunix; i++) {
454 			if (funix[i] != -1 && FD_ISSET(funix[i], fdsr)) {
455 				len = sizeof(fromunix);
456 				len = recvfrom(funix[i], line, MAXLINE, 0,
457 				    (struct sockaddr *)&fromunix, &len);
458 				if (len > 0) {
459 					line[len] = '\0';
460 					printline(LocalHostName, line);
461 				} else if (len < 0 && errno != EINTR)
462 					logerror("recvfrom unix");
463 			}
464 		}
465 	}
466 	if (fdsr)
467 		free(fdsr);
468 }
469 
470 void
471 usage(void)
472 {
473 
474 	(void)fprintf(stderr,
475 	    "usage: syslogd [-dnu] [-f config_file] [-m mark_interval] "
476 	    "[-a path] [-p log_socket]\n");
477 	exit(1);
478 }
479 
480 /*
481  * Take a raw input line, decode the message, and print the message
482  * on the appropriate log files.
483  */
484 void
485 printline(char *hname, char *msg)
486 {
487 	int pri;
488 	char *p, *q, line[MAXLINE + 1];
489 
490 	/* test for special codes */
491 	pri = DEFUPRI;
492 	p = msg;
493 	if (*p == '<') {
494 		pri = 0;
495 		while (isdigit(*++p))
496 			pri = 10 * pri + (*p - '0');
497 		if (*p == '>')
498 			++p;
499 	}
500 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
501 		pri = DEFUPRI;
502 
503 	/* don't allow users to log kernel messages */
504 	if (LOG_FAC(pri) == LOG_KERN)
505 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
506 
507 	for (q = line; *p && q < &line[sizeof(line) - 4]; p++) {
508 		if (*p == '\n')
509 			*q++ = ' ';
510 		else
511 			q = vis(q, *p, 0, 0);
512 	}
513 	*q = '\0';
514 
515 	logmsg(pri, line, hname, 0);
516 }
517 
518 /*
519  * Take a raw input line from /dev/klog, split and format similar to syslog().
520  */
521 void
522 printsys(char *msg)
523 {
524 	int c, pri, flags;
525 	char *lp, *p, *q, line[MAXLINE + 1];
526 
527 	(void)snprintf(line, sizeof line, "%s: ", _PATH_UNIX);
528 	lp = line + strlen(line);
529 	for (p = msg; *p != '\0'; ) {
530 		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
531 		pri = DEFSPRI;
532 		if (*p == '<') {
533 			pri = 0;
534 			while (isdigit(*++p))
535 				pri = 10 * pri + (*p - '0');
536 			if (*p == '>')
537 				++p;
538 		} else {
539 			/* kernel printf's come out on console */
540 			flags |= IGN_CONS;
541 		}
542 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
543 			pri = DEFSPRI;
544 
545 		q = lp;
546 		while (*p && (c = *p++) != '\n' && q < &line[sizeof(line) - 4])
547 			q = vis(q, c, 0, 0);
548 
549 		logmsg(pri, line, LocalHostName, flags);
550 	}
551 }
552 
553 time_t	now;
554 
555 /*
556  * Log a message to the appropriate log files, users, etc. based on
557  * the priority.
558  */
559 void
560 logmsg(int pri, char *msg, char *from, int flags)
561 {
562 	struct filed *f;
563 	int fac, msglen, prilev, i;
564 	sigset_t mask, omask;
565 	char *timestamp;
566 	char prog[NAME_MAX+1];
567 
568 	dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
569 	    pri, flags, from, msg);
570 
571 	sigemptyset(&mask);
572 	sigaddset(&mask, SIGALRM);
573 	sigaddset(&mask, SIGHUP);
574 	sigprocmask(SIG_BLOCK, &mask, &omask);
575 
576 	/*
577 	 * Check to see if msg looks non-standard.
578 	 */
579 	msglen = strlen(msg);
580 	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
581 	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
582 		flags |= ADDDATE;
583 
584 	(void)time(&now);
585 	if (flags & ADDDATE)
586 		timestamp = ctime(&now) + 4;
587 	else {
588 		timestamp = msg;
589 		msg += 16;
590 		msglen -= 16;
591 	}
592 
593 	/* extract facility and priority level */
594 	if (flags & MARK)
595 		fac = LOG_NFACILITIES;
596 	else
597 		fac = LOG_FAC(pri);
598 	prilev = LOG_PRI(pri);
599 
600 	/* extract program name */
601 	for(i = 0; i < NAME_MAX; i++) {
602 		if (!isalnum(msg[i]))
603 			break;
604 		prog[i] = msg[i];
605 	}
606 	prog[i] = 0;
607 
608 	/* log the message to the particular outputs */
609 	if (!Initialized) {
610 		f = &consfile;
611 		f->f_file = open(ctty, O_WRONLY|O_NONBLOCK, 0);
612 
613 		if (f->f_file >= 0) {
614 			fprintlog(f, flags, msg);
615 			(void)close(f->f_file);
616 			f->f_file = -1;
617 		}
618 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
619 		return;
620 	}
621 	for (f = Files; f; f = f->f_next) {
622 		/* skip messages that are incorrect priority */
623 		if (f->f_pmask[fac] < prilev ||
624 		    f->f_pmask[fac] == INTERNAL_NOPRI)
625 			continue;
626 
627 		/* skip messages with the incorrect program name */
628 		if (f->f_program)
629 			if (strcmp(prog, f->f_program) != 0)
630 				continue;
631 
632 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
633 			continue;
634 
635 		/* don't output marks to recently written files */
636 		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
637 			continue;
638 
639 		/*
640 		 * suppress duplicate lines to this file
641 		 */
642 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
643 		    !strcmp(msg, f->f_prevline) &&
644 		    !strcmp(from, f->f_prevhost)) {
645 			strlcpy(f->f_lasttime, timestamp, 16);
646 			f->f_prevcount++;
647 			dprintf("msg repeated %d times, %ld sec of %d\n",
648 			    f->f_prevcount, (long)(now - f->f_time),
649 			    repeatinterval[f->f_repeatcount]);
650 			/*
651 			 * If domark would have logged this by now,
652 			 * flush it now (so we don't hold isolated messages),
653 			 * but back off so we'll flush less often
654 			 * in the future.
655 			 */
656 			if (now > REPEATTIME(f)) {
657 				fprintlog(f, flags, (char *)NULL);
658 				BACKOFF(f);
659 			}
660 		} else {
661 			/* new line, save it */
662 			if (f->f_prevcount)
663 				fprintlog(f, 0, (char *)NULL);
664 			f->f_repeatcount = 0;
665 			f->f_prevpri = pri;
666 			strlcpy(f->f_lasttime, timestamp, 16);
667 			strlcpy(f->f_prevhost, from,
668 			    sizeof(f->f_prevhost));
669 			if (msglen < MAXSVLINE) {
670 				f->f_prevlen = msglen;
671 				strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
672 				fprintlog(f, flags, (char *)NULL);
673 			} else {
674 				f->f_prevline[0] = 0;
675 				f->f_prevlen = 0;
676 				fprintlog(f, flags, msg);
677 			}
678 		}
679 	}
680 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
681 }
682 
683 void
684 fprintlog(struct filed *f, int flags, char *msg)
685 {
686 	struct iovec iov[6];
687 	struct iovec *v;
688 	int l;
689 	char line[MAXLINE + 1], repbuf[80], greetings[500];
690 
691 	v = iov;
692 	if (f->f_type == F_WALL) {
693 		if ((l = snprintf(greetings, sizeof(greetings),
694 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
695 		    f->f_prevhost, ctime(&now))) >= sizeof(greetings) ||
696 		    l == -1)
697 			l = strlen(greetings);
698 		v->iov_base = greetings;
699 		v->iov_len = l;
700 		v++;
701 		v->iov_base = "";
702 		v->iov_len = 0;
703 		v++;
704 	} else {
705 		v->iov_base = f->f_lasttime;
706 		v->iov_len = 15;
707 		v++;
708 		v->iov_base = " ";
709 		v->iov_len = 1;
710 		v++;
711 	}
712 	v->iov_base = f->f_prevhost;
713 	v->iov_len = strlen(v->iov_base);
714 	v++;
715 	v->iov_base = " ";
716 	v->iov_len = 1;
717 	v++;
718 
719 	if (msg) {
720 		v->iov_base = msg;
721 		v->iov_len = strlen(msg);
722 	} else if (f->f_prevcount > 1) {
723 		if ((l = snprintf(repbuf, sizeof(repbuf),
724 		    "last message repeated %d times", f->f_prevcount)) >=
725 		    sizeof(repbuf) || l == -1)
726 			l = strlen(repbuf);
727 		v->iov_base = repbuf;
728 		v->iov_len = l;
729 	} else {
730 		v->iov_base = f->f_prevline;
731 		v->iov_len = f->f_prevlen;
732 	}
733 	v++;
734 
735 	dprintf("Logging to %s", TypeNames[f->f_type]);
736 	f->f_time = now;
737 
738 	switch (f->f_type) {
739 	case F_UNUSED:
740 		dprintf("\n");
741 		break;
742 
743 	case F_FORW:
744 		dprintf(" %s\n", f->f_un.f_forw.f_hname);
745 		if ((l = snprintf(line, sizeof(line), "<%d>%.15s %s",
746 		    f->f_prevpri, (char *)iov[0].iov_base,
747 		    (char *)iov[4].iov_base)) >= sizeof(line) || l == -1)
748 			l = strlen(line);
749 		if (sendto(finet, line, l, 0,
750 		    (struct sockaddr *)&f->f_un.f_forw.f_addr,
751 		    sizeof(f->f_un.f_forw.f_addr)) != l) {
752 			f->f_type = F_UNUSED;
753 			logerror("sendto");
754 		}
755 		break;
756 
757 	case F_CONSOLE:
758 		if (flags & IGN_CONS) {
759 			dprintf(" (ignored)\n");
760 			break;
761 		}
762 		/* FALLTHROUGH */
763 
764 	case F_TTY:
765 	case F_FILE:
766 		dprintf(" %s\n", f->f_un.f_fname);
767 		if (f->f_type != F_FILE) {
768 			v->iov_base = "\r\n";
769 			v->iov_len = 2;
770 		} else {
771 			v->iov_base = "\n";
772 			v->iov_len = 1;
773 		}
774 	again:
775 		if (writev(f->f_file, iov, 6) < 0) {
776 			int e = errno;
777 			(void)close(f->f_file);
778 			/*
779 			 * Check for errors on TTY's due to loss of tty
780 			 */
781 			if (e == EAGAIN) {
782 				/*
783 				 * Silently drop messages on blocked write.
784 				 * This can happen when logging to a locked tty.
785 				 */
786 				break;
787 			} else if ((e == EIO || e == EBADF) &&
788 			    f->f_type != F_FILE) {
789 				f->f_file = open(f->f_un.f_fname,
790 				    O_WRONLY|O_APPEND|O_NONBLOCK, 0);
791 				if (f->f_file < 0) {
792 					f->f_type = F_UNUSED;
793 					logerror(f->f_un.f_fname);
794 				} else
795 					goto again;
796 			} else {
797 				f->f_type = F_UNUSED;
798 				f->f_file = -1;
799 				errno = e;
800 				logerror(f->f_un.f_fname);
801 			}
802 		} else if (flags & SYNC_FILE)
803 			(void)fsync(f->f_file);
804 		break;
805 
806 	case F_USERS:
807 	case F_WALL:
808 		dprintf("\n");
809 		v->iov_base = "\r\n";
810 		v->iov_len = 2;
811 		wallmsg(f, iov);
812 		break;
813 	}
814 	f->f_prevcount = 0;
815 }
816 
817 /*
818  *  WALLMSG -- Write a message to the world at large
819  *
820  *	Write the specified message to either the entire
821  *	world, or a list of approved users.
822  */
823 void
824 wallmsg(struct filed *f, struct iovec *iov)
825 {
826 	struct utmp ut;
827 	char line[sizeof(ut.ut_line) + 1], *p;
828 	static int reenter;			/* avoid calling ourselves */
829 	FILE *uf;
830 	int i;
831 
832 	if (reenter++)
833 		return;
834 	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
835 		logerror(_PATH_UTMP);
836 		reenter = 0;
837 		return;
838 	}
839 	/* NOSTRICT */
840 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
841 		if (ut.ut_name[0] == '\0')
842 			continue;
843 		strlcpy(line, ut.ut_line, sizeof(line));
844 		if (f->f_type == F_WALL) {
845 			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
846 				errno = 0;	/* already in msg */
847 				logerror(p);
848 			}
849 			continue;
850 		}
851 		/* should we send the message to this user? */
852 		for (i = 0; i < MAXUNAMES; i++) {
853 			if (!f->f_un.f_uname[i][0])
854 				break;
855 			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
856 			    UT_NAMESIZE)) {
857 				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
858 								!= NULL) {
859 					errno = 0;	/* already in msg */
860 					logerror(p);
861 				}
862 				break;
863 			}
864 		}
865 	}
866 	(void)fclose(uf);
867 	reenter = 0;
868 }
869 
870 void
871 reapchild(int signo)
872 {
873 	int save_errno = errno;
874 	int status;
875 
876 	while (waitpid(-1, &status, WNOHANG) > 0)
877 		;
878 	errno = save_errno;
879 }
880 
881 /*
882  * Return a printable representation of a host address.
883  */
884 char *
885 cvthname(struct sockaddr_in *f)
886 {
887 	struct hostent *hp;
888 	sigset_t omask, nmask;
889 	char *p;
890 	char *ip;
891 
892 	if (f->sin_family != AF_INET) {
893 		dprintf("Malformed from address\n");
894 		return ("???");
895 	}
896 
897 	ip = inet_ntoa(f->sin_addr);
898 	dprintf("cvthname(%s)\n", ip);
899 	if (NoDNS)
900 		return (ip);
901 
902 	sigemptyset(&nmask);
903 	sigaddset(&nmask, SIGHUP);
904 	sigprocmask(SIG_BLOCK, &nmask, &omask);
905 	hp = gethostbyaddr((char *)&f->sin_addr,
906 	    sizeof(struct in_addr), f->sin_family);
907 	sigprocmask(SIG_SETMASK, &omask, NULL);
908 	if (hp == 0) {
909 		dprintf("Host name for your address (%s) unknown\n", ip);
910 		return (ip);
911 	}
912 	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
913 		*p = '\0';
914 	return (hp->h_name);
915 }
916 
917 void
918 dodie(int signo)
919 {
920 	WantDie = signo;
921 }
922 
923 void
924 domark(int signo)
925 {
926 	MarkSet = 1;
927 }
928 
929 void
930 doinit(int signo)
931 {
932 	DoInit = 1;
933 }
934 
935 /*
936  * Print syslogd errors some place.
937  */
938 void
939 logerror(char *type)
940 {
941 	char buf[100];
942 
943 	if (errno)
944 		(void)snprintf(buf, sizeof(buf), "syslogd: %s: %s",
945 		    type, strerror(errno));
946 	else
947 		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
948 	errno = 0;
949 	dprintf("%s\n", buf);
950 	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
951 }
952 
953 void
954 die(int signo)
955 {
956 	struct filed *f;
957 	int was_initialized = Initialized;
958 	char buf[100];
959 	int i;
960 
961 	Initialized = 0;		/* Don't log SIGCHLDs */
962 	alarm(0);
963 	for (f = Files; f != NULL; f = f->f_next) {
964 		/* flush any pending output */
965 		if (f->f_prevcount)
966 			fprintlog(f, 0, (char *)NULL);
967 	}
968 	Initialized = was_initialized;
969 	if (signo) {
970 		dprintf("syslogd: exiting on signal %d\n", signo);
971 		(void)snprintf(buf, sizeof buf, "exiting on signal %d", signo);
972 		errno = 0;
973 		logerror(buf);
974 	}
975 	for (i = 0; i < nfunix; i++)
976 		if (funixn[i] && funix[i] != -1)
977 			(void)unlink(funixn[i]);
978 	exit(0);
979 }
980 
981 /*
982  *  INIT -- Initialize syslogd from configuration table
983  */
984 void
985 init(void)
986 {
987 	char cline[LINE_MAX], prog[NAME_MAX+1], *p;
988 	struct filed *f, *next, **nextp;
989 	FILE *cf;
990 	int i;
991 
992 	dprintf("init\n");
993 
994 	/*
995 	 *  Close all open log files.
996 	 */
997 	Initialized = 0;
998 	for (f = Files; f != NULL; f = next) {
999 		/* flush any pending output */
1000 		if (f->f_prevcount)
1001 			fprintlog(f, 0, (char *)NULL);
1002 
1003 		switch (f->f_type) {
1004 		case F_FILE:
1005 		case F_TTY:
1006 		case F_CONSOLE:
1007 			(void)close(f->f_file);
1008 			break;
1009 		case F_FORW:
1010 			break;
1011 		}
1012 		next = f->f_next;
1013 		if (f->f_program)
1014 			free(f->f_program);
1015 		free((char *)f);
1016 	}
1017 	Files = NULL;
1018 	nextp = &Files;
1019 
1020 	/* open the configuration file */
1021 	if ((cf = fopen(ConfFile, "r")) == NULL) {
1022 		dprintf("cannot open %s\n", ConfFile);
1023 		*nextp = (struct filed *)calloc(1, sizeof(*f));
1024 		cfline("*.ERR\t/dev/console", *nextp, "*");
1025 		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
1026 		cfline("*.PANIC\t*", (*nextp)->f_next, "*");
1027 		Initialized = 1;
1028 		return;
1029 	}
1030 
1031 	/*
1032 	 *  Foreach line in the conf table, open that file.
1033 	 */
1034 	f = NULL;
1035 	strlcpy(prog, "*", sizeof(prog));
1036 	while (fgets(cline, sizeof(cline), cf) != NULL) {
1037 		/*
1038 		 * check for end-of-section, comments, strip off trailing
1039 		 * spaces and newline character. !prog is treated
1040 		 * specially: the following lines apply only to that program.
1041 		 */
1042 		for (p = cline; isspace(*p); ++p)
1043 			continue;
1044 		if (*p == '\0' || *p == '#')
1045 			continue;
1046 		if (*p == '!') {
1047 			p++;
1048 			while (isspace(*p))
1049 				p++;
1050 			if (!*p) {
1051 				strlcpy(prog, "*", sizeof(prog));
1052 				continue;
1053 			}
1054 			for (i = 0; i < NAME_MAX; i++) {
1055 				if (!isalnum(p[i]))
1056 					break;
1057 				prog[i] = p[i];
1058 			}
1059 			prog[i] = 0;
1060 			continue;
1061 		}
1062 		p = cline + strlen(cline);
1063 		while (p > cline)
1064 			if (!isspace(*--p)) {
1065 				p++;
1066 				break;
1067 			}
1068 		*p = '\0';
1069 		f = (struct filed *)calloc(1, sizeof(*f));
1070 		*nextp = f;
1071 		nextp = &f->f_next;
1072 		cfline(cline, f, prog);
1073 	}
1074 
1075 	/* close the configuration file */
1076 	(void)fclose(cf);
1077 
1078 	Initialized = 1;
1079 
1080 	if (Debug) {
1081 		for (f = Files; f; f = f->f_next) {
1082 			for (i = 0; i <= LOG_NFACILITIES; i++)
1083 				if (f->f_pmask[i] == INTERNAL_NOPRI)
1084 					printf("X ");
1085 				else
1086 					printf("%d ", f->f_pmask[i]);
1087 			printf("%s: ", TypeNames[f->f_type]);
1088 			switch (f->f_type) {
1089 			case F_FILE:
1090 			case F_TTY:
1091 			case F_CONSOLE:
1092 				printf("%s", f->f_un.f_fname);
1093 				break;
1094 
1095 			case F_FORW:
1096 				printf("%s", f->f_un.f_forw.f_hname);
1097 				break;
1098 
1099 			case F_USERS:
1100 				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1101 					printf("%s, ", f->f_un.f_uname[i]);
1102 				break;
1103 			}
1104 			if (f->f_program)
1105 				printf(" (%s)", f->f_program);
1106 			printf("\n");
1107 		}
1108 	}
1109 
1110 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName,
1111 	    ADDDATE);
1112 	dprintf("syslogd: restarted\n");
1113 }
1114 
1115 /*
1116  * Crack a configuration file line
1117  */
1118 void
1119 cfline(char *line, struct filed *f, char *prog)
1120 {
1121 	struct hostent *hp;
1122 	int i, pri;
1123 	char *bp, *p, *q;
1124 	char buf[MAXLINE], ebuf[100];
1125 
1126 	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1127 
1128 	errno = 0;	/* keep strerror() stuff out of logerror messages */
1129 
1130 	/* clear out file entry */
1131 	memset(f, 0, sizeof(*f));
1132 	for (i = 0; i <= LOG_NFACILITIES; i++)
1133 		f->f_pmask[i] = INTERNAL_NOPRI;
1134 
1135 	/* save program name if any */
1136 	if (!strcmp(prog, "*"))
1137 		prog = NULL;
1138 	else {
1139 		f->f_program = calloc(1, strlen(prog)+1);
1140 		if (f->f_program)
1141 			strlcpy(f->f_program, prog, strlen(prog)+1);
1142 	}
1143 
1144 	/* scan through the list of selectors */
1145 	for (p = line; *p && *p != '\t';) {
1146 
1147 		/* find the end of this facility name list */
1148 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1149 			continue;
1150 
1151 		/* collect priority name */
1152 		for (bp = buf; *q && !strchr("\t,;", *q); )
1153 			*bp++ = *q++;
1154 		*bp = '\0';
1155 
1156 		/* skip cruft */
1157 		while (strchr(", ;", *q))
1158 			q++;
1159 
1160 		/* decode priority name */
1161 		if (*buf == '*')
1162 			pri = LOG_PRIMASK + 1;
1163 		else {
1164 			/* ignore trailing spaces */
1165 			int i;
1166 			for (i=strlen(buf)-1; i >= 0 && buf[i] == ' '; i--) {
1167 				buf[i]='\0';
1168 			}
1169 
1170 			pri = decode(buf, prioritynames);
1171 			if (pri < 0) {
1172 				(void)snprintf(ebuf, sizeof ebuf,
1173 				    "unknown priority name \"%s\"", buf);
1174 				logerror(ebuf);
1175 				return;
1176 			}
1177 		}
1178 
1179 		/* scan facilities */
1180 		while (*p && !strchr("\t.;", *p)) {
1181 			for (bp = buf; *p && !strchr("\t,;.", *p); )
1182 				*bp++ = *p++;
1183 			*bp = '\0';
1184 			if (*buf == '*')
1185 				for (i = 0; i < LOG_NFACILITIES; i++)
1186 					f->f_pmask[i] = pri;
1187 			else {
1188 				i = decode(buf, facilitynames);
1189 				if (i < 0) {
1190 					(void)snprintf(ebuf, sizeof(ebuf),
1191 					    "unknown facility name \"%s\"",
1192 					    buf);
1193 					logerror(ebuf);
1194 					return;
1195 				}
1196 				f->f_pmask[i >> 3] = pri;
1197 			}
1198 			while (*p == ',' || *p == ' ')
1199 				p++;
1200 		}
1201 
1202 		p = q;
1203 	}
1204 
1205 	/* skip to action part */
1206 	while (*p == '\t')
1207 		p++;
1208 
1209 	switch (*p) {
1210 	case '@':
1211 		if (!InetInuse)
1212 			break;
1213 		(void)strlcpy(f->f_un.f_forw.f_hname, ++p,
1214 		    sizeof(f->f_un.f_forw.f_hname));
1215 		hp = gethostbyname(f->f_un.f_forw.f_hname);
1216 		if (hp == NULL) {
1217 			extern int h_errno;
1218 
1219 			logerror((char *)hstrerror(h_errno));
1220 			break;
1221 		}
1222 		memset(&f->f_un.f_forw.f_addr, 0,
1223 		    sizeof(f->f_un.f_forw.f_addr));
1224 		f->f_un.f_forw.f_addr.sin_len = sizeof(f->f_un.f_forw.f_addr);
1225 		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1226 		f->f_un.f_forw.f_addr.sin_port = LogPort;
1227 		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr,
1228 		    hp->h_length);
1229 		f->f_type = F_FORW;
1230 		break;
1231 
1232 	case '/':
1233 		(void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
1234 		f->f_file = open(p, O_WRONLY|O_APPEND|O_NONBLOCK, 0);
1235 		if (f->f_file < 0) {
1236 			f->f_type = F_UNUSED;
1237 			logerror(p);
1238 			break;
1239 		}
1240 		if (isatty(f->f_file)) {
1241 			if (strcmp(p, ctty) == 0)
1242 				f->f_type = F_CONSOLE;
1243 			else
1244 				f->f_type = F_TTY;
1245 		} else {
1246 			f->f_type = F_FILE;
1247 			/* Clear O_NONBLOCK flag on f->f_file */
1248 			if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) {
1249 				i &= ~O_NONBLOCK;
1250 				fcntl(f->f_file, F_SETFL, i);
1251 			}
1252 		}
1253 		break;
1254 
1255 	case '*':
1256 		f->f_type = F_WALL;
1257 		break;
1258 
1259 	default:
1260 		for (i = 0; i < MAXUNAMES && *p; i++) {
1261 			for (q = p; *q && *q != ','; )
1262 				q++;
1263 			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1264 			if ((q - p) > UT_NAMESIZE)
1265 				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1266 			else
1267 				f->f_un.f_uname[i][q - p] = '\0';
1268 			while (*q == ',' || *q == ' ')
1269 				q++;
1270 			p = q;
1271 		}
1272 		f->f_type = F_USERS;
1273 		break;
1274 	}
1275 }
1276 
1277 
1278 /*
1279  * Retrieve the size of the kernel message buffer, via sysctl.
1280  */
1281 int
1282 getmsgbufsize(void)
1283 {
1284 	int msgbufsize, mib[2];
1285 	size_t size;
1286 
1287 	mib[0] = CTL_KERN;
1288 	mib[1] = KERN_MSGBUFSIZE;
1289 	size = sizeof msgbufsize;
1290 	if (sysctl(mib, 2, &msgbufsize, &size, NULL, 0) == -1) {
1291 		dprintf("couldn't get kern.msgbufsize\n");
1292 		return (0);
1293 	}
1294 	return (msgbufsize);
1295 }
1296 
1297 /*
1298  *  Decode a symbolic name to a numeric value
1299  */
1300 int
1301 decode(const char *name, 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