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