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