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