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