xref: /openbsd-src/usr.sbin/syslogd/syslogd.c (revision 62a742911104f98b9185b2c6b6007d9b1c36396c)
1 /*
2  * Copyright (c) 1983, 1988, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static char copyright[] =
36 "@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
37 	The Regents of the University of California.  All rights reserved.\n";
38 #endif /* not lint */
39 
40 #ifndef lint
41 /*static char sccsid[] = "@(#)syslogd.c	8.3 (Berkeley) 4/4/94";*/
42 static char rcsid[] = "$NetBSD: syslogd.c,v 1.5 1996/01/02 17:48:41 perry Exp $";
43 #endif /* not lint */
44 
45 /*
46  *  syslogd -- log system messages
47  *
48  * This program implements a system log. It takes a series of lines.
49  * Each line may have a priority, signified as "<n>" as
50  * the first characters of the line.  If this is
51  * not present, a default priority is used.
52  *
53  * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
54  * cause it to reread its configuration file.
55  *
56  * Defined Constants:
57  *
58  * MAXLINE -- the maximimum line length that can be handled.
59  * DEFUPRI -- the default priority for user messages
60  * DEFSPRI -- the default priority for kernel messages
61  *
62  * Author: Eric Allman
63  * extensive changes by Ralph Campbell
64  * more extensive changes by Eric Allman (again)
65  */
66 
67 #define	MAXLINE		1024		/* maximum line length */
68 #define	MAXSVLINE	120		/* maximum saved line length */
69 #define DEFUPRI		(LOG_USER|LOG_NOTICE)
70 #define DEFSPRI		(LOG_KERN|LOG_CRIT)
71 #define TIMERINTVL	30		/* interval for checking flush, mark */
72 #define TTYMSGTIME	1		/* timeout passed to ttymsg */
73 
74 #include <sys/param.h>
75 #include <sys/ioctl.h>
76 #include <sys/stat.h>
77 #include <sys/wait.h>
78 #include <sys/socket.h>
79 #include <sys/msgbuf.h>
80 #include <sys/uio.h>
81 #include <sys/un.h>
82 #include <sys/time.h>
83 #include <sys/resource.h>
84 
85 #include <netinet/in.h>
86 #include <netdb.h>
87 #include <arpa/inet.h>
88 
89 #include <ctype.h>
90 #include <errno.h>
91 #include <fcntl.h>
92 #include <setjmp.h>
93 #include <signal.h>
94 #include <stdio.h>
95 #include <stdlib.h>
96 #include <string.h>
97 #include <unistd.h>
98 #include <utmp.h>
99 #include <paths.h>
100 
101 #define SYSLOG_NAMES
102 #include <sys/syslog.h>
103 
104 char	*ConfFile = _PATH_LOGCONF;
105 char	*PidFile = _PATH_LOGPID;
106 char	ctty[] = _PATH_CONSOLE;
107 
108 #define	dprintf		if (Debug) printf
109 
110 #define MAXUNAMES	20	/* maximum number of user names */
111 
112 /*
113  * Flags to logmsg().
114  */
115 
116 #define IGN_CONS	0x001	/* don't print on console */
117 #define SYNC_FILE	0x002	/* do fsync on file after printing */
118 #define ADDDATE		0x004	/* add a date to the message */
119 #define MARK		0x008	/* this message is a mark */
120 
121 /*
122  * This structure represents the files that will have log
123  * copies printed.
124  */
125 
126 struct filed {
127 	struct	filed *f_next;		/* next in linked list */
128 	short	f_type;			/* entry type, see below */
129 	short	f_file;			/* file descriptor */
130 	time_t	f_time;			/* time this was last written */
131 	u_char	f_pmask[LOG_NFACILITIES+1];	/* priority mask */
132 	char	*f_program;		/* program this applies to */
133 	union {
134 		char	f_uname[MAXUNAMES][UT_NAMESIZE+1];
135 		struct {
136 			char	f_hname[MAXHOSTNAMELEN];
137 			struct sockaddr_in	f_addr;
138 		} f_forw;		/* forwarding address */
139 		char	f_fname[MAXPATHLEN];
140 	} f_un;
141 	char	f_prevline[MAXSVLINE];		/* last message logged */
142 	char	f_lasttime[16];			/* time of last occurrence */
143 	char	f_prevhost[MAXHOSTNAMELEN];	/* host from which recd. */
144 	int	f_prevpri;			/* pri of f_prevline */
145 	int	f_prevlen;			/* length of f_prevline */
146 	int	f_prevcount;			/* repetition cnt of prevline */
147 	int	f_repeatcount;			/* number of "repeated" msgs */
148 };
149 
150 /*
151  * Intervals at which we flush out "message repeated" messages,
152  * in seconds after previous message is logged.  After each flush,
153  * we move to the next interval until we reach the largest.
154  */
155 int	repeatinterval[] = { 30, 120, 600 };	/* # of secs before flush */
156 #define	MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
157 #define	REPEATTIME(f)	((f)->f_time + repeatinterval[(f)->f_repeatcount])
158 #define	BACKOFF(f)	{ if (++(f)->f_repeatcount > MAXREPEAT) \
159 				 (f)->f_repeatcount = MAXREPEAT; \
160 			}
161 
162 /* values for f_type */
163 #define F_UNUSED	0		/* unused entry */
164 #define F_FILE		1		/* regular file */
165 #define F_TTY		2		/* terminal */
166 #define F_CONSOLE	3		/* console terminal */
167 #define F_FORW		4		/* remote machine */
168 #define F_USERS		5		/* list of users */
169 #define F_WALL		6		/* everyone logged on */
170 
171 char	*TypeNames[7] = {
172 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
173 	"FORW",		"USERS",	"WALL"
174 };
175 
176 struct	filed *Files;
177 struct	filed consfile;
178 
179 int	Debug;			/* debug flag */
180 char	LocalHostName[MAXHOSTNAMELEN];	/* our hostname */
181 char	*LocalDomain;		/* our local domain name */
182 int	InetInuse = 0;		/* non-zero if INET sockets are being used */
183 int	finet;			/* Internet datagram socket */
184 int	LogPort;		/* port number for INET connections */
185 int	Initialized = 0;	/* set when we have initialized ourselves */
186 int	MarkInterval = 20 * 60;	/* interval between marks in seconds */
187 int	MarkSeq = 0;		/* mark sequence number */
188 int	SecureMode = 1;		/* when true, speak only unix domain socks */
189 
190 void	cfline __P((char *, struct filed *, char *));
191 char   *cvthname __P((struct sockaddr_in *));
192 int	decode __P((const char *, CODE *));
193 void	die __P((int));
194 void	domark __P((int));
195 void	fprintlog __P((struct filed *, int, char *));
196 void	init __P((int));
197 void	logerror __P((char *));
198 void	logmsg __P((int, char *, char *, int));
199 void	printline __P((char *, char *));
200 void	printsys __P((char *));
201 void	reapchild __P((int));
202 char   *ttymsg __P((struct iovec *, int, char *, int));
203 void	usage __P((void));
204 void	wallmsg __P((struct filed *, struct iovec *));
205 
206 #define MAXFUNIX	21
207 
208 int nfunix = 1;
209 char *funixn[MAXFUNIX] = { _PATH_LOG };
210 int funix[MAXFUNIX];
211 
212 int
213 main(argc, argv)
214 	int argc;
215 	char *argv[];
216 {
217 	int ch, i, fklog, len;
218 	struct sockaddr_un sunx, fromunix;
219 	struct sockaddr_in sin, frominet;
220 	FILE *fp;
221 	char *p, line[MSG_BSIZE + 1];
222 
223 	while ((ch = getopt(argc, argv, "duf:m:p:a:")) != -1)
224 		switch (ch) {
225 		case 'd':		/* debug */
226 			Debug++;
227 			break;
228 		case 'f':		/* configuration file */
229 			ConfFile = optarg;
230 			break;
231 		case 'm':		/* mark interval */
232 			MarkInterval = atoi(optarg) * 60;
233 			break;
234 		case 'p':		/* path */
235 			funixn[0] = optarg;
236 			break;
237 		case 'u':		/* allow udp input port */
238 			SecureMode = 0;
239 			break;
240 		case 'a':
241 			if (nfunix >= MAXFUNIX)
242 				fprintf(stderr,
243 				    "syslogd: out of descriptors, ignoring %s\n",
244 				    optarg);
245 			else if (strlen(optarg) >= sizeof(sunx.sun_path))
246 				fprintf(stderr,
247 				    "syslogd: path to long, ignoring %s\n",
248 				    optarg);
249 			else
250 				funixn[nfunix++] = optarg;
251 			break;
252 		case '?':
253 		default:
254 			usage();
255 		}
256 	if ((argc -= optind) != 0)
257 		usage();
258 
259 	if (!Debug)
260 		(void)daemon(0, 0);
261 	else
262 		setlinebuf(stdout);
263 
264 	consfile.f_type = F_CONSOLE;
265 	(void)strcpy(consfile.f_un.f_fname, ctty);
266 	(void)gethostname(LocalHostName, sizeof(LocalHostName));
267 	if ((p = strchr(LocalHostName, '.')) != NULL) {
268 		*p++ = '\0';
269 		LocalDomain = p;
270 	} else
271 		LocalDomain = "";
272 	(void)signal(SIGTERM, die);
273 	(void)signal(SIGINT, Debug ? die : SIG_IGN);
274 	(void)signal(SIGQUIT, Debug ? die : SIG_IGN);
275 	(void)signal(SIGCHLD, reapchild);
276 	(void)signal(SIGALRM, domark);
277 	(void)alarm(TIMERINTVL);
278 
279 #ifndef SUN_LEN
280 #define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
281 #endif
282 	for (i = 0; i < nfunix; i++) {
283 		(void)unlink(funixn[i]);
284 
285 		memset(&sunx, 0, sizeof(sunx));
286 		sunx.sun_family = AF_UNIX;
287 		(void)strncpy(sunx.sun_path, funixn[i], sizeof(sunx.sun_path));
288 		funix[i] = socket(AF_UNIX, SOCK_DGRAM, 0);
289 		if (funix[i] < 0 ||
290 		    bind(funix[i], (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
291 		    chmod(funixn[i], 0666) < 0) {
292 			(void) snprintf(line, sizeof line, "cannot create %s",
293 			    funixn[i]);
294 			logerror(line);
295 			dprintf("cannot create %s (%d)\n", funixn[i], errno);
296 			if (i == 0)
297 				die(0);
298 		}
299 	}
300 	finet = socket(AF_INET, SOCK_DGRAM, 0);
301 	if (finet >= 0) {
302 		struct servent *sp;
303 
304 		sp = getservbyname("syslog", "udp");
305 		if (sp == NULL) {
306 			errno = 0;
307 			logerror("syslog/udp: unknown service");
308 			die(0);
309 		}
310 		memset(&sin, 0, sizeof(sin));
311 		sin.sin_len = sizeof(sin);
312 		sin.sin_family = AF_INET;
313 		sin.sin_port = LogPort = sp->s_port;
314 		if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
315 			logerror("bind");
316 			if (!Debug)
317 				die(0);
318 		} else {
319 			InetInuse = 1;
320 		}
321 	}
322 	if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 0)
323 		dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
324 
325 	/* tuck my process id away */
326 	if (!Debug) {
327 		fp = fopen(PidFile, "w");
328 		if (fp != NULL) {
329 			fprintf(fp, "%d\n", getpid());
330 			(void) fclose(fp);
331 		}
332 	}
333 
334 	dprintf("off & running....\n");
335 
336 	init(0);
337 	(void)signal(SIGHUP, init);
338 
339 	for (;;) {
340 		fd_set readfds;
341 		int nfds = 0;
342 
343 		FD_ZERO(&readfds);
344 		if (fklog != -1) {
345 			FD_SET(fklog, &readfds);
346 			if (fklog > nfds)
347 				nfds = fklog;
348 		}
349 		if (finet != -1) {
350 			FD_SET(finet, &readfds);
351 			if (finet > nfds)
352 				nfds = finet;
353 		}
354 		for (i = 0; i < nfunix; i++) {
355 			if (funix[i] != -1) {
356 				FD_SET(funix[i], &readfds);
357 				if (funix[i] > nfds)
358 					nfds = funix[i];
359 			}
360 		}
361 
362 		/*dprintf("readfds = %#x\n", readfds);*/
363 		nfds = select(nfds+1, &readfds, (fd_set *)NULL,
364 		    (fd_set *)NULL, (struct timeval *)NULL);
365 		if (nfds == 0)
366 			continue;
367 		if (nfds < 0) {
368 			if (errno != EINTR)
369 				logerror("select");
370 			continue;
371 		}
372 		/*dprintf("got a message (%d, %#x)\n", nfds, readfds);*/
373 		if (fklog != -1 && FD_ISSET(fklog, &readfds)) {
374 			i = read(fklog, line, sizeof(line) - 1);
375 			if (i > 0) {
376 				line[i] = '\0';
377 				printsys(line);
378 			} else if (i < 0 && errno != EINTR) {
379 				logerror("klog");
380 				fklog = -1;
381 			}
382 		}
383 		if (finet != -1 && FD_ISSET(finet, &readfds)) {
384 			len = sizeof(frominet);
385 			i = recvfrom(finet, line, MAXLINE, 0,
386 			    (struct sockaddr *)&frominet, &len);
387 			if (SecureMode) {
388 				/* silently drop it */
389 			} else {
390 				if (i > 0) {
391 					line[i] = '\0';
392 					printline(cvthname(&frominet), line);
393 				} else if (i < 0 && errno != EINTR)
394 					logerror("recvfrom inet");
395 			}
396 		}
397 		for (i = 0; i < nfunix; i++) {
398 			if (funix[i] != -1 && FD_ISSET(funix[i], &readfds)) {
399 				len = sizeof(fromunix);
400 				len = recvfrom(funix[i], line, MAXLINE, 0,
401 				    (struct sockaddr *)&fromunix, &len);
402 				if (len > 0) {
403 					line[len] = '\0';
404 					printline(LocalHostName, line);
405 				} else if (len < 0 && errno != EINTR)
406 					logerror("recvfrom unix");
407 			}
408 		}
409 	}
410 }
411 
412 void
413 usage()
414 {
415 
416 	(void)fprintf(stderr,
417 	    "usage: syslogd [-u] [-f conffile] [-m markinterval] [-p logpath] [-a logpath]\n");
418 	exit(1);
419 }
420 
421 /*
422  * Take a raw input line, decode the message, and print the message
423  * on the appropriate log files.
424  */
425 void
426 printline(hname, msg)
427 	char *hname;
428 	char *msg;
429 {
430 	int c, pri;
431 	char *p, *q, line[MAXLINE + 1];
432 
433 	/* test for special codes */
434 	pri = DEFUPRI;
435 	p = msg;
436 	if (*p == '<') {
437 		pri = 0;
438 		while (isdigit(*++p))
439 			pri = 10 * pri + (*p - '0');
440 		if (*p == '>')
441 			++p;
442 	}
443 	if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
444 		pri = DEFUPRI;
445 
446 	/* don't allow users to log kernel messages */
447 	if (LOG_FAC(pri) == LOG_KERN)
448 		pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
449 
450 	q = line;
451 
452 	while ((c = *p++ & 0177) != '\0' &&
453 	    q < &line[sizeof(line) - 1])
454 		if (iscntrl(c))
455 			if (c == '\n')
456 				*q++ = ' ';
457 			else if (c == '\t')
458 				*q++ = '\t';
459 			else {
460 				*q++ = '^';
461 				*q++ = c ^ 0100;
462 			}
463 		else
464 			*q++ = c;
465 	*q = '\0';
466 
467 	logmsg(pri, line, hname, 0);
468 }
469 
470 /*
471  * Take a raw input line from /dev/klog, split and format similar to syslog().
472  */
473 void
474 printsys(msg)
475 	char *msg;
476 {
477 	int c, pri, flags;
478 	char *lp, *p, *q, line[MAXLINE + 1];
479 
480 	(void)strcpy(line, _PATH_UNIX);
481 	(void)strcat(line, ": ");
482 	lp = line + strlen(line);
483 	for (p = msg; *p != '\0'; ) {
484 		flags = SYNC_FILE | ADDDATE;	/* fsync file after write */
485 		pri = DEFSPRI;
486 		if (*p == '<') {
487 			pri = 0;
488 			while (isdigit(*++p))
489 				pri = 10 * pri + (*p - '0');
490 			if (*p == '>')
491 				++p;
492 		} else {
493 			/* kernel printf's come out on console */
494 			flags |= IGN_CONS;
495 		}
496 		if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
497 			pri = DEFSPRI;
498 		q = lp;
499 		while (*p != '\0' && (c = *p++) != '\n' &&
500 		    q < &line[MAXLINE])
501 			*q++ = c;
502 		*q = '\0';
503 		logmsg(pri, line, LocalHostName, flags);
504 	}
505 }
506 
507 time_t	now;
508 
509 /*
510  * Log a message to the appropriate log files, users, etc. based on
511  * the priority.
512  */
513 void
514 logmsg(pri, msg, from, flags)
515 	int pri;
516 	char *msg, *from;
517 	int flags;
518 {
519 	struct filed *f;
520 	int fac, msglen, prilev, i;
521 	sigset_t mask, omask;
522 	char *timestamp;
523  	char prog[NAME_MAX+1];
524 
525 	dprintf("logmsg: pri 0%o, flags 0x%x, from %s, msg %s\n",
526 	    pri, flags, from, msg);
527 
528 	sigemptyset(&mask);
529 	sigaddset(&mask, SIGALRM);
530 	sigaddset(&mask, SIGHUP);
531 	sigprocmask(SIG_BLOCK, &mask, &omask);
532 
533 	/*
534 	 * Check to see if msg looks non-standard.
535 	 */
536 	msglen = strlen(msg);
537 	if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
538 	    msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
539 		flags |= ADDDATE;
540 
541 	(void)time(&now);
542 	if (flags & ADDDATE)
543 		timestamp = ctime(&now) + 4;
544 	else {
545 		timestamp = msg;
546 		msg += 16;
547 		msglen -= 16;
548 	}
549 
550 	/* extract facility and priority level */
551 	if (flags & MARK)
552 		fac = LOG_NFACILITIES;
553 	else
554 		fac = LOG_FAC(pri);
555 	prilev = LOG_PRI(pri);
556 
557 	/* extract program name */
558 	for(i = 0; i < NAME_MAX; i++) {
559 		if (!isalnum(msg[i]))
560 			break;
561 		prog[i] = msg[i];
562 	}
563 	prog[i] = 0;
564 
565 	/* log the message to the particular outputs */
566 	if (!Initialized) {
567 		f = &consfile;
568 		f->f_file = open(ctty, O_WRONLY, 0);
569 
570 		if (f->f_file >= 0) {
571 			fprintlog(f, flags, msg);
572 			(void)close(f->f_file);
573 			f->f_file = -1;
574 		}
575 		(void)sigprocmask(SIG_SETMASK, &omask, NULL);
576 		return;
577 	}
578 	for (f = Files; f; f = f->f_next) {
579 		/* skip messages that are incorrect priority */
580 		if (f->f_pmask[fac] < prilev ||
581 		    f->f_pmask[fac] == INTERNAL_NOPRI)
582 			continue;
583 
584 		/* skip messages with the incorrect program name */
585 		if (f->f_program)
586 			if (strcmp(prog, f->f_program) != 0)
587 				continue;
588 
589 		if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
590 			continue;
591 
592 		/* don't output marks to recently written files */
593 		if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
594 			continue;
595 
596 		/*
597 		 * suppress duplicate lines to this file
598 		 */
599 		if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
600 		    !strcmp(msg, f->f_prevline) &&
601 		    !strcmp(from, f->f_prevhost)) {
602 			(void)strncpy(f->f_lasttime, timestamp, 15);
603 			f->f_prevcount++;
604 			dprintf("msg repeated %d times, %ld sec of %d\n",
605 			    f->f_prevcount, (long)(now - f->f_time),
606 			    repeatinterval[f->f_repeatcount]);
607 			/*
608 			 * If domark would have logged this by now,
609 			 * flush it now (so we don't hold isolated messages),
610 			 * but back off so we'll flush less often
611 			 * in the future.
612 			 */
613 			if (now > REPEATTIME(f)) {
614 				fprintlog(f, flags, (char *)NULL);
615 				BACKOFF(f);
616 			}
617 		} else {
618 			/* new line, save it */
619 			if (f->f_prevcount)
620 				fprintlog(f, 0, (char *)NULL);
621 			f->f_repeatcount = 0;
622 			f->f_prevpri = pri;
623 			(void)strncpy(f->f_lasttime, timestamp, 15);
624 			(void)strncpy(f->f_prevhost, from,
625 					sizeof(f->f_prevhost)-1);
626 			f->f_prevhost[sizeof(f->f_prevhost)-1] = '\0';
627 			if (msglen < MAXSVLINE) {
628 				f->f_prevlen = msglen;
629 				(void)strcpy(f->f_prevline, msg);
630 				fprintlog(f, flags, (char *)NULL);
631 			} else {
632 				f->f_prevline[0] = 0;
633 				f->f_prevlen = 0;
634 				fprintlog(f, flags, msg);
635 			}
636 		}
637 	}
638 	(void)sigprocmask(SIG_SETMASK, &omask, NULL);
639 }
640 
641 void
642 fprintlog(f, flags, msg)
643 	struct filed *f;
644 	int flags;
645 	char *msg;
646 {
647 	struct iovec iov[6];
648 	struct iovec *v;
649 	int l;
650 	char line[MAXLINE + 1], repbuf[80], greetings[500];
651 
652 	v = iov;
653 	if (f->f_type == F_WALL) {
654 		v->iov_base = greetings;
655 		v->iov_len = snprintf(greetings, sizeof(greetings),
656 		    "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
657 		    f->f_prevhost, ctime(&now));
658 		if (v->iov_len >= sizeof(greetings))
659 			v->iov_len = sizeof(greetings) - 1;
660 		v++;
661 		v->iov_base = "";
662 		v->iov_len = 0;
663 		v++;
664 	} else {
665 		v->iov_base = f->f_lasttime;
666 		v->iov_len = 15;
667 		v++;
668 		v->iov_base = " ";
669 		v->iov_len = 1;
670 		v++;
671 	}
672 	v->iov_base = f->f_prevhost;
673 	v->iov_len = strlen(v->iov_base);
674 	v++;
675 	v->iov_base = " ";
676 	v->iov_len = 1;
677 	v++;
678 
679 	if (msg) {
680 		v->iov_base = msg;
681 		v->iov_len = strlen(msg);
682 	} else if (f->f_prevcount > 1) {
683 		v->iov_base = repbuf;
684 		v->iov_len = sprintf(repbuf, "last message repeated %d times",
685 		    f->f_prevcount);
686 	} else {
687 		v->iov_base = f->f_prevline;
688 		v->iov_len = f->f_prevlen;
689 	}
690 	v++;
691 
692 	dprintf("Logging to %s", TypeNames[f->f_type]);
693 	f->f_time = now;
694 
695 	switch (f->f_type) {
696 	case F_UNUSED:
697 		dprintf("\n");
698 		break;
699 
700 	case F_FORW:
701 		dprintf(" %s\n", f->f_un.f_forw.f_hname);
702 		l = snprintf(line, sizeof(line) - 1, "<%d>%.15s %s", f->f_prevpri,
703 		    iov[0].iov_base, iov[4].iov_base);
704 		if (l > MAXLINE)
705 			l = MAXLINE;
706 		if (sendto(finet, line, l, 0,
707 		    (struct sockaddr *)&f->f_un.f_forw.f_addr,
708 		    sizeof(f->f_un.f_forw.f_addr)) != l) {
709 			f->f_type = F_UNUSED;
710 			logerror("sendto");
711 		}
712 		break;
713 
714 	case F_CONSOLE:
715 		if (flags & IGN_CONS) {
716 			dprintf(" (ignored)\n");
717 			break;
718 		}
719 		/* FALLTHROUGH */
720 
721 	case F_TTY:
722 	case F_FILE:
723 		dprintf(" %s\n", f->f_un.f_fname);
724 		if (f->f_type != F_FILE) {
725 			v->iov_base = "\r\n";
726 			v->iov_len = 2;
727 		} else {
728 			v->iov_base = "\n";
729 			v->iov_len = 1;
730 		}
731 	again:
732 		if (writev(f->f_file, iov, 6) < 0) {
733 			int e = errno;
734 			(void)close(f->f_file);
735 			/*
736 			 * Check for errors on TTY's due to loss of tty
737 			 */
738 			if ((e == EIO || e == EBADF) && f->f_type != F_FILE) {
739 				f->f_file = open(f->f_un.f_fname,
740 				    O_WRONLY|O_APPEND, 0);
741 				if (f->f_file < 0) {
742 					f->f_type = F_UNUSED;
743 					logerror(f->f_un.f_fname);
744 				} else
745 					goto again;
746 			} else {
747 				f->f_type = F_UNUSED;
748 				f->f_file = -1;
749 				errno = e;
750 				logerror(f->f_un.f_fname);
751 			}
752 		} else if (flags & SYNC_FILE)
753 			(void)fsync(f->f_file);
754 		break;
755 
756 	case F_USERS:
757 	case F_WALL:
758 		dprintf("\n");
759 		v->iov_base = "\r\n";
760 		v->iov_len = 2;
761 		wallmsg(f, iov);
762 		break;
763 	}
764 	f->f_prevcount = 0;
765 }
766 
767 /*
768  *  WALLMSG -- Write a message to the world at large
769  *
770  *	Write the specified message to either the entire
771  *	world, or a list of approved users.
772  */
773 void
774 wallmsg(f, iov)
775 	struct filed *f;
776 	struct iovec *iov;
777 {
778 	static int reenter;			/* avoid calling ourselves */
779 	FILE *uf;
780 	struct utmp ut;
781 	int i;
782 	char *p;
783 	char line[sizeof(ut.ut_line) + 1];
784 
785 	if (reenter++)
786 		return;
787 	if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
788 		logerror(_PATH_UTMP);
789 		reenter = 0;
790 		return;
791 	}
792 	/* NOSTRICT */
793 	while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
794 		if (ut.ut_name[0] == '\0')
795 			continue;
796 		strncpy(line, ut.ut_line, sizeof(ut.ut_line));
797 		line[sizeof(ut.ut_line)] = '\0';
798 		if (f->f_type == F_WALL) {
799 			if ((p = ttymsg(iov, 6, line, TTYMSGTIME)) != NULL) {
800 				errno = 0;	/* already in msg */
801 				logerror(p);
802 			}
803 			continue;
804 		}
805 		/* should we send the message to this user? */
806 		for (i = 0; i < MAXUNAMES; i++) {
807 			if (!f->f_un.f_uname[i][0])
808 				break;
809 			if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
810 			    UT_NAMESIZE)) {
811 				if ((p = ttymsg(iov, 6, line, TTYMSGTIME))
812 								!= NULL) {
813 					errno = 0;	/* already in msg */
814 					logerror(p);
815 				}
816 				break;
817 			}
818 		}
819 	}
820 	(void)fclose(uf);
821 	reenter = 0;
822 }
823 
824 void
825 reapchild(signo)
826 	int signo;
827 {
828 	union wait status;
829 	int save_errno = errno;
830 
831 	while (wait3((int *)&status, WNOHANG, (struct rusage *)NULL) > 0)
832 		;
833 	errno = save_errno;
834 }
835 
836 /*
837  * Return a printable representation of a host address.
838  */
839 char *
840 cvthname(f)
841 	struct sockaddr_in *f;
842 {
843 	struct hostent *hp;
844 	sigset_t omask, nmask;
845 	char *p;
846 
847 	dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
848 
849 	if (f->sin_family != AF_INET) {
850 		dprintf("Malformed from address\n");
851 		return ("???");
852 	}
853 	sigemptyset(&nmask);
854 	sigaddset(&nmask, SIGHUP);
855 	sigprocmask(SIG_BLOCK, &nmask, &omask);
856 	hp = gethostbyaddr((char *)&f->sin_addr,
857 	    sizeof(struct in_addr), f->sin_family);
858 	sigprocmask(SIG_SETMASK, &omask, NULL);
859 	if (hp == 0) {
860 		dprintf("Host name for your address (%s) unknown\n",
861 			inet_ntoa(f->sin_addr));
862 		return (inet_ntoa(f->sin_addr));
863 	}
864 	if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
865 		*p = '\0';
866 	return (hp->h_name);
867 }
868 
869 void
870 domark(signo)
871 	int signo;
872 {
873 	struct filed *f;
874 
875 	now = time((time_t *)NULL);
876 	MarkSeq += TIMERINTVL;
877 	if (MarkSeq >= MarkInterval) {
878 		logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
879 		MarkSeq = 0;
880 	}
881 
882 	for (f = Files; f; f = f->f_next) {
883 		if (f->f_prevcount && now >= REPEATTIME(f)) {
884 			dprintf("flush %s: repeated %d times, %d sec.\n",
885 			    TypeNames[f->f_type], f->f_prevcount,
886 			    repeatinterval[f->f_repeatcount]);
887 			fprintlog(f, 0, (char *)NULL);
888 			BACKOFF(f);
889 		}
890 	}
891 	(void)alarm(TIMERINTVL);
892 }
893 
894 /*
895  * Print syslogd errors some place.
896  */
897 void
898 logerror(type)
899 	char *type;
900 {
901 	char buf[100];
902 
903 	if (errno)
904 		(void)snprintf(buf, sizeof(buf), "syslogd: %s: %s",
905 		    type, strerror(errno));
906 	else
907 		(void)snprintf(buf, sizeof(buf), "syslogd: %s", type);
908 	errno = 0;
909 	dprintf("%s\n", buf);
910 	logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
911 }
912 
913 void
914 die(signo)
915 	int signo;
916 {
917 	struct filed *f;
918 	int was_initialized = Initialized;
919 	char buf[100];
920 	int i;
921 
922 	Initialized = 0;		/* Don't log SIGCHLDs */
923 	for (f = Files; f != NULL; f = f->f_next) {
924 		/* flush any pending output */
925 		if (f->f_prevcount)
926 			fprintlog(f, 0, (char *)NULL);
927 	}
928 	Initialized = was_initialized;
929 	if (signo) {
930 		dprintf("syslogd: exiting on signal %d\n", signo);
931 		(void)sprintf(buf, "exiting on signal %d", signo);
932 		errno = 0;
933 		logerror(buf);
934 	}
935 	for (i = 0; i < nfunix; i++)
936 		if (funixn[i] && funix[i] != -1)
937 			(void)unlink(funixn[i]);
938 	exit(0);
939 }
940 
941 /*
942  *  INIT -- Initialize syslogd from configuration table
943  */
944 void
945 init(signo)
946 	int signo;
947 {
948 	int i;
949 	FILE *cf;
950 	struct filed *f, *next, **nextp;
951 	char *p;
952 	char cline[LINE_MAX];
953  	char prog[NAME_MAX+1];
954 
955 	dprintf("init\n");
956 
957 	/*
958 	 *  Close all open log files.
959 	 */
960 	Initialized = 0;
961 	for (f = Files; f != NULL; f = next) {
962 		/* flush any pending output */
963 		if (f->f_prevcount)
964 			fprintlog(f, 0, (char *)NULL);
965 
966 		switch (f->f_type) {
967 		case F_FILE:
968 		case F_TTY:
969 		case F_CONSOLE:
970 			(void)close(f->f_file);
971 			break;
972 		case F_FORW:
973 			break;
974 		}
975 		next = f->f_next;
976 		if (f->f_program)
977 			free(f->f_program);
978 		free((char *)f);
979 	}
980 	Files = NULL;
981 	nextp = &Files;
982 
983 	/* open the configuration file */
984 	if ((cf = fopen(ConfFile, "r")) == NULL) {
985 		dprintf("cannot open %s\n", ConfFile);
986 		*nextp = (struct filed *)calloc(1, sizeof(*f));
987 		cfline("*.ERR\t/dev/console", *nextp, "*");
988 		(*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
989 		cfline("*.PANIC\t*", (*nextp)->f_next, "*");
990 		Initialized = 1;
991 		return;
992 	}
993 
994 	/*
995 	 *  Foreach line in the conf table, open that file.
996 	 */
997 	f = NULL;
998 	strcpy(prog, "*");
999 	while (fgets(cline, sizeof(cline), cf) != NULL) {
1000 		/*
1001 		 * check for end-of-section, comments, strip off trailing
1002 		 * spaces and newline character. #!prog  and !prog are treated
1003 		 * specially: the following lines apply only to that program.
1004 		 */
1005 		for (p = cline; isspace(*p); ++p)
1006 			continue;
1007 		if (*p == '\0')
1008 			continue;
1009 		if (*p == '#') {
1010 			p++;
1011 			if (*p != '!')
1012 				continue;
1013 		}
1014 		if (*p == '!') {
1015 			p++;
1016 			while (isspace(*p))
1017 				p++;
1018 			if (!*p) {
1019 				strcpy(prog, "*");
1020 				continue;
1021 			}
1022 			for (i = 0; i < NAME_MAX; i++) {
1023 				if (!isalnum(p[i]))
1024 					break;
1025 				prog[i] = p[i];
1026 			}
1027 			prog[i] = 0;
1028 			continue;
1029 		}
1030 		p = cline + strlen(cline);
1031 		while (p > cline)
1032 			if (!isspace(*--p)) {
1033 				p++;
1034 				break;
1035 			}
1036 		*p = '\0';
1037 		f = (struct filed *)calloc(1, sizeof(*f));
1038 		*nextp = f;
1039 		nextp = &f->f_next;
1040 		cfline(cline, f, prog);
1041 	}
1042 
1043 	/* close the configuration file */
1044 	(void)fclose(cf);
1045 
1046 	Initialized = 1;
1047 
1048 	if (Debug) {
1049 		for (f = Files; f; f = f->f_next) {
1050 			for (i = 0; i <= LOG_NFACILITIES; i++)
1051 				if (f->f_pmask[i] == INTERNAL_NOPRI)
1052 					printf("X ");
1053 				else
1054 					printf("%d ", f->f_pmask[i]);
1055 			printf("%s: ", TypeNames[f->f_type]);
1056 			switch (f->f_type) {
1057 			case F_FILE:
1058 			case F_TTY:
1059 			case F_CONSOLE:
1060 				printf("%s", f->f_un.f_fname);
1061 				break;
1062 
1063 			case F_FORW:
1064 				printf("%s", f->f_un.f_forw.f_hname);
1065 				break;
1066 
1067 			case F_USERS:
1068 				for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
1069 					printf("%s, ", f->f_un.f_uname[i]);
1070 				break;
1071 			}
1072 			if (f->f_program)
1073 				printf(" (%s)", f->f_program);
1074 			printf("\n");
1075 		}
1076 	}
1077 
1078 	logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
1079 	dprintf("syslogd: restarted\n");
1080 }
1081 
1082 /*
1083  * Crack a configuration file line
1084  */
1085 void
1086 cfline(line, f, prog)
1087 	char *line;
1088 	struct filed *f;
1089 	char *prog;
1090 {
1091 	struct hostent *hp;
1092 	int i, pri;
1093 	char *bp, *p, *q;
1094 	char buf[MAXLINE], ebuf[100];
1095 
1096 	dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
1097 
1098 	errno = 0;	/* keep strerror() stuff out of logerror messages */
1099 
1100 	/* clear out file entry */
1101 	memset(f, 0, sizeof(*f));
1102 	for (i = 0; i <= LOG_NFACILITIES; i++)
1103 		f->f_pmask[i] = INTERNAL_NOPRI;
1104 
1105 	/* save program name if any */
1106 	if (!strcmp(prog, "*"))
1107 		prog = NULL;
1108 	else {
1109 		f->f_program = calloc(1, strlen(prog)+1);
1110 		if (f->f_program)
1111 			strcpy(f->f_program, prog);
1112 	}
1113 
1114 	/* scan through the list of selectors */
1115 	for (p = line; *p && *p != '\t';) {
1116 
1117 		/* find the end of this facility name list */
1118 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
1119 			continue;
1120 
1121 		/* collect priority name */
1122 		for (bp = buf; *q && !strchr("\t,;", *q); )
1123 			*bp++ = *q++;
1124 		*bp = '\0';
1125 
1126 		/* skip cruft */
1127 		while (strchr(", ;", *q))
1128 			q++;
1129 
1130 		/* decode priority name */
1131 		if (*buf == '*')
1132 			pri = LOG_PRIMASK + 1;
1133 		else {
1134 			/* ignore trailing spaces */
1135 			int i;
1136 			for (i=strlen(buf)-1; i >= 0 && buf[i] == ' '; i--) {
1137 				buf[i]='\0';
1138 			}
1139 
1140 			pri = decode(buf, prioritynames);
1141 			if (pri < 0) {
1142 				(void)snprintf(ebuf, sizeof ebuf,
1143 				    "unknown priority name \"%s\"", buf);
1144 				logerror(ebuf);
1145 				return;
1146 			}
1147 		}
1148 
1149 		/* scan facilities */
1150 		while (*p && !strchr("\t.;", *p)) {
1151 			for (bp = buf; *p && !strchr("\t,;.", *p); )
1152 				*bp++ = *p++;
1153 			*bp = '\0';
1154 			if (*buf == '*')
1155 				for (i = 0; i < LOG_NFACILITIES; i++)
1156 					f->f_pmask[i] = pri;
1157 			else {
1158 				i = decode(buf, facilitynames);
1159 				if (i < 0) {
1160 					(void)snprintf(ebuf, sizeof(ebuf),
1161 					    "unknown facility name \"%s\"",
1162 					    buf);
1163 					logerror(ebuf);
1164 					return;
1165 				}
1166 				f->f_pmask[i >> 3] = pri;
1167 			}
1168 			while (*p == ',' || *p == ' ')
1169 				p++;
1170 		}
1171 
1172 		p = q;
1173 	}
1174 
1175 	/* skip to action part */
1176 	while (*p == '\t')
1177 		p++;
1178 
1179 	switch (*p)
1180 	{
1181 	case '@':
1182 		if (!InetInuse)
1183 			break;
1184 		(void)strncpy(f->f_un.f_forw.f_hname, ++p,
1185 		    sizeof(f->f_un.f_forw.f_hname)-1);
1186 		f->f_un.f_forw.f_hname[sizeof(f->f_un.f_forw.f_hname)-1] = '\0';
1187 		hp = gethostbyname(f->f_un.f_forw.f_hname);
1188 		if (hp == NULL) {
1189 			extern int h_errno;
1190 
1191 			logerror((char *)hstrerror(h_errno));
1192 			break;
1193 		}
1194 		memset(&f->f_un.f_forw.f_addr, 0,
1195 		    sizeof(f->f_un.f_forw.f_addr));
1196 		f->f_un.f_forw.f_addr.sin_len = sizeof(f->f_un.f_forw.f_addr);
1197 		f->f_un.f_forw.f_addr.sin_family = AF_INET;
1198 		f->f_un.f_forw.f_addr.sin_port = LogPort;
1199 		memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr,
1200 		    hp->h_length);
1201 		f->f_type = F_FORW;
1202 		break;
1203 
1204 	case '/':
1205 		(void)strcpy(f->f_un.f_fname, p);
1206 		if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
1207 			f->f_type = F_UNUSED;
1208 			logerror(p);
1209 			break;
1210 		}
1211 		if (isatty(f->f_file))
1212 			f->f_type = F_TTY;
1213 		else
1214 			f->f_type = F_FILE;
1215 		if (strcmp(p, ctty) == 0)
1216 			f->f_type = F_CONSOLE;
1217 		break;
1218 
1219 	case '*':
1220 		f->f_type = F_WALL;
1221 		break;
1222 
1223 	default:
1224 		for (i = 0; i < MAXUNAMES && *p; i++) {
1225 			for (q = p; *q && *q != ','; )
1226 				q++;
1227 			(void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
1228 			if ((q - p) > UT_NAMESIZE)
1229 				f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
1230 			else
1231 				f->f_un.f_uname[i][q - p] = '\0';
1232 			while (*q == ',' || *q == ' ')
1233 				q++;
1234 			p = q;
1235 		}
1236 		f->f_type = F_USERS;
1237 		break;
1238 	}
1239 }
1240 
1241 
1242 /*
1243  *  Decode a symbolic name to a numeric value
1244  */
1245 int
1246 decode(name, codetab)
1247 	const char *name;
1248 	CODE *codetab;
1249 {
1250 	CODE *c;
1251 	char *p, buf[40];
1252 
1253 	if (isdigit(*name))
1254 		return (atoi(name));
1255 
1256 	for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
1257 		if (isupper(*name))
1258 			*p = tolower(*name);
1259 		else
1260 			*p = *name;
1261 	}
1262 	*p = '\0';
1263 	for (c = codetab; c->c_name; c++)
1264 		if (!strcmp(buf, c->c_name))
1265 			return (c->c_val);
1266 
1267 	return (-1);
1268 }
1269