xref: /openbsd-src/usr.sbin/syslogd/syslogd.c (revision 298116df5b000b61a69743d21c92035418df8900)
1 /*	$OpenBSD: syslogd.c,v 1.92 2005/06/10 01:41:43 millert 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. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #ifndef lint
33 static const char copyright[] =
34 "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
35 	The Regents of the University of California.  All rights reserved.\n";
36 #endif /* not lint */
37 
38 #ifndef lint
39 #if 0
40 static const char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";
41 #else
42 static const char rcsid[] = "$OpenBSD: syslogd.c,v 1.92 2005/06/10 01:41:43 millert Exp $";
43 #endif
44 #endif /* not lint */
45 
46 /*
47  *  syslogd -- log system messages
48  *
49  * This program implements a system log. It takes a series of lines.
50  * Each line may have a priority, signified as "<n>" as
51  * the first characters of the line.  If this is
52  * not present, a default priority is used.
53  *
54  * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
55  * cause it to reread its configuration file.
56  *
57  * Defined Constants:
58  *
59  * MAXLINE -- the maximum line length that can be handled.
60  * DEFUPRI -- the default priority for user messages
61  * DEFSPRI -- the default priority for kernel messages
62  *
63  * Author: Eric Allman
64  * extensive changes by Ralph Campbell
65  * more extensive changes by Eric Allman (again)
66  * memory buffer logging by Damien Miller
67  */
68 
69 #define	MAXLINE		1024		/* maximum line length */
70 #define MIN_MEMBUF	(MAXLINE * 4)	/* Minimum memory buffer size */
71 #define MAX_MEMBUF	(256 * 1024)	/* Maximum memory buffer size */
72 #define MAX_MEMBUF_NAME	64		/* Max length of membuf log name */
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 <poll.h>
101 #include <signal.h>
102 #include <stdio.h>
103 #include <stdlib.h>
104 #include <string.h>
105 #include <unistd.h>
106 #include <utmp.h>
107 #include <vis.h>
108 
109 #define SYSLOG_NAMES
110 #include <sys/syslog.h>
111 
112 #include "syslogd.h"
113 
114 char *ConfFile = _PATH_LOGCONF;
115 const char ctty[] = _PATH_CONSOLE;
116 
117 #define MAXUNAMES	20	/* maximum number of user names */
118 
119 
120 /*
121  * Flags to logmsg().
122  */
123 
124 #define IGN_CONS	0x001	/* don't print on console */
125 #define SYNC_FILE	0x002	/* do fsync on file after printing */
126 #define ADDDATE		0x004	/* add a date to the message */
127 #define MARK		0x008	/* this message is a mark */
128 
129 /*
130  * This structure represents the files that will have log
131  * copies printed.
132  */
133 
134 struct filed {
135 	struct	filed *f_next;		/* next in linked list */
136 	short	f_type;			/* entry type, see below */
137 	short	f_file;			/* file descriptor */
138 	time_t	f_time;			/* time this was last written */
139 	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
140 	char	*f_program;		/* program this applies to */
141 	union {
142 		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
143 		struct {
144 			char	f_hname[MAXHOSTNAMELEN];
145 			struct sockaddr_storage	f_addr;
146 		} f_forw;		/* forwarding address */
147 		char	f_fname[MAXPATHLEN];
148 		struct {
149 			char	f_mname[MAX_MEMBUF_NAME];
150 			struct ringbuf *f_rb;
151 			int	f_overflow;
152 		} f_mb;		/* Memory buffer */
153 	} f_un;
154 	char	f_prevline[MAXSVLINE];		/* last message logged */
155 	char	f_lasttime[16];			/* time of last occurrence */
156 	char	f_prevhost[MAXHOSTNAMELEN];	/* host from which recd. */
157 	int	f_prevpri;			/* pri of f_prevline */
158 	int	f_prevlen;			/* length of f_prevline */
159 	int	f_prevcount;			/* repetition cnt of prevline */
160 	unsigned int f_repeatcount;		/* number of "repeated" msgs */
161 	int	f_quick;			/* abort when matched */
162 };
163 
164 /*
165  * Intervals at which we flush out "message repeated" messages,
166  * in seconds after previous message is logged.  After each flush,
167  * we move to the next interval until we reach the largest.
168  */
169 int	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
170 #define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
171 #define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
172 #define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
173 				(f)->f_repeatcount = MAXREPEAT; \
174 			}
175 
176 /* values for f_type */
177 #define F_UNUSED	0		/* unused entry */
178 #define F_FILE		1		/* regular file */
179 #define F_TTY		2		/* terminal */
180 #define F_CONSOLE	3		/* console terminal */
181 #define F_FORW		4		/* remote machine */
182 #define F_USERS		5		/* list of users */
183 #define F_WALL		6		/* everyone logged on */
184 #define F_MEMBUF	7		/* memory buffer */
185 
186 char	*TypeNames[8] = {
187 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
188 	"FORW",		"USERS",	"WALL",		"MEMBUF"
189 };
190 
191 struct	filed *Files;
192 struct	filed consfile;
193 
194 int	nfunix = 1;		/* Number of Unix domain sockets requested */
195 char	*funixn[MAXFUNIX] = { _PATH_LOG }; /* Paths to Unix domain sockets */
196 int	Debug;			/* debug flag */
197 int	Startup = 1;		/* startup flag */
198 char	LocalHostName[MAXHOSTNAMELEN];	/* our hostname */
199 char	*LocalDomain;		/* our local domain name */
200 int	InetInuse = 0;		/* non-zero if INET sockets are being used */
201 int	LogPort;		/* port number for INET connections */
202 int	Initialized = 0;	/* set when we have initialized ourselves */
203 
204 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
205 int	MarkSeq = 0;		/* mark sequence number */
206 int	SecureMode = 1;		/* when true, speak only unix domain socks */
207 int	NoDNS = 0;		/* when true, will refrain from doing DNS lookups */
208 
209 char	*ctlsock_path = NULL;	/* Path to control socket */
210 
211 #define CTL_READING_CMD		1
212 #define CTL_WRITING_REPLY	2
213 int	ctl_state = 0;		/* What the control socket is up to */
214 
215 /*
216  * Client protocol NB. all numeric fields in network byte order
217  */
218 #define CTL_VERSION		0
219 
220 /* Request */
221 struct	{
222 	u_int32_t	version;
223 #define CMD_READ	1	/* Read out log */
224 #define CMD_READ_CLEAR	2	/* Read and clear log */
225 #define CMD_CLEAR	3	/* Clear log */
226 #define CMD_LIST	4	/* List available logs */
227 #define CMD_FLAGS	5	/* Query flags only */
228 	u_int32_t	cmd;
229 	char		logname[MAX_MEMBUF_NAME];
230 }	ctl_cmd;
231 
232 size_t	ctl_cmd_bytes = 0;	/* number of bytes of ctl_cmd read */
233 
234 /* Reply */
235 struct ctl_reply_hdr {
236 	u_int32_t	version;
237 #define CTL_HDR_FLAG_OVERFLOW	0x01
238 	u_int32_t	flags;
239 	/* Reply text follows, up to MAX_MEMBUF long */
240 };
241 
242 #define CTL_HDR_LEN		(sizeof(struct ctl_reply_hdr))
243 #define CTL_REPLY_MAXSIZE	(CTL_HDR_LEN + MAX_MEMBUF)
244 
245 char	*ctl_reply = NULL;	/* Buffer for control connection reply */
246 size_t	ctl_reply_size = 0;	/* Number of bytes used in reply */
247 size_t	ctl_reply_offset = 0;	/* Number of bytes of reply written so far */
248 
249 struct pollfd pfd[N_PFD];
250 
251 volatile sig_atomic_t MarkSet;
252 volatile sig_atomic_t WantDie;
253 volatile sig_atomic_t DoInit;
254 
255 struct filed *cfline(char *, char *);
256 void    cvthname(struct sockaddr_in *, char *, size_t);
257 int	decode(const char *, const CODE *);
258 void	dodie(int);
259 void	doinit(int);
260 void	die(int);
261 void	domark(int);
262 void	markit(void);
263 void	fprintlog(struct filed *, int, char *);
264 void	init(void);
265 void	logerror(const char *);
266 void	logmsg(int, char *, char *, int);
267 struct filed *find_dup(struct filed *);
268 void	printline(char *, char *);
269 void	printsys(char *);
270 void	reapchild(int);
271 char   *ttymsg(struct iovec *, int, char *, int);
272 void	usage(void);
273 void	wallmsg(struct filed *, struct iovec *);
274 int	getmsgbufsize(void);
275 int	unix_socket(char *, int, mode_t);
276 void	double_rbuf(int);
277 void	ctlsock_accept_handler(void);
278 void	ctlconn_read_handler(void);
279 void	ctlconn_write_handler(void);
280 
281 int
282 main(int argc, char *argv[])
283 {
284 	int ch, i, linesize, fd;
285 	struct sockaddr_un fromunix;
286 	struct sockaddr_in s_in, frominet;
287 	socklen_t len;
288 	char *p, *line;
289 	char resolve[MAXHOSTNAMELEN];
290 	int lockpipe[2], nullfd;
291 	FILE *fp;
292 
293 	while ((ch = getopt(argc, argv, "dnuf:m:p:a:s:")) != -1)
294 		switch (ch) {
295 		case 'd':		/* debug */
296 			Debug++;
297 			break;
298 		case 'f':		/* configuration file */
299 			ConfFile = optarg;
300 			break;
301 		case 'm':		/* mark interval */
302 			MarkInterval = atoi(optarg) * 60;
303 			break;
304 		case 'n':		/* don't do DNS lookups */
305 			NoDNS = 1;
306 			break;
307 		case 'p':		/* path */
308 			funixn[0] = optarg;
309 			break;
310 		case 'u':		/* allow udp input port */
311 			SecureMode = 0;
312 			break;
313 		case 'a':
314 			if (nfunix >= MAXFUNIX)
315 				fprintf(stderr, "syslogd: "
316 				    "out of descriptors, ignoring %s\n",
317 				    optarg);
318 			else
319 				funixn[nfunix++] = optarg;
320 			break;
321 		case 's':
322 			ctlsock_path = optarg;
323 			break;
324 		default:
325 			usage();
326 		}
327 	if ((argc -= optind) != 0)
328 		usage();
329 
330 	if (Debug)
331 		setlinebuf(stdout);
332 
333 	if ((nullfd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
334 		logerror("Couldn't open /dev/null");
335 		die(0);
336 	}
337 	while (nullfd < 2) {
338 		dup2(nullfd, nullfd + 1);
339 		nullfd++;
340 	}
341 
342 	consfile.f_type = F_CONSOLE;
343 	(void)strlcpy(consfile.f_un.f_fname, ctty,
344 	    sizeof(consfile.f_un.f_fname));
345 	(void)gethostname(LocalHostName, sizeof(LocalHostName));
346 	if ((p = strchr(LocalHostName, '.')) != NULL) {
347 		*p++ = '\0';
348 		LocalDomain = p;
349 	} else
350 		LocalDomain = "";
351 
352 	linesize = getmsgbufsize();
353 	if (linesize < MAXLINE)
354 		linesize = MAXLINE;
355 	linesize++;
356 	if ((line = malloc(linesize)) == NULL) {
357 		logerror("Couldn't allocate line buffer");
358 		die(0);
359 	}
360 
361 	/* Clear poll array, set all fds to ignore */
362 	for (i = 0; i < N_PFD; i++) {
363 		pfd[i].fd = -1;
364 		pfd[i].events = 0;
365 	}
366 
367 	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) != -1) {
368 		struct servent *sp;
369 
370 		/* XXX use getaddrinfo */
371 		sp = getservbyname("syslog", "udp");
372 		if (sp == NULL) {
373 			errno = 0;
374 			logerror("syslog/udp: unknown service");
375 			die(0);
376 		}
377 		memset(&s_in, 0, sizeof(s_in));
378 		s_in.sin_len = sizeof(s_in);
379 		s_in.sin_family = AF_INET;
380 		s_in.sin_port = LogPort = sp->s_port;
381 		if (bind(fd, (struct sockaddr *)&s_in, sizeof(s_in)) < 0) {
382 			logerror("bind");
383 			if (!Debug)
384 				die(0);
385 		} else {
386 			InetInuse = 1;
387 			pfd[PFD_INET].fd = fd;
388 			if (SecureMode) {
389 				shutdown(fd, SHUT_RD);
390 			} else {
391 				double_rbuf(fd);
392 				pfd[PFD_INET].events = POLLIN;
393 			}
394 		}
395 	}
396 
397 #ifndef SUN_LEN
398 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
399 #endif
400 	for (i = 0; i < nfunix; i++) {
401 		if ((fd = unix_socket(funixn[i], SOCK_DGRAM, 0666)) == -1) {
402 			if (i == 0 && !Debug)
403 				die(0);
404 			continue;
405 		}
406 		double_rbuf(fd);
407 		pfd[PFD_UNIX_0 + i].fd = fd;
408 		pfd[PFD_UNIX_0 + i].events = POLLIN;
409 	}
410 
411 	if (ctlsock_path != NULL) {
412 		fd = unix_socket(ctlsock_path, SOCK_STREAM, 0600);
413 		if (fd != -1) {
414 			if (listen(fd, 16) == -1) {
415 				logerror("ctlsock listen");
416 				die(0);
417 			}
418 			pfd[PFD_CTLSOCK].fd = fd;
419 			pfd[PFD_CTLSOCK].events = POLLIN;
420 		} else if (!Debug)
421 			die(0);
422 	}
423 
424 	if ((fd = open(_PATH_KLOG, O_RDONLY, 0)) == -1) {
425 		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
426 	} else {
427 		pfd[PFD_KLOG].fd = fd;
428 		pfd[PFD_KLOG].events = POLLIN;
429 	}
430 
431 	dprintf("off & running....\n");
432 
433 	chdir("/");
434 
435 	tzset();
436 
437 	if (!Debug) {
438 		char c;
439 
440 		pipe(lockpipe);
441 
442 		switch(fork()) {
443 		case -1:
444 			exit(1);
445 		case 0:
446 			setsid();
447 			close(lockpipe[0]);
448 			break;
449 		default:
450 			close(lockpipe[1]);
451 			read(lockpipe[0], &c, 1);
452 			_exit(0);
453 		}
454 	}
455 
456 	/* tuck my process id away */
457 	if (!Debug) {
458 		fp = fopen(_PATH_LOGPID, "w");
459 		if (fp != NULL) {
460 			fprintf(fp, "%ld\n", (long)getpid());
461 			(void) fclose(fp);
462 		}
463 	}
464 
465 	/* Privilege separation begins here */
466 	if (priv_init(ConfFile, NoDNS, lockpipe[1], nullfd, argv) < 0)
467 		errx(1, "unable to privsep");
468 
469 	/* Process is now unprivileged and inside a chroot */
470 	init();
471 
472 	Startup = 0;
473 
474 	/* Allocate ctl socket reply buffer if we have a ctl socket */
475 	if (pfd[PFD_CTLSOCK].fd != -1 &&
476 	    (ctl_reply = malloc(CTL_REPLY_MAXSIZE)) == NULL) {
477 		logerror("Couldn't allocate ctlsock reply buffer");
478 		die(0);
479 	}
480 
481 	if (!Debug) {
482 		dup2(nullfd, STDIN_FILENO);
483 		dup2(nullfd, STDOUT_FILENO);
484 		dup2(nullfd, STDERR_FILENO);
485 		if (nullfd > 2)
486 			close(nullfd);
487 		close(lockpipe[1]);
488 	}
489 
490 	/*
491 	 * Signal to the priv process that the initial config parsing is done
492 	 * so that it will reject any future attempts to open more files
493 	 */
494 	priv_config_parse_done();
495 
496 	(void)signal(SIGHUP, doinit);
497 	(void)signal(SIGTERM, dodie);
498 	(void)signal(SIGINT, Debug ? dodie : SIG_IGN);
499 	(void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
500 	(void)signal(SIGCHLD, reapchild);
501 	(void)signal(SIGALRM, domark);
502 	(void)signal(SIGPIPE, SIG_IGN);
503 	(void)alarm(TIMERINTVL);
504 
505 	for (;;) {
506 		if (MarkSet)
507 			markit();
508 		if (WantDie)
509 			die(WantDie);
510 
511 		if (DoInit) {
512 			init();
513 			DoInit = 0;
514 		}
515 
516 		switch (poll(pfd, PFD_UNIX_0 + nfunix, -1)) {
517 		case 0:
518 			continue;
519 		case -1:
520 			if (errno != EINTR)
521 				logerror("poll");
522 			continue;
523 		}
524 
525 		if ((pfd[PFD_KLOG].revents & POLLIN) != 0) {
526 			i = read(pfd[PFD_KLOG].fd, line, linesize - 1);
527 			if (i > 0) {
528 				line[i] = '\0';
529 				printsys(line);
530 			} else if (i < 0 && errno != EINTR) {
531 				logerror("klog");
532 				pfd[PFD_KLOG].fd = -1;
533 				pfd[PFD_KLOG].events = 0;
534 			}
535 		}
536 		if ((pfd[PFD_INET].revents & POLLIN) != 0) {
537 			len = sizeof(frominet);
538 			i = recvfrom(pfd[PFD_INET].fd, line, MAXLINE, 0,
539 			    (struct sockaddr *)&frominet, &len);
540 			if (i > 0) {
541 				line[i] = '\0';
542 				cvthname(&frominet, resolve,
543 				    sizeof resolve);
544 				dprintf("cvthname res: %s\n", resolve);
545 				printline(resolve, line);
546 			} else if (i < 0 && errno != EINTR)
547 				logerror("recvfrom inet");
548 		}
549 		if ((pfd[PFD_CTLSOCK].revents & POLLIN) != 0)
550 			ctlsock_accept_handler();
551 		if ((pfd[PFD_CTLCONN].revents & POLLIN) != 0)
552 			ctlconn_read_handler();
553 		if ((pfd[PFD_CTLCONN].revents & POLLOUT) != 0)
554 			ctlconn_write_handler();
555 
556 		for (i = 0; i < nfunix; i++) {
557 			if ((pfd[PFD_UNIX_0 + i].revents & POLLIN) != 0) {
558 				ssize_t rlen;
559 
560 				len = sizeof(fromunix);
561 				rlen = recvfrom(pfd[PFD_UNIX_0 + i].fd, line,
562 				    MAXLINE, 0, (struct sockaddr *)&fromunix,
563 				    &len);
564 				if (rlen > 0) {
565 					line[rlen] = '\0';
566 					printline(LocalHostName, line);
567 				} else if (rlen == -1 && errno != EINTR)
568 					logerror("recvfrom unix");
569 			}
570 		}
571 	}
572 	/* NOTREACHED */
573 	free(pfd);
574 	return (0);
575 }
576 
577 void
578 usage(void)
579 {
580 
581 	(void)fprintf(stderr,
582 	    "usage: syslogd [-dnu] [-a path] [-f config_file] [-m mark_interval]\n"
583 	    "               [-p log_socket] [-s reporting_socket]\n");
584 	exit(1);
585 }
586 
587 /*
588  * Take a raw input line, decode the message, and print the message
589  * on the appropriate log files.
590  */
591 void
592 printline(char *hname, char *msg)
593 {
594 	int pri;
595 	char *p, *q, line[MAXLINE + 1];
596 
597 	/* test for special codes */
598 	pri = DEFUPRI;
599 	p = msg;
600 	if (*p == '<') {
601 		pri = 0;
602 		while (isdigit(*++p))
603 			pri = 10 * pri + (*p - '0');
604 		if (*p == '>')
605 			++p;
606 	}
607 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
608 		pri = DEFUPRI;
609 
610 	/*
611 	 * Don't allow users to log kernel messages.
612 	 * NOTE: since LOG_KERN == 0 this will also match
613 	 * messages with no facility specified.
614 	 */
615 	if (LOG_FAC(pri) == LOG_KERN)
616 		pri = LOG_USER | LOG_PRI(pri);
617 
618 	for (q = line; *p && q < &line[sizeof(line) - 4]; p++) {
619 		if (*p == '\n')
620 			*q++ = ' ';
621 		else
622 			q = vis(q, *p, 0, 0);
623 	}
624 	*q = '\0';
625 
626 	logmsg(pri, line, hname, 0);
627 }
628 
629 /*
630  * Take a raw input line from /dev/klog, split and format similar to syslog().
631  */
632 void
633 printsys(char *msg)
634 {
635 	int c, pri, flags;
636 	char *lp, *p, *q, line[MAXLINE + 1];
637 
638 	(void)snprintf(line, sizeof line, "%s: ", _PATH_UNIX);
639 	lp = line + strlen(line);
640 	for (p = msg; *p != '\0'; ) {
641 		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
642 		pri = DEFSPRI;
643 		if (*p == '<') {
644 			pri = 0;
645 			while (isdigit(*++p))
646 				pri = 10 * pri + (*p - '0');
647 			if (*p == '>')
648 				++p;
649 		} else {
650 			/* kernel printf's come out on console */
651 			flags |= IGN_CONS;
652 		}
653 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
654 			pri = DEFSPRI;
655 
656 		q = lp;
657 		while (*p && (c = *p++) != '\n' && q < &line[sizeof(line) - 4])
658 			q = vis(q, c, 0, 0);
659 
660 		logmsg(pri, line, LocalHostName, flags);
661 	}
662 }
663 
664 time_t	now;
665 
666 /*
667  * Log a message to the appropriate log files, users, etc. based on
668  * the priority.
669  */
670 void
671 logmsg(int pri, char *msg, char *from, int flags)
672 {
673 	struct filed *f;
674 	int fac, msglen, prilev, i;
675 	sigset_t mask, omask;
676 	char *timestamp;
677 	char prog[NAME_MAX+1];
678 
679 	dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
680 	    pri, flags, from, msg);
681 
682 	sigemptyset(&mask);
683 	sigaddset(&mask, SIGALRM);
684 	sigaddset(&mask, SIGHUP);
685 	sigprocmask(SIG_BLOCK, &mask, &omask);
686 
687 	/*
688 	 * Check to see if msg looks non-standard.
689 	 */
690 	msglen = strlen(msg);
691 	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
692 	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
693 		flags |= ADDDATE;
694 
695 	(void)time(&now);
696 	if (flags & ADDDATE)
697 		timestamp = ctime(&now) + 4;
698 	else {
699 		timestamp = msg;
700 		msg += 16;
701 		msglen -= 16;
702 	}
703 
704 	/* extract facility and priority level */
705 	if (flags & MARK)
706 		fac = LOG_NFACILITIES;
707 	else
708 		fac = LOG_FAC(pri);
709 	prilev = LOG_PRI(pri);
710 
711 	/* extract program name */
712 	for(i = 0; i < NAME_MAX; i++) {
713 		if (!isalnum(msg[i]) && msg[i] != '-')
714 			break;
715 		prog[i] = msg[i];
716 	}
717 	prog[i] = 0;
718 
719 	/* log the message to the particular outputs */
720 	if (!Initialized) {
721 		f = &consfile;
722 		f->f_file = priv_open_tty(ctty);
723 
724 		if (f->f_file >= 0) {
725 			fprintlog(f, flags, msg);
726 			(void)close(f->f_file);
727 			f->f_file = -1;
728 		}
729 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
730 		return;
731 	}
732 	for (f = Files; f; f = f->f_next) {
733 		/* skip messages that are incorrect priority */
734 		if (f->f_pmask[fac] < prilev ||
735 		    f->f_pmask[fac] == INTERNAL_NOPRI)
736 			continue;
737 
738 		/* skip messages with the incorrect program name */
739 		if (f->f_program)
740 			if (strcmp(prog, f->f_program) != 0)
741 				continue;
742 
743 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
744 			continue;
745 
746 		/* don't output marks to recently written files */
747 		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
748 			continue;
749 
750 		/*
751 		 * suppress duplicate lines to this file
752 		 */
753 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
754 		    !strcmp(msg, f->f_prevline) &&
755 		    !strcmp(from, f->f_prevhost)) {
756 			strlcpy(f->f_lasttime, timestamp, 16);
757 			f->f_prevcount++;
758 			dprintf("msg repeated %d times, %ld sec of %d\n",
759 			    f->f_prevcount, (long)(now - f->f_time),
760 			    repeatinterval[f->f_repeatcount]);
761 			/*
762 			 * If domark would have logged this by now,
763 			 * flush it now (so we don't hold isolated messages),
764 			 * but back off so we'll flush less often
765 			 * in the future.
766 			 */
767 			if (now > REPEATTIME(f)) {
768 				fprintlog(f, flags, (char *)NULL);
769 				BACKOFF(f);
770 			}
771 		} else {
772 			/* new line, save it */
773 			if (f->f_prevcount)
774 				fprintlog(f, 0, (char *)NULL);
775 			f->f_repeatcount = 0;
776 			f->f_prevpri = pri;
777 			strlcpy(f->f_lasttime, timestamp, 16);
778 			strlcpy(f->f_prevhost, from,
779 			    sizeof(f->f_prevhost));
780 			if (msglen < MAXSVLINE) {
781 				f->f_prevlen = msglen;
782 				strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
783 				fprintlog(f, flags, (char *)NULL);
784 			} else {
785 				f->f_prevline[0] = 0;
786 				f->f_prevlen = 0;
787 				fprintlog(f, flags, msg);
788 			}
789 		}
790 
791 		if (f->f_quick)
792 			break;
793 	}
794 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
795 }
796 
797 void
798 fprintlog(struct filed *f, int flags, char *msg)
799 {
800 	struct iovec iov[6];
801 	struct iovec *v;
802 	int l, retryonce;
803 	char line[MAXLINE + 1], repbuf[80], greetings[500];
804 
805 	v = iov;
806 	if (f->f_type == F_WALL) {
807 		if ((l = snprintf(greetings, sizeof(greetings),
808 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
809 		    f->f_prevhost, ctime(&now))) >= sizeof(greetings) ||
810 		    l == -1)
811 			l = strlen(greetings);
812 		v->iov_base = greetings;
813 		v->iov_len = l;
814 		v++;
815 		v->iov_base = "";
816 		v->iov_len = 0;
817 		v++;
818 	} else {
819 		v->iov_base = f->f_lasttime;
820 		v->iov_len = 15;
821 		v++;
822 		v->iov_base = " ";
823 		v->iov_len = 1;
824 		v++;
825 	}
826 	v->iov_base = f->f_prevhost;
827 	v->iov_len = strlen(v->iov_base);
828 	v++;
829 	v->iov_base = " ";
830 	v->iov_len = 1;
831 	v++;
832 
833 	if (msg) {
834 		v->iov_base = msg;
835 		v->iov_len = strlen(msg);
836 	} else if (f->f_prevcount > 1) {
837 		if ((l = snprintf(repbuf, sizeof(repbuf),
838 		    "last message repeated %d times", f->f_prevcount)) >=
839 		    sizeof(repbuf) || l == -1)
840 			l = strlen(repbuf);
841 		v->iov_base = repbuf;
842 		v->iov_len = l;
843 	} else {
844 		v->iov_base = f->f_prevline;
845 		v->iov_len = f->f_prevlen;
846 	}
847 	v++;
848 
849 	dprintf("Logging to %s", TypeNames[f->f_type]);
850 	f->f_time = now;
851 
852 	switch (f->f_type) {
853 	case F_UNUSED:
854 		dprintf("\n");
855 		break;
856 
857 	case F_FORW:
858 		dprintf(" %s\n", f->f_un.f_forw.f_hname);
859 		if ((l = snprintf(line, sizeof(line), "<%d>%.15s %s",
860 		    f->f_prevpri, (char *)iov[0].iov_base,
861 		    (char *)iov[4].iov_base)) >= sizeof(line) || l == -1)
862 			l = strlen(line);
863 		if (sendto(pfd[PFD_INET].fd, line, l, 0,
864 		    (struct sockaddr *)&f->f_un.f_forw.f_addr,
865 		    f->f_un.f_forw.f_addr.ss_len) != l) {
866 			switch (errno) {
867 			case EHOSTDOWN:
868 			case EHOSTUNREACH:
869 			case ENETDOWN:
870 			case ENOBUFS:
871 				/* silently dropped */
872 				break;
873 			default:
874 				f->f_type = F_UNUSED;
875 				logerror("sendto");
876 				break;
877 			}
878 		}
879 		break;
880 
881 	case F_CONSOLE:
882 		if (flags & IGN_CONS) {
883 			dprintf(" (ignored)\n");
884 			break;
885 		}
886 		/* FALLTHROUGH */
887 
888 	case F_TTY:
889 	case F_FILE:
890 		dprintf(" %s\n", f->f_un.f_fname);
891 		if (f->f_type != F_FILE) {
892 			v->iov_base = "\r\n";
893 			v->iov_len = 2;
894 		} else {
895 			v->iov_base = "\n";
896 			v->iov_len = 1;
897 		}
898 		retryonce = 0;
899 	again:
900 		if (writev(f->f_file, iov, 6) < 0) {
901 			int e = errno;
902 			(void)close(f->f_file);
903 			/*
904 			 * Check for errors on TTY's due to loss of tty
905 			 */
906 			if (e == EAGAIN) {
907 				/*
908 				 * Silently drop messages on blocked write.
909 				 * This can happen when logging to a locked tty.
910 				 */
911 				break;
912 			} else if ((e == EIO || e == EBADF) &&
913 			    f->f_type != F_FILE && !retryonce) {
914 				f->f_file = priv_open_tty(f->f_un.f_fname);
915 				retryonce = 1;
916 				if (f->f_file < 0) {
917 					f->f_type = F_UNUSED;
918 					logerror(f->f_un.f_fname);
919 				} else
920 					goto again;
921 			} else {
922 				f->f_type = F_UNUSED;
923 				f->f_file = -1;
924 				errno = e;
925 				logerror(f->f_un.f_fname);
926 			}
927 		} else if (flags & SYNC_FILE)
928 			(void)fsync(f->f_file);
929 		break;
930 
931 	case F_USERS:
932 	case F_WALL:
933 		dprintf("\n");
934 		v->iov_base = "\r\n";
935 		v->iov_len = 2;
936 		wallmsg(f, iov);
937 		break;
938 
939 	case F_MEMBUF:
940 		dprintf("\n");
941 		snprintf(line, sizeof(line), "%.15s %s %s",
942 		    (char *)iov[0].iov_base, (char *)iov[2].iov_base,
943 		    (char *)iov[4].iov_base);
944 		if (ringbuf_append_line(f->f_un.f_mb.f_rb, line) == 1)
945 			f->f_un.f_mb.f_overflow = 1;
946 		break;
947 	}
948 	f->f_prevcount = 0;
949 }
950 
951 /*
952  *  WALLMSG -- Write a message to the world at large
953  *
954  *	Write the specified message to either the entire
955  *	world, or a list of approved users.
956  */
957 void
958 wallmsg(struct filed *f, struct iovec *iov)
959 {
960 	struct utmp ut;
961 	char line[sizeof(ut.ut_line) + 1], *p;
962 	static int reenter;			/* avoid calling ourselves */
963 	FILE *uf;
964 	int i;
965 
966 	if (reenter++)
967 		return;
968 	if ((uf = priv_open_utmp()) == NULL) {
969 		logerror(_PATH_UTMP);
970 		reenter = 0;
971 		return;
972 	}
973 	/* NOSTRICT */
974 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
975 		if (ut.ut_name[0] == '\0')
976 			continue;
977 		/* must use strncpy since ut_* may not be NUL terminated */
978 		strncpy(line, ut.ut_line, sizeof(line) - 1);
979 		line[sizeof(line) - 1] = '\0';
980 		if (f->f_type == F_WALL) {
981 			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
982 				errno = 0;	/* already in msg */
983 				logerror(p);
984 			}
985 			continue;
986 		}
987 		/* should we send the message to this user? */
988 		for (i = 0; i < MAXUNAMES; i++) {
989 			if (!f->f_un.f_uname[i][0])
990 				break;
991 			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
992 			    UT_NAMESIZE)) {
993 				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
994 								!= NULL) {
995 					errno = 0;	/* already in msg */
996 					logerror(p);
997 				}
998 				break;
999 			}
1000 		}
1001 	}
1002 	(void)fclose(uf);
1003 	reenter = 0;
1004 }
1005 
1006 /* ARGSUSED */
1007 void
1008 reapchild(int signo)
1009 {
1010 	int save_errno = errno;
1011 	int status;
1012 
1013 	while (waitpid(-1, &status, WNOHANG) > 0)
1014 		;
1015 	errno = save_errno;
1016 }
1017 
1018 /*
1019  * Return a printable representation of a host address.
1020  */
1021 void
1022 cvthname(struct sockaddr_in *f, char *result, size_t res_len)
1023 {
1024 	sigset_t omask, nmask;
1025 	char *p, *ip;
1026 	int ret_len;
1027 
1028 	if (f->sin_family != AF_INET) {
1029 		dprintf("Malformed from address\n");
1030 		strlcpy(result, "???", res_len);
1031 		return;
1032 	}
1033 
1034 	ip = inet_ntoa(f->sin_addr);
1035 	dprintf("cvthname(%s)\n", ip);
1036 	if (NoDNS) {
1037 		strlcpy(result, ip, res_len);
1038 		return;
1039 	}
1040 
1041 	sigemptyset(&nmask);
1042 	sigaddset(&nmask, SIGHUP);
1043 	sigprocmask(SIG_BLOCK, &nmask, &omask);
1044 
1045 	ret_len = priv_gethostbyaddr((char *)&f->sin_addr,
1046 		sizeof(struct in_addr), f->sin_family, result, res_len);
1047 
1048 	sigprocmask(SIG_SETMASK, &omask, NULL);
1049 	if (ret_len == 0) {
1050 		dprintf("Host name for your address (%s) unknown\n", ip);
1051 		strlcpy(result, ip, res_len);
1052 	} else if ((p = strchr(result, '.')) && strcmp(p + 1, LocalDomain) == 0)
1053 		*p = '\0';
1054 }
1055 
1056 void
1057 dodie(int signo)
1058 {
1059 	WantDie = signo;
1060 }
1061 
1062 /* ARGSUSED */
1063 void
1064 domark(int signo)
1065 {
1066 	MarkSet = 1;
1067 }
1068 
1069 /* ARGSUSED */
1070 void
1071 doinit(int signo)
1072 {
1073 	DoInit = 1;
1074 }
1075 
1076 /*
1077  * Print syslogd errors some place.
1078  */
1079 void
1080 logerror(const char *type)
1081 {
1082 	char buf[100];
1083 
1084 	if (errno)
1085 		(void)snprintf(buf, sizeof(buf), "syslogd: %s: %s",
1086 		    type, strerror(errno));
1087 	else
1088 		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
1089 	errno = 0;
1090 	dprintf("%s\n", buf);
1091 	if (Startup)
1092 		fprintf(stderr, "%s\n", buf);
1093 	else
1094 		logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
1095 }
1096 
1097 void
1098 die(int signo)
1099 {
1100 	struct filed *f;
1101 	int was_initialized = Initialized;
1102 	char buf[100];
1103 
1104 	Initialized = 0;		/* Don't log SIGCHLDs */
1105 	alarm(0);
1106 	for (f = Files; f != NULL; f = f->f_next) {
1107 		/* flush any pending output */
1108 		if (f->f_prevcount)
1109 			fprintlog(f, 0, (char *)NULL);
1110 	}
1111 	Initialized = was_initialized;
1112 	if (signo) {
1113 		dprintf("syslogd: exiting on signal %d\n", signo);
1114 		(void)snprintf(buf, sizeof buf, "exiting on signal %d", signo);
1115 		errno = 0;
1116 		logerror(buf);
1117 	}
1118 	dprintf("[unpriv] syslogd child about to exit\n");
1119 	exit(0);
1120 }
1121 
1122 /*
1123  *  INIT -- Initialize syslogd from configuration table
1124  */
1125 void
1126 init(void)
1127 {
1128 	char cline[LINE_MAX], prog[NAME_MAX+1], *p;
1129 	struct filed *f, *next, **nextp;
1130 	FILE *cf;
1131 	int i;
1132 
1133 	dprintf("init\n");
1134 
1135 	/* If config file has been modified, then just die to restart */
1136 	if (priv_config_modified()) {
1137 		dprintf("config file changed: dying\n");
1138 		die(0);
1139 	}
1140 
1141 	/*
1142 	 *  Close all open log files.
1143 	 */
1144 	Initialized = 0;
1145 	for (f = Files; f != NULL; f = next) {
1146 		/* flush any pending output */
1147 		if (f->f_prevcount)
1148 			fprintlog(f, 0, (char *)NULL);
1149 
1150 		switch (f->f_type) {
1151 		case F_FILE:
1152 		case F_TTY:
1153 		case F_CONSOLE:
1154 			(void)close(f->f_file);
1155 			break;
1156 		case F_FORW:
1157 			break;
1158 		case F_MEMBUF:
1159 			ringbuf_free(f->f_un.f_mb.f_rb);
1160 			break;
1161 		}
1162 		next = f->f_next;
1163 		if (f->f_program)
1164 			free(f->f_program);
1165 		free((char *)f);
1166 	}
1167 	Files = NULL;
1168 	nextp = &Files;
1169 
1170 	/* open the configuration file */
1171 	if ((cf = priv_open_config()) == NULL) {
1172 		dprintf("cannot open %s\n", ConfFile);
1173 		*nextp = cfline("*.ERR\t/dev/console", "*");
1174 		(*nextp)->f_next = cfline("*.PANIC\t*", "*");
1175 		Initialized = 1;
1176 		return;
1177 	}
1178 
1179 	/*
1180 	 *  Foreach line in the conf table, open that file.
1181 	 */
1182 	f = NULL;
1183 	strlcpy(prog, "*", sizeof(prog));
1184 	while (fgets(cline, sizeof(cline), cf) != NULL) {
1185 		/*
1186 		 * check for end-of-section, comments, strip off trailing
1187 		 * spaces and newline character. !prog is treated
1188 		 * specially: the following lines apply only to that program.
1189 		 */
1190 		for (p = cline; isspace(*p); ++p)
1191 			continue;
1192 		if (*p == '\0' || *p == '#')
1193 			continue;
1194 		if (*p == '!') {
1195 			p++;
1196 			while (isspace(*p))
1197 				p++;
1198 			if (!*p || (*p == '*' && (!p[1] || isspace(p[1])))) {
1199 				strlcpy(prog, "*", sizeof(prog));
1200 				continue;
1201 			}
1202 			for (i = 0; i < NAME_MAX; i++) {
1203 				if (!isalnum(p[i]) && p[i] != '-' && p[i] != '!')
1204 					break;
1205 				prog[i] = p[i];
1206 			}
1207 			prog[i] = 0;
1208 			continue;
1209 		}
1210 		p = cline + strlen(cline);
1211 		while (p > cline)
1212 			if (!isspace(*--p)) {
1213 				p++;
1214 				break;
1215 			}
1216 		*p = '\0';
1217 		f = cfline(cline, prog);
1218 		if (f != NULL) {
1219 			*nextp = f;
1220 			nextp = &f->f_next;
1221 		}
1222 	}
1223 
1224 	/* close the configuration file */
1225 	(void)fclose(cf);
1226 
1227 	Initialized = 1;
1228 
1229 	if (Debug) {
1230 		for (f = Files; f; f = f->f_next) {
1231 			for (i = 0; i <= LOG_NFACILITIES; i++)
1232 				if (f->f_pmask[i] == INTERNAL_NOPRI)
1233 					printf("X ");
1234 				else
1235 					printf("%d ", f->f_pmask[i]);
1236 			printf("%s: ", TypeNames[f->f_type]);
1237 			switch (f->f_type) {
1238 			case F_FILE:
1239 			case F_TTY:
1240 			case F_CONSOLE:
1241 				printf("%s", f->f_un.f_fname);
1242 				break;
1243 
1244 			case F_FORW:
1245 				printf("%s", f->f_un.f_forw.f_hname);
1246 				break;
1247 
1248 			case F_USERS:
1249 				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1250 					printf("%s, ", f->f_un.f_uname[i]);
1251 				break;
1252 
1253 			case F_MEMBUF:
1254 				printf("%s", f->f_un.f_mb.f_mname);
1255 				break;
1256 
1257 			}
1258 			if (f->f_program)
1259 				printf(" (%s)", f->f_program);
1260 			printf("\n");
1261 		}
1262 	}
1263 
1264 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName,
1265 	    ADDDATE);
1266 	dprintf("syslogd: restarted\n");
1267 }
1268 
1269 #define progmatches(p1, p2) \
1270 	(p1 == p2 || (p1 != NULL && p2 != NULL && strcmp(p1, p2) == 0))
1271 
1272 /*
1273  * Spot a line with a duplicate file, console, tty, or membuf target.
1274  */
1275 struct filed *
1276 find_dup(struct filed *f)
1277 {
1278 	struct filed *list;
1279 
1280 	for (list = Files; list; list = list->f_next) {
1281 		if (list->f_quick || f->f_quick)
1282 			continue;
1283 		switch (list->f_type) {
1284 		case F_FILE:
1285 		case F_TTY:
1286 		case F_CONSOLE:
1287 			if (strcmp(list->f_un.f_fname, f->f_un.f_fname) == 0 &&
1288 			    progmatches(list->f_program, f->f_program))
1289 				return (list);
1290 			break;
1291 		case F_MEMBUF:
1292 			if (strcmp(list->f_un.f_mb.f_mname,
1293 			    f->f_un.f_mb.f_mname) == 0 &&
1294 			    progmatches(list->f_program, f->f_program))
1295 				return (list);
1296 			break;
1297 		}
1298 	}
1299 	return (NULL);
1300 }
1301 
1302 /*
1303  * Crack a configuration file line
1304  */
1305 struct filed *
1306 cfline(char *line, char *prog)
1307 {
1308 	int i, pri, addr_len;
1309 	size_t rb_len;
1310 	char *bp, *p, *q, *cp;
1311 	char buf[MAXLINE], ebuf[100];
1312 	struct filed *xf, *f, *d;
1313 
1314 	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1315 
1316 	errno = 0;	/* keep strerror() stuff out of logerror messages */
1317 
1318 	if ((f = calloc(1, sizeof(*f))) == NULL) {
1319 		logerror("Couldn't allocate struct filed");
1320 		die(0);
1321 	}
1322 	for (i = 0; i <= LOG_NFACILITIES; i++)
1323 		f->f_pmask[i] = INTERNAL_NOPRI;
1324 
1325 	/* save program name if any */
1326 	if (*prog == '!') {
1327 		f->f_quick = 1;
1328 		prog++;
1329 	} else
1330 		f->f_quick = 0;
1331 	if (!strcmp(prog, "*"))
1332 		prog = NULL;
1333 	else {
1334 		f->f_program = calloc(1, strlen(prog)+1);
1335 		if (f->f_program)
1336 			strlcpy(f->f_program, prog, strlen(prog)+1);
1337 	}
1338 
1339 	/* scan through the list of selectors */
1340 	for (p = line; *p && *p != '\t';) {
1341 
1342 		/* find the end of this facility name list */
1343 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1344 			continue;
1345 
1346 		/* collect priority name */
1347 		for (bp = buf; *q && !strchr("\t,;", *q); )
1348 			*bp++ = *q++;
1349 		*bp = '\0';
1350 
1351 		/* skip cruft */
1352 		while (*q && strchr(", ;", *q))
1353 			q++;
1354 
1355 		/* decode priority name */
1356 		if (*buf == '*')
1357 			pri = LOG_PRIMASK + 1;
1358 		else {
1359 			/* ignore trailing spaces */
1360 			for (i=strlen(buf)-1; i >= 0 && buf[i] == ' '; i--) {
1361 				buf[i]='\0';
1362 			}
1363 
1364 			pri = decode(buf, prioritynames);
1365 			if (pri < 0) {
1366 				(void)snprintf(ebuf, sizeof ebuf,
1367 				    "unknown priority name \"%s\"", buf);
1368 				logerror(ebuf);
1369 				free(f);
1370 				return (NULL);
1371 			}
1372 		}
1373 
1374 		/* scan facilities */
1375 		while (*p && !strchr("\t.;", *p)) {
1376 			for (bp = buf; *p && !strchr("\t,;.", *p); )
1377 				*bp++ = *p++;
1378 			*bp = '\0';
1379 			if (*buf == '*')
1380 				for (i = 0; i < LOG_NFACILITIES; i++)
1381 					f->f_pmask[i] = pri;
1382 			else {
1383 				i = decode(buf, facilitynames);
1384 				if (i < 0) {
1385 					(void)snprintf(ebuf, sizeof(ebuf),
1386 					    "unknown facility name \"%s\"",
1387 					    buf);
1388 					logerror(ebuf);
1389 					free(f);
1390 					return (NULL);
1391 				}
1392 				f->f_pmask[i >> 3] = pri;
1393 			}
1394 			while (*p == ',' || *p == ' ')
1395 				p++;
1396 		}
1397 
1398 		p = q;
1399 	}
1400 
1401 	/* skip to action part */
1402 	while (*p == '\t')
1403 		p++;
1404 
1405 	switch (*p) {
1406 	case '@':
1407 		if (!InetInuse)
1408 			break;
1409 		if ((cp = strrchr(++p, ':')) != NULL)
1410 			*cp++ = '\0';
1411 		if ((strlcpy(f->f_un.f_forw.f_hname, p,
1412 		    sizeof(f->f_un.f_forw.f_hname)) >=
1413 		    sizeof(f->f_un.f_forw.f_hname))) {
1414 			snprintf(ebuf, sizeof(ebuf), "hostname too long \"%s\"",
1415 			    p);
1416 			logerror(ebuf);
1417 			break;
1418 		}
1419 		addr_len = priv_gethostserv(f->f_un.f_forw.f_hname,
1420 		    cp == NULL ? "syslog" : cp,
1421 		    (struct sockaddr*)&f->f_un.f_forw.f_addr,
1422 		    sizeof(f->f_un.f_forw.f_addr));
1423 		if (addr_len < 1) {
1424 			snprintf(ebuf, sizeof(ebuf), "bad hostname \"%s\"", p);
1425 			logerror(ebuf);
1426 			break;
1427 		}
1428 		f->f_type = F_FORW;
1429 		break;
1430 
1431 	case '/':
1432 		(void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
1433 		d = find_dup(f);
1434 		if (d != NULL) {
1435 			for (i = 0; i <= LOG_NFACILITIES; i++)
1436 				if (f->f_pmask[i] != INTERNAL_NOPRI)
1437 					d->f_pmask[i] = f->f_pmask[i];
1438 			free(f);
1439 			return (NULL);
1440 		}
1441 		if (strcmp(p, ctty) == 0)
1442 			f->f_file = priv_open_tty(p);
1443 		else
1444 			f->f_file = priv_open_log(p);
1445 		if (f->f_file < 0) {
1446 			f->f_type = F_UNUSED;
1447 			logerror(p);
1448 			break;
1449 		}
1450 		if (isatty(f->f_file)) {
1451 			if (strcmp(p, ctty) == 0)
1452 				f->f_type = F_CONSOLE;
1453 			else
1454 				f->f_type = F_TTY;
1455 		} else {
1456 			f->f_type = F_FILE;
1457 			/* Clear O_NONBLOCK flag on f->f_file */
1458 			if ((i = fcntl(f->f_file, F_GETFL, 0)) != -1) {
1459 				i &= ~O_NONBLOCK;
1460 				fcntl(f->f_file, F_SETFL, i);
1461 			}
1462 		}
1463 		break;
1464 
1465 	case '*':
1466 		f->f_type = F_WALL;
1467 		break;
1468 
1469 	case ':':
1470 		f->f_type = F_MEMBUF;
1471 
1472 		/* Parse buffer size (in kb) */
1473 		errno = 0;
1474 		rb_len = strtoul(++p, &q, 0);
1475 		if (*p == '\0' || (errno == ERANGE && rb_len == ULONG_MAX) ||
1476 		    *q != ':' || rb_len == 0) {
1477 			f->f_type = F_UNUSED;
1478 			logerror(p);
1479 			break;
1480 		}
1481 		q++;
1482 		rb_len *= 1024;
1483 
1484 		/* Copy buffer name */
1485 		for(i = 0; i < sizeof(f->f_un.f_mb.f_mname) - 1; i++) {
1486 			if (!isalnum(q[i]))
1487 				break;
1488 			f->f_un.f_mb.f_mname[i] = q[i];
1489 		}
1490 
1491 		/* Make sure buffer name is unique */
1492 		xf = find_dup(f);
1493 
1494 		/* Error on missing or non-unique name, or bad buffer length */
1495 		if (i == 0 || rb_len > MAX_MEMBUF || xf != NULL) {
1496 			f->f_type = F_UNUSED;
1497 			logerror(p);
1498 			break;
1499 		}
1500 
1501 		/* Allocate buffer */
1502 		rb_len = MAX(rb_len, MIN_MEMBUF);
1503 		if ((f->f_un.f_mb.f_rb = ringbuf_init(rb_len)) == NULL) {
1504 			f->f_type = F_UNUSED;
1505 			logerror(p);
1506 			break;
1507 		}
1508 		f->f_un.f_mb.f_overflow = 0;
1509 		break;
1510 
1511 	default:
1512 		for (i = 0; i < MAXUNAMES && *p; i++) {
1513 			for (q = p; *q && *q != ','; )
1514 				q++;
1515 			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1516 			if ((q - p) > UT_NAMESIZE)
1517 				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1518 			else
1519 				f->f_un.f_uname[i][q - p] = '\0';
1520 			while (*q == ',' || *q == ' ')
1521 				q++;
1522 			p = q;
1523 		}
1524 		f->f_type = F_USERS;
1525 		break;
1526 	}
1527 	return (f);
1528 }
1529 
1530 
1531 /*
1532  * Retrieve the size of the kernel message buffer, via sysctl.
1533  */
1534 int
1535 getmsgbufsize(void)
1536 {
1537 	int msgbufsize, mib[2];
1538 	size_t size;
1539 
1540 	mib[0] = CTL_KERN;
1541 	mib[1] = KERN_MSGBUFSIZE;
1542 	size = sizeof msgbufsize;
1543 	if (sysctl(mib, 2, &msgbufsize, &size, NULL, 0) == -1) {
1544 		dprintf("couldn't get kern.msgbufsize\n");
1545 		return (0);
1546 	}
1547 	return (msgbufsize);
1548 }
1549 
1550 /*
1551  *  Decode a symbolic name to a numeric value
1552  */
1553 int
1554 decode(const char *name, const CODE *codetab)
1555 {
1556 	const CODE *c;
1557 	char *p, buf[40];
1558 
1559 	if (isdigit(*name))
1560 		return (atoi(name));
1561 
1562 	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1563 		if (isupper(*name))
1564 			*p = tolower(*name);
1565 		else
1566 			*p = *name;
1567 	}
1568 	*p = '\0';
1569 	for (c = codetab; c->c_name; c++)
1570 		if (!strcmp(buf, c->c_name))
1571 			return (c->c_val);
1572 
1573 	return (-1);
1574 }
1575 
1576 void
1577 markit(void)
1578 {
1579 	struct filed *f;
1580 
1581 	now = time((time_t *)NULL);
1582 	MarkSeq += TIMERINTVL;
1583 	if (MarkSeq >= MarkInterval) {
1584 		logmsg(LOG_INFO, "-- MARK --",
1585 		    LocalHostName, ADDDATE|MARK);
1586 		MarkSeq = 0;
1587 	}
1588 
1589 	for (f = Files; f; f = f->f_next) {
1590 		if (f->f_prevcount && now >= REPEATTIME(f)) {
1591 			dprintf("flush %s: repeated %d times, %d sec.\n",
1592 			    TypeNames[f->f_type], f->f_prevcount,
1593 			    repeatinterval[f->f_repeatcount]);
1594 			fprintlog(f, 0, (char *)NULL);
1595 			BACKOFF(f);
1596 		}
1597 	}
1598 	MarkSet = 0;
1599 	(void)alarm(TIMERINTVL);
1600 }
1601 
1602 int
1603 unix_socket(char *path, int type, mode_t mode)
1604 {
1605 	struct sockaddr_un s_un;
1606 	char errbuf[512];
1607 	int fd;
1608 	mode_t old_umask;
1609 
1610 	memset(&s_un, 0, sizeof(s_un));
1611 	s_un.sun_family = AF_UNIX;
1612 	if (strlcpy(s_un.sun_path, path, sizeof(s_un.sun_path)) >
1613 	    sizeof(s_un.sun_path)) {
1614 		snprintf(errbuf, sizeof(errbuf), "socket path too long: %s",
1615 		    path);
1616 		logerror(errbuf);
1617 		die(0);
1618 	}
1619 
1620 	if ((fd = socket(AF_UNIX, type, 0)) == -1) {
1621 		logerror("socket");
1622 		return (-1);
1623 	}
1624 
1625 	if (Debug) {
1626 		if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0 ||
1627 		    errno == EPROTOTYPE) {
1628 			close(fd);
1629 			errno = EISCONN;
1630 			logerror("connect");
1631 			return (-1);
1632 		}
1633 	}
1634 
1635 	old_umask = umask(0177);
1636 
1637 	unlink(path);
1638 	if (bind(fd, (struct sockaddr *)&s_un, SUN_LEN(&s_un)) == -1) {
1639 		snprintf(errbuf, sizeof(errbuf), "cannot bind %s", path);
1640 		logerror(errbuf);
1641 		umask(old_umask);
1642 		close(fd);
1643 		return (-1);
1644 	}
1645 
1646 	umask(old_umask);
1647 
1648 	if (chmod(path, mode) == -1) {
1649 		snprintf(errbuf, sizeof(errbuf), "cannot chmod %s", path);
1650 		logerror(errbuf);
1651 		close(fd);
1652 		unlink(path);
1653 		return (-1);
1654 	}
1655 
1656 	return (fd);
1657 }
1658 
1659 void
1660 double_rbuf(int fd)
1661 {
1662 	socklen_t slen, len;
1663 
1664 	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, &slen) == 0) {
1665 		len *= 2;
1666 		setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, slen);
1667 	}
1668 }
1669 
1670 static void
1671 ctlconn_cleanup(void)
1672 {
1673 	if (pfd[PFD_CTLCONN].fd != -1)
1674 		close(pfd[PFD_CTLCONN].fd);
1675 
1676 	pfd[PFD_CTLCONN].fd = -1;
1677 	pfd[PFD_CTLCONN].events = pfd[PFD_CTLCONN].revents = 0;
1678 
1679 	pfd[PFD_CTLSOCK].events = POLLIN;
1680 
1681 	ctl_state = ctl_cmd_bytes = ctl_reply_offset = ctl_reply_size = 0;
1682 }
1683 
1684 void
1685 ctlsock_accept_handler(void)
1686 {
1687 	int fd, flags;
1688 
1689 	dprintf("Accepting control connection\n");
1690 	fd = accept(pfd[PFD_CTLSOCK].fd, NULL, NULL);
1691 	if (fd == -1) {
1692 		if (errno != EINTR && errno != ECONNABORTED)
1693 			logerror("accept ctlsock");
1694 		return;
1695 	}
1696 
1697 	ctlconn_cleanup();
1698 
1699 	/* Only one connection at a time */
1700 	pfd[PFD_CTLSOCK].events = pfd[PFD_CTLSOCK].revents = 0;
1701 
1702 	if ((flags = fcntl(fd, F_GETFL)) == -1 ||
1703 	    fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
1704 		logerror("fcntl ctlconn");
1705 		close(fd);
1706 		return;
1707 	}
1708 
1709 	pfd[PFD_CTLCONN].fd = fd;
1710 	pfd[PFD_CTLCONN].events = POLLIN;
1711 	ctl_state = CTL_READING_CMD;
1712 	ctl_cmd_bytes = 0;
1713 }
1714 
1715 static struct filed
1716 *find_membuf_log(const char *name)
1717 {
1718 	struct filed *f;
1719 
1720 	for (f = Files; f != NULL; f = f->f_next) {
1721 		if (f->f_type == F_MEMBUF &&
1722 		    strcmp(f->f_un.f_mb.f_mname, name) == 0)
1723 			break;
1724 	}
1725 	return (f);
1726 }
1727 
1728 void
1729 ctlconn_read_handler(void)
1730 {
1731 	ssize_t n;
1732 	struct filed *f;
1733 	u_int32_t flags = 0;
1734 	size_t reply_text_size;
1735 	struct ctl_reply_hdr *reply_hdr = (struct ctl_reply_hdr *)ctl_reply;
1736 	char *reply_text = ctl_reply + CTL_HDR_LEN;
1737 
1738 	if (ctl_state != CTL_READING_CMD) {
1739 		/* Shouldn't be here! */
1740 		logerror("ctlconn_read with bad ctl_state");
1741 		ctlconn_cleanup();
1742 		return;
1743 	}
1744 
1745  retry:
1746 	n = read(pfd[PFD_CTLCONN].fd, (char*)&ctl_cmd + ctl_cmd_bytes,
1747 	    sizeof(ctl_cmd) - ctl_cmd_bytes);
1748 	switch (n) {
1749 	case -1:
1750 		if (errno == EINTR)
1751 			goto retry;
1752 		logerror("ctlconn read");
1753 		/* FALLTHROUGH */
1754 	case 0:
1755 		ctlconn_cleanup();
1756 		return;
1757 	default:
1758 		ctl_cmd_bytes += n;
1759 	}
1760 
1761 	if (ctl_cmd_bytes < sizeof(ctl_cmd))
1762 		return;
1763 
1764 	if (ntohl(ctl_cmd.version) != CTL_VERSION) {
1765 		logerror("Unknown client protocol version");
1766 		ctlconn_cleanup();
1767 		return;
1768 	}
1769 
1770 	/* Ensure that logname is \0 terminated */
1771 	if (memchr(ctl_cmd.logname, '\0', sizeof(ctl_cmd.logname)) == NULL) {
1772 		logerror("Corrupt ctlsock command");
1773 		ctlconn_cleanup();
1774 		return;
1775 	}
1776 
1777 	*reply_text = '\0';
1778 
1779 	ctl_reply_size = reply_text_size = ctl_reply_offset = 0;
1780 	memset(reply_hdr, '\0', sizeof(*reply_hdr));
1781 
1782 	ctl_cmd.cmd = ntohl(ctl_cmd.cmd);
1783 	dprintf("ctlcmd %x logname \"%s\"\n", ctl_cmd.cmd, ctl_cmd.logname);
1784 
1785 	switch (ctl_cmd.cmd) {
1786 	case CMD_READ:
1787 	case CMD_READ_CLEAR:
1788 	case CMD_FLAGS:
1789 		f = find_membuf_log(ctl_cmd.logname);
1790 		if (f == NULL) {
1791 			strlcpy(reply_text, "No such log\n", MAX_MEMBUF);
1792 		} else {
1793 			if (ctl_cmd.cmd != CMD_FLAGS) {
1794 				ringbuf_to_string(reply_text, MAX_MEMBUF,
1795 				    f->f_un.f_mb.f_rb);
1796 			}
1797 			if (f->f_un.f_mb.f_overflow)
1798 				flags |= CTL_HDR_FLAG_OVERFLOW;
1799 			if (ctl_cmd.cmd == CMD_READ_CLEAR) {
1800 				ringbuf_clear(f->f_un.f_mb.f_rb);
1801 				f->f_un.f_mb.f_overflow = 0;
1802 			}
1803 		}
1804 		reply_text_size = strlen(reply_text);
1805 		break;
1806 	case CMD_CLEAR:
1807 		f = find_membuf_log(ctl_cmd.logname);
1808 		if (f == NULL) {
1809 			strlcpy(reply_text, "No such log\n", MAX_MEMBUF);
1810 		} else {
1811 			ringbuf_clear(f->f_un.f_mb.f_rb);
1812 			if (f->f_un.f_mb.f_overflow)
1813 				flags |= CTL_HDR_FLAG_OVERFLOW;
1814 			f->f_un.f_mb.f_overflow = 0;
1815 			strlcpy(reply_text, "Log cleared\n", MAX_MEMBUF);
1816 		}
1817 		reply_text_size = strlen(reply_text);
1818 		break;
1819 	case CMD_LIST:
1820 		for (f = Files; f != NULL; f = f->f_next) {
1821 			if (f->f_type == F_MEMBUF) {
1822 				strlcat(reply_text, f->f_un.f_mb.f_mname,
1823 				    MAX_MEMBUF);
1824 				if (f->f_un.f_mb.f_overflow) {
1825 					strlcat(reply_text, "*", MAX_MEMBUF);
1826 					flags |= CTL_HDR_FLAG_OVERFLOW;
1827 				}
1828 				strlcat(reply_text, " ", MAX_MEMBUF);
1829 			}
1830 		}
1831 		strlcat(reply_text, "\n", MAX_MEMBUF);
1832 		reply_text_size = strlen(reply_text);
1833 		break;
1834 	default:
1835 		logerror("Unsupported ctlsock command");
1836 		ctlconn_cleanup();
1837 		return;
1838 	}
1839 	reply_hdr->version = htonl(CTL_VERSION);
1840 	reply_hdr->flags = htonl(flags);
1841 
1842 	ctl_reply_size = reply_text_size + CTL_HDR_LEN;
1843 	dprintf("ctlcmd reply length %lu\n", (u_long)ctl_reply_size);
1844 
1845 	/* Otherwise, set up to write out reply */
1846 	ctl_state = CTL_WRITING_REPLY;
1847 	pfd[PFD_CTLCONN].events = POLLOUT;
1848 	pfd[PFD_CTLCONN].revents = 0;
1849 }
1850 
1851 void
1852 ctlconn_write_handler(void)
1853 {
1854 	ssize_t n;
1855 
1856 	if (ctl_state != CTL_WRITING_REPLY) {
1857 		/* Shouldn't be here! */
1858 		logerror("ctlconn_write with bad ctl_state");
1859 		ctlconn_cleanup();
1860 		return;
1861 	}
1862  retry:
1863 	n = write(pfd[PFD_CTLCONN].fd, ctl_reply + ctl_reply_offset,
1864 	    ctl_reply_size - ctl_reply_offset);
1865 	switch (n) {
1866 	case -1:
1867 		if (errno == EINTR)
1868 			goto retry;
1869 		if (errno != EPIPE)
1870 			logerror("ctlconn write");
1871 		/* FALLTHROUGH */
1872 	case 0:
1873 		ctlconn_cleanup();
1874 		return;
1875 	default:
1876 		ctl_reply_offset += n;
1877 	}
1878 
1879 	if (ctl_reply_offset >= ctl_reply_size)
1880 		ctlconn_cleanup();
1881 }
1882