xref: /illumos-gate/usr/src/cmd/syslogd/syslogd.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2012 Milan Jurik. All rights reserved.
25  */
26 
27 /*
28  *	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
29  *	All Rights Reserved
30  */
31 
32 /*
33  * University Copyright- Copyright (c) 1982, 1986, 1988
34  * The Regents of the University of California
35  * All Rights Reserved
36  *
37  * University Acknowledgment- Portions of this document are derived from
38  * software developed by the University of California, Berkeley, and its
39  * contributors.
40  */
41 
42 /*
43  *  syslogd -- log system messages
44  *
45  * This program implements a system log. It takes a series of lines.
46  * Each line may have a priority, signified as "<n>" as
47  * the first characters of the line.  If this is
48  * not present, a default priority is used.
49  *
50  * To kill syslogd, send a signal 15 (terminate).  A signal 1 (hup) will
51  * cause it to reconfigure.
52  *
53  * Defined Constants:
54  *
55  * MAXLINE -- the maximimum line length that can be handled.
56  * DEFUPRI -- the default priority for user messages.
57  * DEFSPRI -- the default priority for kernel messages.
58  *
59  */
60 
61 #include <unistd.h>
62 #include <note.h>
63 #include <errno.h>
64 #include <sys/types.h>
65 #include <stdio.h>
66 #include <stdio_ext.h>
67 #include <stdlib.h>
68 #include <ctype.h>
69 #include <signal.h>
70 #include <string.h>
71 #include <strings.h>
72 #include <libscf.h>
73 #include <netconfig.h>
74 #include <netdir.h>
75 #include <pwd.h>
76 #include <sys/socket.h>
77 #include <tiuser.h>
78 #include <utmpx.h>
79 #include <limits.h>
80 #include <pthread.h>
81 #include <fcntl.h>
82 #include <stropts.h>
83 #include <assert.h>
84 #include <sys/statvfs.h>
85 
86 #include <sys/param.h>
87 #include <sys/sysmacros.h>
88 #include <sys/syslog.h>
89 #include <sys/strlog.h>
90 #include <sys/stat.h>
91 #include <sys/time.h>
92 #include <sys/utsname.h>
93 #include <sys/poll.h>
94 #include <sys/wait.h>
95 #include <sys/resource.h>
96 #include <sys/mman.h>
97 #include <sys/note.h>
98 #include <door.h>
99 
100 #include <wchar.h>
101 #include <locale.h>
102 #include <stdarg.h>
103 
104 #include "dataq.h"
105 #include "conf.h"
106 #include "syslogd.h"
107 
108 #define	DOORFILE		"/var/run/syslog_door"
109 #define	RELATIVE_DOORFILE	"../var/run/syslog_door"
110 #define	OLD_DOORFILE		"/etc/.syslog_door"
111 
112 #define	PIDFILE			"/var/run/syslog.pid"
113 #define	RELATIVE_PIDFILE	"../var/run/syslog.pid"
114 #define	OLD_PIDFILE		"/etc/syslog.pid"
115 
116 static char		*LogName = "/dev/log";
117 static char		*ConfFile = "/etc/syslog.conf";
118 static char		ctty[] = "/dev/console";
119 static char		sysmsg[] = "/dev/sysmsg";
120 static int		DoorFd = -1;
121 static int		DoorCreated = 0;
122 static int		PidfileCreated = 0;
123 static char		*DoorFileName = DOORFILE;
124 static char		*PidFileName = PIDFILE;
125 
126 /*
127  * configuration file directives
128  */
129 
130 static struct code	PriNames[] = {
131 	"panic",	LOG_EMERG,
132 	"emerg",	LOG_EMERG,
133 	"alert",	LOG_ALERT,
134 	"crit",		LOG_CRIT,
135 	"err",		LOG_ERR,
136 	"error",	LOG_ERR,
137 	"warn",		LOG_WARNING,
138 	"warning",	LOG_WARNING,
139 	"notice",	LOG_NOTICE,
140 	"info",		LOG_INFO,
141 	"debug",	LOG_DEBUG,
142 	"none",		NOPRI,
143 	NULL,		-1
144 };
145 
146 static struct code	FacNames[] = {
147 	"kern",		LOG_KERN,
148 	"user",		LOG_USER,
149 	"mail",		LOG_MAIL,
150 	"daemon",	LOG_DAEMON,
151 	"auth",		LOG_AUTH,
152 	"security",	LOG_AUTH,
153 	"mark",		LOG_MARK,
154 	"syslog",	LOG_SYSLOG,
155 	"lpr",		LOG_LPR,
156 	"news",		LOG_NEWS,
157 	"uucp",		LOG_UUCP,
158 	"audit",	LOG_AUDIT,
159 	"cron",		LOG_CRON,
160 	"local0",	LOG_LOCAL0,
161 	"local1",	LOG_LOCAL1,
162 	"local2",	LOG_LOCAL2,
163 	"local3",	LOG_LOCAL3,
164 	"local4",	LOG_LOCAL4,
165 	"local5",	LOG_LOCAL5,
166 	"local6",	LOG_LOCAL6,
167 	"local7",	LOG_LOCAL7,
168 	NULL,		-1
169 };
170 
171 static char		*TypeNames[7] = {
172 	"UNUSED",	"FILE",		"TTY",		"CONSOLE",
173 	"FORW",		"USERS",	"WALL"
174 };
175 
176 /*
177  * we allocate our own thread stacks so we can create them
178  * without the MAP_NORESERVE option. We need to be sure
179  * we have stack space even if the machine runs out of swap
180  */
181 
182 #define	DEFAULT_STACKSIZE (100 * 1024)  /* 100 k stack */
183 #define	DEFAULT_REDZONESIZE (8 * 1024)	/* 8k redzone */
184 
185 static pthread_mutex_t wmp = PTHREAD_MUTEX_INITIALIZER;	/* wallmsg lock */
186 
187 static pthread_mutex_t cft = PTHREAD_MUTEX_INITIALIZER;
188 static int conf_threads = 0;
189 
190 static pthread_mutex_t hup_lock = PTHREAD_MUTEX_INITIALIZER;
191 static pthread_cond_t hup_done = PTHREAD_COND_INITIALIZER;
192 
193 static pthread_mutex_t logerror_lock = PTHREAD_MUTEX_INITIALIZER;
194 
195 #define	HUP_ACCEPTABLE		0x0000	/* can start SIGHUP process */
196 #define	HUP_INPROGRESS		0x0001	/* SIGHUP process in progress */
197 #define	HUP_COMPLETED		0x0002	/* SIGHUP process completed */
198 #define	HUP_SUSP_LOGMSG_REQD	0x1000	/* request to suspend */
199 #define	HUP_LOGMSG_SUSPENDED	0x2000	/* logmsg is suspended */
200 static int hup_state = HUP_ACCEPTABLE;
201 
202 static size_t stacksize;		/* thread stack size */
203 static size_t redzonesize;		/* thread stack redzone size */
204 static char *stack_ptr;			/* ptr to allocated stacks */
205 static char *cstack_ptr;		/* ptr to conf_thr stacks */
206 
207 static time_t start_time;
208 
209 static pthread_t sys_thread;		/* queues messages from us */
210 static pthread_t net_thread;		/* queues messages from the net */
211 static pthread_t log_thread;		/* message processing thread */
212 static pthread_t hnl_thread;		/* hostname lookup thread */
213 
214 static dataq_t inputq;			/* the input queue */
215 static dataq_t tmpq;			/* temporary queue for err msg */
216 static dataq_t hnlq;			/* hostname lookup queue */
217 
218 static struct filed fallback[2];
219 static struct filed *Files;
220 static int nlogs;
221 static int Debug;			/* debug flag */
222 static host_list_t LocalHostName;	/* our hostname */
223 static host_list_t NullHostName;	/* in case of lookup failure */
224 static int debuglev = 1;		/* debug print level */
225 static int interrorlog;			/* internal error logging */
226 
227 static int MarkInterval = 20;		/* interval between marks (mins) */
228 static int Marking = 0;			/* non-zero if marking some file */
229 static int Ninputs = 0;			/* number of network inputs */
230 static int curalarm = 0;		/* current timeout value (secs) */
231 static int sys_msg_count = 0;		/* total msgs rcvd from local log */
232 static int sys_init_msg_count = 0;	/* initially received */
233 static int net_msg_count = 0;		/* total msgs rcvd from net */
234 
235 static struct pollfd Pfd;		/* Pollfd for local the log device */
236 static struct pollfd *Nfd;		/* Array of pollfds for udp ports */
237 static struct netconfig *Ncf;
238 static struct netbuf **Myaddrs;
239 static struct t_unitdata **Udp;
240 static struct t_uderr **Errp;
241 static int turnoff = 0;
242 static int shutting_down;
243 
244 /* for managing door server threads */
245 static pthread_mutex_t door_server_cnt_lock = PTHREAD_MUTEX_INITIALIZER;
246 static uint_t door_server_cnt = 0;
247 static pthread_attr_t door_thr_attr;
248 
249 static struct hostname_cache **hnc_cache;
250 static pthread_mutex_t hnc_mutex = PTHREAD_MUTEX_INITIALIZER;
251 static size_t hnc_size = DEF_HNC_SIZE;
252 static unsigned int hnc_ttl = DEF_HNC_TTL;
253 
254 #define	DPRINT0(d, m)		if ((Debug) && debuglev >= (d)) \
255 				(void) fprintf(stderr, m)
256 #define	DPRINT1(d, m, a)	if ((Debug) && debuglev >= (d)) \
257 				(void) fprintf(stderr, m, a)
258 #define	DPRINT2(d, m, a, b)	if ((Debug) && debuglev >= (d)) \
259 				(void) fprintf(stderr, m, a, b)
260 #define	DPRINT3(d, m, a, b, c)	if ((Debug) && debuglev >= (d)) \
261 				(void) fprintf(stderr, m, a, b, c)
262 #define	DPRINT4(d, m, a, b, c, e)	if ((Debug) && debuglev >= (d)) \
263 				(void) fprintf(stderr, m, a, b, c, e)
264 #define	MALLOC_FAIL(x)	\
265 		logerror("malloc failed: " x)
266 #define	MALLOC_FAIL_EXIT	\
267 		logerror("malloc failed - fatal"); \
268 		exit(1)
269 
270 
271 #define	MAILCMD "mailx -s \"syslogd shut down\" root"
272 
273 /*
274  * Number of seconds to wait before giving up on threads that won't
275  * shutdown: (that's right, 10 minutes!)
276  */
277 #define	LOOP_MAX	(10 * 60)
278 
279 /*
280  * Interval(sec) to check the status of output queue while processing
281  * HUP signal.
282  */
283 #define	LOOP_INTERVAL	(15)
284 
285 int
286 main(int argc, char **argv)
287 {
288 	int i;
289 	char *pstr;
290 	int sig, fd;
291 	int tflag = 0, Tflag = 0;
292 	sigset_t sigs, allsigs;
293 	struct rlimit rlim;
294 	char *debugstr;
295 	int mcount = 0;
296 	struct sigaction act;
297 	pthread_t mythreadno = 0;
298 	char cbuf [30];
299 	struct stat sb;
300 
301 #ifdef DEBUG
302 #define	DEBUGDIR "/var/tmp"
303 	if (chdir(DEBUGDIR))
304 		DPRINT2(1, "main(%u): Unable to cd to %s\n", mythreadno,
305 		    DEBUGDIR);
306 #endif /* DEBUG */
307 
308 	(void) setlocale(LC_ALL, "");
309 
310 	if ((debugstr = getenv("SYSLOGD_DEBUG")) != NULL)
311 		if ((debuglev = atoi(debugstr)) == 0)
312 			debuglev = 1;
313 
314 #if ! defined(TEXT_DOMAIN)	/* should be defined by cc -D */
315 #define	TEXT_DOMAIN "SYS_TEST"
316 #endif
317 	(void) textdomain(TEXT_DOMAIN);
318 
319 	(void) time(&start_time);
320 
321 	if (lstat("/var/run", &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
322 		DoorFileName = OLD_DOORFILE;
323 		PidFileName  = OLD_PIDFILE;
324 	}
325 
326 	properties();
327 
328 	while ((i = getopt(argc, argv, "df:p:m:tT")) != EOF) {
329 		switch (i) {
330 		case 'f':		/* configuration file */
331 			ConfFile = optarg;
332 			break;
333 
334 		case 'd':		/* debug */
335 			Debug++;
336 			break;
337 
338 		case 'p':		/* path */
339 			LogName = optarg;
340 			break;
341 
342 		case 'm':		/* mark interval */
343 			for (pstr = optarg; *pstr; pstr++) {
344 				if (! (isdigit(*pstr))) {
345 					(void) fprintf(stderr,
346 					    "Illegal interval\n");
347 					usage();
348 				}
349 			}
350 			MarkInterval = atoi(optarg);
351 			if (MarkInterval < 1 || MarkInterval > INT_MAX) {
352 				(void) fprintf(stderr,
353 				    "Interval must be between 1 and %d\n",
354 				    INT_MAX);
355 				usage();
356 			}
357 			break;
358 		case 't':		/* turn off remote reception */
359 			tflag++;
360 			turnoff++;
361 			break;
362 		case 'T':		/* turn on remote reception */
363 			Tflag++;
364 			turnoff = 0;
365 			break;
366 		default:
367 			usage();
368 		}
369 	}
370 
371 	if (optind < argc)
372 		usage();
373 
374 	if (tflag && Tflag) {
375 		(void) fprintf(stderr, "specify only one of -t and -T\n");
376 		usage();
377 	}
378 
379 	/*
380 	 * close all fd's except 0-2
381 	 */
382 
383 	closefrom(3);
384 
385 	if (!Debug) {
386 		if (fork())
387 			return (0);
388 		(void) close(0);
389 		(void) open("/", 0);
390 		(void) dup2(0, 1);
391 		(void) dup2(0, 2);
392 		untty();
393 	}
394 
395 	if (Debug) {
396 		mythreadno = pthread_self();
397 	}
398 
399 	/*
400 	 * DO NOT call logerror() until tmpq is initialized.
401 	 */
402 	disable_errorlog();
403 
404 	/*
405 	 * ensure that file descriptor limit is "high enough"
406 	 */
407 	(void) getrlimit(RLIMIT_NOFILE, &rlim);
408 	if (rlim.rlim_cur < rlim.rlim_max)
409 		rlim.rlim_cur = rlim.rlim_max;
410 	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
411 		logerror("Unable to increase file descriptor limit.");
412 	(void) enable_extended_FILE_stdio(-1, -1);
413 
414 	/* block all signals from all threads initially */
415 	(void) sigfillset(&allsigs);
416 	(void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL);
417 
418 	DPRINT2(1, "main(%u): Started at time %s", mythreadno,
419 	    ctime_r(&start_time, cbuf));
420 
421 	init();			/* read configuration, start threads */
422 
423 	DPRINT1(1, "main(%u): off & running....\n", mythreadno);
424 
425 	/* now set up to catch signals we care about */
426 
427 	(void) sigemptyset(&sigs);
428 	(void) sigaddset(&sigs, SIGHUP);	/* reconfigure */
429 	(void) sigaddset(&sigs, SIGALRM);	/* mark & flush timer */
430 	(void) sigaddset(&sigs, SIGTERM);	/* exit */
431 	(void) sigaddset(&sigs, SIGINT);	/* exit if debugging */
432 	(void) sigaddset(&sigs, SIGQUIT);	/* exit if debugging */
433 	(void) sigaddset(&sigs, SIGPIPE);	/* catch & discard */
434 	(void) sigaddset(&sigs, SIGUSR1);	/* dump debug stats */
435 
436 	/*
437 	 * We must set up to catch these signals, even though sigwait
438 	 * will get them before the isr does.  Setting SA_SIGINFO ensures
439 	 * that signals will be enqueued.
440 	 */
441 
442 	act.sa_flags = SA_SIGINFO;
443 	act.sa_sigaction = signull;
444 
445 	(void) sigaction(SIGHUP, &act, NULL);
446 	(void) sigaction(SIGALRM, &act, NULL);
447 	(void) sigaction(SIGTERM, &act, NULL);
448 	(void) sigaction(SIGINT, &act, NULL);
449 	(void) sigaction(SIGQUIT, &act, NULL);
450 	(void) sigaction(SIGPIPE, &act, NULL);
451 	(void) sigaction(SIGUSR1, &act, NULL);
452 
453 	/* we now turn into the signal handling thread */
454 
455 	DPRINT1(2, "main(%u): now handling signals\n", mythreadno);
456 	for (;;) {
457 		(void) sigwait(&sigs, &sig);
458 		DPRINT2(2, "main(%u): received signal %d\n", mythreadno, sig);
459 		switch (sig) {
460 		case SIGALRM:
461 			DPRINT1(1, "main(%u): Got SIGALRM\n",
462 			    mythreadno);
463 			flushmsg(NOCOPY);
464 			if (Marking && (++mcount % MARKCOUNT == 0)) {
465 				if (logmymsg(LOG_INFO, "-- MARK --",
466 				    ADDDATE|MARK|NOCOPY, 0) == -1) {
467 					MALLOC_FAIL(
468 					    "dropping MARK message");
469 				}
470 
471 				mcount = 0;
472 			}
473 			curalarm = MarkInterval * 60 / MARKCOUNT;
474 			(void) alarm((unsigned)curalarm);
475 			DPRINT2(2, "main(%u): Next alarm in %d "
476 			    "seconds\n", mythreadno, curalarm);
477 			break;
478 		case SIGHUP:
479 			DPRINT1(1, "main(%u): got SIGHUP - "
480 			    "reconfiguring\n", mythreadno);
481 
482 			reconfigure();
483 
484 			DPRINT1(1, "main(%u): done processing SIGHUP\n",
485 			    mythreadno);
486 			break;
487 		case SIGQUIT:
488 		case SIGINT:
489 			if (!Debug) {
490 				/* allow these signals if debugging */
491 				break;
492 			}
493 			/* FALLTHROUGH */
494 		case SIGTERM:
495 			DPRINT2(1, "main(%u): going down on signal %d\n",
496 			    mythreadno, sig);
497 			(void) alarm(0);
498 			flushmsg(0);
499 			errno = 0;
500 			t_errno = 0;
501 			logerror("going down on signal %d", sig);
502 			disable_errorlog();	/* force msg to console */
503 			(void) shutdown_msg();	/* stop threads */
504 			shutdown_input();
505 			close_door();
506 			delete_doorfiles();
507 			return (0);
508 		case SIGUSR1:			/* secret debug dump mode */
509 			/* if in debug mode, use stdout */
510 
511 			if (Debug) {
512 				dumpstats(STDOUT_FILENO);
513 				break;
514 			}
515 			/* otherwise dump to a debug file */
516 			if ((fd = open(DEBUGFILE,
517 			    (O_WRONLY|O_CREAT|O_TRUNC|O_EXCL),
518 			    0644)) < 0)
519 				break;
520 			dumpstats(fd);
521 			(void) close(fd);
522 			break;
523 		default:
524 			DPRINT2(2, "main(%u): unexpected signal %d\n",
525 			    mythreadno, sig);
526 			break;
527 		}
528 	}
529 }
530 
531 /*
532  * Attempts to open the local log device
533  * and return a file descriptor.
534  */
535 static int
536 openklog(char *name, int mode)
537 {
538 	int fd;
539 	struct strioctl str;
540 	pthread_t mythreadno;
541 
542 	if (Debug) {
543 		mythreadno = pthread_self();
544 	}
545 
546 	if ((fd = open(name, mode)) < 0) {
547 		logerror("cannot open %s", name);
548 		DPRINT3(1, "openklog(%u): cannot create %s (%d)\n",
549 		    mythreadno, name, errno);
550 		return (-1);
551 	}
552 	str.ic_cmd = I_CONSLOG;
553 	str.ic_timout = 0;
554 	str.ic_len = 0;
555 	str.ic_dp = NULL;
556 	if (ioctl(fd, I_STR, &str) < 0) {
557 		logerror("cannot register to log console messages");
558 		DPRINT2(1, "openklog(%u): cannot register to log "
559 		    "console messages (%d)\n", mythreadno, errno);
560 		return (-1);
561 	}
562 	return (fd);
563 }
564 
565 
566 /*
567  * Open the log device, and pull up all pending messages.
568  */
569 static void
570 prepare_sys_poll()
571 {
572 	int nfds, funix;
573 
574 	if ((funix = openklog(LogName, O_RDONLY)) < 0) {
575 		logerror("can't open kernel log device - fatal");
576 		exit(1);
577 	}
578 
579 	Pfd.fd = funix;
580 	Pfd.events = POLLIN;
581 
582 	for (;;) {
583 		nfds = poll(&Pfd, 1, 0);
584 		if (nfds <= 0) {
585 			if (sys_init_msg_count > 0)
586 				flushmsg(SYNC_FILE);
587 			break;
588 		}
589 
590 		if (Pfd.revents & POLLIN) {
591 			getkmsg(0);
592 		} else if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
593 			logerror("kernel log driver poll error");
594 			break;
595 		}
596 	}
597 
598 }
599 
600 /*
601  * this thread listens to the local stream log driver for log messages
602  * generated by this host, formats them, and queues them to the logger
603  * thread.
604  */
605 /*ARGSUSED*/
606 static void *
607 sys_poll(void *ap)
608 {
609 	int nfds;
610 	static int klogerrs = 0;
611 	pthread_t mythreadno;
612 
613 	if (Debug) {
614 		mythreadno = pthread_self();
615 	}
616 
617 	DPRINT1(1, "sys_poll(%u): sys_thread started\n", mythreadno);
618 
619 	/*
620 	 * Try to process as many messages as we can without blocking on poll.
621 	 * We count such "initial" messages with sys_init_msg_count and
622 	 * enqueue them without the SYNC_FILE flag.  When no more data is
623 	 * waiting on the local log device, we set timeout to INFTIM,
624 	 * clear sys_init_msg_count, and generate a flush message to sync
625 	 * the previously counted initial messages out to disk.
626 	 */
627 
628 	sys_init_msg_count = 0;
629 
630 	for (;;) {
631 		errno = 0;
632 		t_errno = 0;
633 
634 		nfds = poll(&Pfd, 1, INFTIM);
635 
636 		if (nfds == 0)
637 			continue;
638 
639 		if (nfds < 0) {
640 			if (errno != EINTR)
641 				logerror("poll");
642 			continue;
643 		}
644 		if (Pfd.revents & POLLIN) {
645 			getkmsg(INFTIM);
646 		} else {
647 			if (shutting_down) {
648 				pthread_exit(0);
649 			}
650 			if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
651 				logerror("kernel log driver poll error");
652 				(void) close(Pfd.fd);
653 				Pfd.fd = -1;
654 			}
655 		}
656 
657 		while (Pfd.fd == -1 && klogerrs++ < 10) {
658 			Pfd.fd = openklog(LogName, O_RDONLY);
659 		}
660 		if (klogerrs >= 10) {
661 			logerror("can't reopen kernel log device - fatal");
662 			exit(1);
663 		}
664 	}
665 	/*NOTREACHED*/
666 	return (NULL);
667 }
668 
669 /*
670  * Pull up one message from log driver.
671  */
672 static void
673 getkmsg(int timeout)
674 {
675 	int flags = 0, i;
676 	char *lastline;
677 	struct strbuf ctl, dat;
678 	struct log_ctl hdr;
679 	char buf[MAXLINE+1];
680 	size_t buflen;
681 	size_t len;
682 	char tmpbuf[MAXLINE+1];
683 	pthread_t mythreadno;
684 
685 	if (Debug) {
686 		mythreadno = pthread_self();
687 	}
688 
689 	dat.maxlen = MAXLINE;
690 	dat.buf = buf;
691 	ctl.maxlen = sizeof (struct log_ctl);
692 	ctl.buf = (caddr_t)&hdr;
693 
694 	while ((i = getmsg(Pfd.fd, &ctl, &dat, &flags)) == MOREDATA) {
695 		lastline = &dat.buf[dat.len];
696 		*lastline = '\0';
697 
698 		DPRINT2(5, "sys_poll:(%u): getmsg: dat.len = %d\n",
699 		    mythreadno, dat.len);
700 		buflen = strlen(buf);
701 		len = findnl_bkwd(buf, buflen);
702 
703 		(void) memcpy(tmpbuf, buf, len);
704 		tmpbuf[len] = '\0';
705 
706 		/*
707 		 * Format sys will enqueue the log message.
708 		 * Set the sync flag if timeout != 0, which
709 		 * means that we're done handling all the
710 		 * initial messages ready during startup.
711 		 */
712 		if (timeout == 0) {
713 			formatsys(&hdr, tmpbuf, 0);
714 			sys_init_msg_count++;
715 		} else {
716 			formatsys(&hdr, tmpbuf, 1);
717 		}
718 		sys_msg_count++;
719 
720 		if (len != buflen) {
721 			/* If anything remains in buf */
722 			size_t remlen;
723 
724 			if (buf[len] == '\n') {
725 				/* skip newline */
726 				len++;
727 			}
728 
729 			/*
730 			 *  Move the remaining bytes to
731 			 * the beginnning of buf.
732 			 */
733 
734 			remlen = buflen - len;
735 			(void) memcpy(buf, &buf[len], remlen);
736 			dat.maxlen = MAXLINE - remlen;
737 			dat.buf = &buf[remlen];
738 		} else {
739 			dat.maxlen = MAXLINE;
740 			dat.buf = buf;
741 		}
742 	}
743 
744 	if (i == 0 && dat.len > 0) {
745 		dat.buf[dat.len] = '\0';
746 		/*
747 		 * Format sys will enqueue the log message.
748 		 * Set the sync flag if timeout != 0, which
749 		 * means that we're done handling all the
750 		 * initial messages ready during startup.
751 		 */
752 		DPRINT2(5, "getkmsg(%u): getmsg: dat.maxlen = %d\n",
753 		    mythreadno, dat.maxlen);
754 		DPRINT2(5, "getkmsg(%u): getmsg: dat.len = %d\n",
755 		    mythreadno, dat.len);
756 		DPRINT2(5, "getkmsg(%u): getmsg: strlen(dat.buf) = %d\n",
757 		    mythreadno, strlen(dat.buf));
758 		DPRINT2(5, "getkmsg(%u): getmsg: dat.buf = \"%s\"\n",
759 		    mythreadno, dat.buf);
760 		DPRINT2(5, "getkmsg(%u): buf len = %d\n",
761 		    mythreadno, strlen(buf));
762 		if (timeout == 0) {
763 			formatsys(&hdr, buf, 0);
764 			sys_init_msg_count++;
765 		} else {
766 			formatsys(&hdr, buf, 1);
767 		}
768 		sys_msg_count++;
769 	} else if (i < 0 && errno != EINTR) {
770 		if (!shutting_down) {
771 			logerror("kernel log driver read error");
772 		}
773 		(void) close(Pfd.fd);
774 		Pfd.fd = -1;
775 	}
776 }
777 
778 /*
779  * this thread polls all the network interfaces for syslog messages
780  * forwarded to us, tags them with the hostname they are received
781  * from, and queues them to the logger thread.
782  */
783 /*ARGSUSED*/
784 static void *
785 net_poll(void *ap)
786 {
787 	int nfds, i;
788 	int flags = 0;
789 	struct t_unitdata *udp;
790 	struct t_uderr *errp;
791 	char buf[MAXLINE+1];
792 	char *uap;
793 	log_message_t *mp;
794 	host_info_t *hinfo;
795 	pthread_t mythreadno;
796 
797 	if (Debug) {
798 		mythreadno = pthread_self();
799 	}
800 
801 	DPRINT1(1, "net_poll(%u): net_thread started\n", mythreadno);
802 
803 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp))
804 
805 	for (;;) {
806 		errno = 0;
807 		t_errno = 0;
808 		nfds = poll(Nfd, Ninputs, -1);
809 		if (nfds == 0)
810 			continue;
811 
812 		if (nfds < 0) {
813 			if (errno != EINTR)
814 				logerror("poll");
815 			continue;
816 		}
817 		for (i = 0; nfds > 0 && i < Ninputs; i++) {
818 			if ((Nfd[i].revents & POLLIN) == 0) {
819 				if (shutting_down) {
820 					pthread_exit(0);
821 				}
822 				if (Nfd[i].revents &
823 				    (POLLNVAL|POLLHUP|POLLERR)) {
824 					logerror("POLLNVAL|POLLHUP|POLLERR");
825 					(void) t_close(Nfd[i].fd);
826 					Nfd[i].fd = -1;
827 					nfds--;
828 				}
829 				continue;
830 			}
831 
832 			udp = Udp[i];
833 			udp->udata.buf = buf;
834 			udp->udata.maxlen = MAXLINE;
835 			udp->udata.len = 0;
836 			flags = 0;
837 			if (t_rcvudata(Nfd[i].fd, udp, &flags) < 0) {
838 				errp = Errp[i];
839 				if (t_errno == TLOOK) {
840 					if (t_rcvuderr(Nfd[i].fd, errp) < 0) {
841 						if (!shutting_down) {
842 							logerror("t_rcvuderr");
843 						}
844 						(void) t_close(Nfd[i].fd);
845 						Nfd[i].fd = -1;
846 					}
847 				} else {
848 					if (!shutting_down) {
849 						logerror("t_rcvudata");
850 					}
851 					(void) t_close(Nfd[i].fd);
852 					Nfd[i].fd = -1;
853 				}
854 				nfds--;
855 				if (shutting_down) {
856 					pthread_exit(0);
857 				}
858 				continue;
859 			}
860 			nfds--;
861 
862 			if (udp->udata.len == 0) {
863 				if (Debug) {
864 					uap = NULL;
865 					if (udp->addr.len > 0) {
866 						uap = taddr2uaddr(&Ncf[i],
867 						    &udp->addr);
868 					}
869 					DPRINT2(1, "net_poll(%u):"
870 					    " received empty packet"
871 					    " from %s\n", mythreadno,
872 					    uap ? uap : "<unknown>");
873 					if (uap)
874 						free(uap);
875 				}
876 				continue;	/* No data */
877 			}
878 			if (udp->addr.len == 0) {
879 				/*
880 				 * The previous message was larger than
881 				 * MAXLINE, and T_MORE should have been set.
882 				 * Further data needs to be discarded as
883 				 * we've already received MAXLINE.
884 				 */
885 				DPRINT1(1, "net_poll(%u): discarding packet "
886 				    "exceeds max line size\n", mythreadno);
887 				continue;
888 			}
889 
890 			net_msg_count++;
891 
892 			if ((mp = new_msg()) == NULL) {
893 				MALLOC_FAIL("dropping message from "
894 				    "remote");
895 				continue;
896 			}
897 
898 			buf[udp->udata.len] = '\0';
899 			formatnet(&udp->udata, mp);
900 
901 			if (Debug) {
902 				uap = taddr2uaddr(&Ncf[i], &udp->addr);
903 				DPRINT2(1, "net_poll(%u): received message"
904 				    " from %s\n", mythreadno,
905 				    uap ? uap : "<unknown>");
906 				free(uap);
907 			}
908 			if ((hinfo = malloc(sizeof (*hinfo))) == NULL ||
909 			    (hinfo->addr.buf =
910 			    malloc(udp->addr.len)) == NULL) {
911 				MALLOC_FAIL("dropping message from "
912 				    "remote");
913 				if (hinfo) {
914 					free(hinfo);
915 				}
916 				free_msg(mp);
917 				continue;
918 			}
919 
920 			hinfo->ncp = &Ncf[i];
921 			hinfo->addr.len = udp->addr.len;
922 			(void) memcpy(hinfo->addr.buf, udp->addr.buf,
923 			    udp->addr.len);
924 			mp->ptr = hinfo;
925 			if (dataq_enqueue(&hnlq, (void *)mp) == -1) {
926 				MALLOC_FAIL("dropping message from "
927 				    "remote");
928 				free_msg(mp);
929 				free(hinfo->addr.buf);
930 				free(hinfo);
931 				continue;
932 			}
933 			DPRINT3(5, "net_poll(%u): enqueued msg %p "
934 			    "on queue %p\n", mythreadno, (void *)mp,
935 			    (void *)&hnlq);
936 		}
937 	}
938 	/*NOTREACHED*/
939 	return (NULL);
940 }
941 
942 static void
943 usage(void)
944 {
945 	(void) fprintf(stderr,
946 	    "usage: syslogd [-d] [-t|-T] [-mmarkinterval] [-ppath]"
947 	    " [-fconffile]\n");
948 	exit(1);
949 }
950 
951 static void
952 untty(void)
953 {
954 	if (!Debug)
955 		(void) setsid();
956 }
957 
958 /*
959  * generate a log message internally. The original version of syslogd
960  * simply called logmsg directly, but because everything is now based
961  * on message passing, we need an internal way to generate and queue
962  * log messages from within syslogd itself.
963  */
964 static int
965 logmymsg(int pri, char *msg, int flags, int pending)
966 {
967 	log_message_t *mp;
968 	pthread_t mythreadno;
969 	dataq_t *qptr;
970 
971 	if (Debug) {
972 		mythreadno = pthread_self();
973 	}
974 
975 	if ((mp = new_msg()) == NULL) {
976 		return (-1);
977 	}
978 
979 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp))
980 	mp->pri = pri;
981 	mp->hlp = &LocalHostName;
982 	(void) strlcpy(mp->msg, msg, MAXLINE+1);
983 	mp->flags = flags;
984 	(void) time(&mp->ts);
985 
986 	qptr = pending ? &tmpq : &inputq;
987 	if (dataq_enqueue(qptr, (void *)mp) == -1) {
988 		free_msg(mp);
989 		return (-1);
990 	}
991 
992 	DPRINT3(5, "logmymsg(%u): enqueued msg %p on queue %p\n",
993 	    mythreadno, (void *)mp, (void *)qptr);
994 	DPRINT2(5, "logmymsg(%u): Message content: %s\n", mythreadno, msg);
995 	return (0);
996 }
997 
998 /*
999  * Generate an internal shutdown message
1000  */
1001 static int
1002 shutdown_msg(void)
1003 {
1004 	pthread_t mythreadno;
1005 	log_message_t *mp;
1006 
1007 	if (Debug) {
1008 		mythreadno = pthread_self();
1009 	}
1010 
1011 	if ((mp = new_msg()) == NULL) {
1012 		return (-1);
1013 	}
1014 
1015 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp));
1016 	mp->flags = SHUTDOWN;
1017 	mp->hlp = &LocalHostName;
1018 
1019 	if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1020 		free_msg(mp);
1021 		return (-1);
1022 	}
1023 
1024 	DPRINT3(5, "shutdown_msg(%u): enqueued msg %p on queue %p\n",
1025 	    mythreadno, (void *)mp, (void *)&inputq);
1026 	return (0);
1027 }
1028 
1029 /*
1030  * Generate an internal flush message
1031  */
1032 static void
1033 flushmsg(int flags)
1034 {
1035 	log_message_t *mp;
1036 	pthread_t mythreadno;
1037 
1038 	if (Debug) {
1039 		mythreadno = pthread_self();
1040 	}
1041 
1042 	if ((mp = new_msg()) == NULL) {
1043 		MALLOC_FAIL("dropping flush msg");
1044 		return;
1045 	}
1046 
1047 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp));
1048 	mp->flags = FLUSHMSG | flags;
1049 	mp->hlp = &LocalHostName;
1050 
1051 	if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1052 		free_msg(mp);
1053 		MALLOC_FAIL("dropping flush msg");
1054 		return;
1055 	}
1056 
1057 	DPRINT4(5, "flush_msg(%u): enqueued msg %p on queue %p, flags "
1058 	    "0x%x\n", mythreadno, (void *)mp, (void *)&inputq, flags);
1059 }
1060 
1061 /*
1062  * Do some processing on messages received from the net
1063  */
1064 static void
1065 formatnet(struct netbuf *nbp, log_message_t *mp)
1066 {
1067 	char *p;
1068 	int pri;
1069 	pthread_t mythreadno;
1070 
1071 	if (Debug) {
1072 		mythreadno = pthread_self();
1073 	}
1074 
1075 	DPRINT2(5, "formatnet(%u): called for msg %p\n", mythreadno,
1076 	    (void *)mp);
1077 
1078 	mp->flags = NETWORK;
1079 	(void) time(&mp->ts);
1080 
1081 	/* test for special codes */
1082 	pri = DEFUPRI;
1083 	p = nbp->buf;
1084 	DPRINT2(9, "formatnet(%u): Message content:\n>%s<\n", mythreadno,
1085 	    p);
1086 	if (*p == '<' && isdigit(*(p+1))) {
1087 		pri = 0;
1088 		while (isdigit(*++p))
1089 			pri = 10 * pri + (*p - '0');
1090 		if (*p == '>')
1091 			++p;
1092 		if (pri <= 0 || pri >= (LOG_NFACILITIES << 3))
1093 			pri = DEFUPRI;
1094 	}
1095 
1096 	mp->pri = pri;
1097 	(void) strlcpy(mp->msg, p, MAXLINE+1);
1098 }
1099 
1100 /*
1101  * Do some processing on messages generated by this host
1102  * and then enqueue the log message.
1103  */
1104 static void
1105 formatsys(struct log_ctl *lp, char *msg, int sync)
1106 {
1107 	char *p, *q;
1108 	char line[MAXLINE + 1];
1109 	size_t msglen;
1110 	log_message_t	*mp;
1111 	char cbuf[30];
1112 	pthread_t mythreadno;
1113 
1114 	if (Debug) {
1115 		mythreadno = pthread_self();
1116 	}
1117 
1118 	DPRINT3(3, "formatsys(%u): log_ctl.mid = %d, log_ctl.sid = %d\n",
1119 	    mythreadno, lp->mid, lp->sid);
1120 	DPRINT2(9, "formatsys(%u): Message Content:\n>%s<\n", mythreadno,
1121 	    msg);
1122 
1123 	/* msglen includes the null termination */
1124 	msglen = strlen(msg) + 1;
1125 
1126 	for (p = msg; *p != '\0'; ) {
1127 		size_t linelen;
1128 		size_t len;
1129 
1130 		/*
1131 		 * Allocate a log_message_t structure.
1132 		 * We should do it here since a single message (msg)
1133 		 * could be composed of many lines.
1134 		 */
1135 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp));
1136 
1137 		if ((mp = new_msg()) == NULL) {
1138 			MALLOC_FAIL("dropping message");
1139 			/*
1140 			 * Should bail out from the loop.
1141 			 */
1142 			break;
1143 		}
1144 
1145 		mp->flags &= ~NETWORK;
1146 		mp->hlp = &LocalHostName;
1147 		mp->ts = lp->ttime;
1148 		if (lp->flags & SL_LOGONLY)
1149 			mp->flags |= IGN_CONS;
1150 		if (lp->flags & SL_CONSONLY)
1151 			mp->flags |= IGN_FILE;
1152 
1153 		/* extract facility */
1154 		if ((lp->pri & LOG_FACMASK) == LOG_KERN) {
1155 			(void) sprintf(line, "%.15s ",
1156 			    ctime_r(&mp->ts, cbuf) + 4);
1157 		} else {
1158 			(void) sprintf(line, "");
1159 		}
1160 
1161 		linelen = strlen(line);
1162 		q = line + linelen;
1163 
1164 		DPRINT2(5, "formatsys(%u): msglen = %d\n", mythreadno, msglen);
1165 		len = copynl_frwd(q, MAXLINE + 1 - linelen, p, msglen);
1166 		DPRINT2(5, "formatsys(%u): len (copynl_frwd) = %d\n",
1167 		    mythreadno, len);
1168 
1169 		p += len;
1170 		msglen -= len;
1171 
1172 		if (*p == '\n') {
1173 			/* skip newline */
1174 			p++;
1175 		}
1176 
1177 		if (sync && ((lp->pri & LOG_FACMASK) == LOG_KERN))
1178 			mp->flags |= SYNC_FILE;	/* fsync file after write */
1179 
1180 		if (len != 0) {
1181 			(void) strlcpy(mp->msg, line, MAXLINE+1);
1182 			mp->pri = lp->pri;
1183 
1184 			if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1185 				free_msg(mp);
1186 				MALLOC_FAIL("dropping message");
1187 				break;
1188 			}
1189 
1190 			DPRINT3(5, "formatsys(%u): sys_thread enqueued msg "
1191 			    "%p on queue %p\n", mythreadno, (void *)mp,
1192 			    (void *)&inputq);
1193 		} else
1194 			free_msg(mp);
1195 	}
1196 }
1197 
1198 /*
1199  * Log a message to the appropriate log files, users, etc. based on
1200  * the priority.
1201  */
1202 /*ARGSUSED*/
1203 static void *
1204 logmsg(void *ap)
1205 {
1206 	struct filed *f;
1207 	int fac, prilev, flags, refcnt;
1208 	int fake_shutdown, skip_shutdown;
1209 	log_message_t *mp, *save_mp;
1210 	pthread_t mythreadno;
1211 
1212 	if (Debug) {
1213 		mythreadno = pthread_self();
1214 	}
1215 
1216 	DPRINT1(1, "logmsg(%u): msg dispatcher started\n", mythreadno);
1217 
1218 	fake_shutdown = skip_shutdown = 0;
1219 	save_mp = NULL;
1220 	for (;;) {
1221 		if (save_mp) {
1222 			/*
1223 			 * If we have set aside a message in order to fake a
1224 			 * SHUTDOWN, use that message before picking from the
1225 			 * queue again.
1226 			 */
1227 			mp = save_mp;
1228 			save_mp = NULL;
1229 		} else {
1230 			(void) dataq_dequeue(&inputq, (void **)&mp, 0);
1231 		}
1232 		_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp))
1233 		DPRINT3(5, "logmsg(%u): msg dispatcher dequeued %p from "
1234 		    "queue %p\n", mythreadno, (void *)mp,
1235 		    (void *)&inputq);
1236 
1237 		/*
1238 		 * In most cases, if the message traffic is low, logmsg() wakes
1239 		 * up when it receives the SHUTDOWN msg, and will sleep until
1240 		 * HUP process is complete.  However, if the inputq is too
1241 		 * long, logmsg() may not receive SHUTDOWN before reconfigure()
1242 		 * releases the logger fds, filed and logit threads.  That, in
1243 		 * turn, will cause logmsg to refer to invalid fileds.
1244 		 *
1245 		 * logmsg() needs to respond to the SHUTDOWN message within
1246 		 * LOOP_INTERVAL seconds when reconfigure() enqueues it. It
1247 		 * does so in most cases.  When it does not respond in time,
1248 		 * logmsg() needs to be in suspended state immediately, since
1249 		 * filed may have been invalidated. reconfigure() will set the
1250 		 * HUP_SUSP_LOGMSG_REQD bit in hup_state and wait another
1251 		 * LOOP_INTERVAL seconds before proceeding.
1252 		 *
1253 		 * When HUP_SUSP_LOGMSG_REQD is set, we will create a fake
1254 		 * SHUTDOWN message, and dispatch it to the various logit
1255 		 * threads, and logmsg() itself will suspend.  In order to
1256 		 * ignore the real SHUTDOWN which will arrive later, we keep a
1257 		 * counter (skip_shutdown) and decrement it when the SHUTDOWN
1258 		 * message arrives.
1259 		 */
1260 		if ((hup_state & HUP_SUSP_LOGMSG_REQD) &&
1261 		    (mp->flags & SHUTDOWN) == 0) {
1262 			DPRINT1(3, "logmsg(%u): suspend request\n",
1263 			    mythreadno);
1264 
1265 			save_mp = mp;
1266 
1267 			/* create a fake SHUTDOWN msg */
1268 			if ((mp = new_msg()) == NULL) {
1269 				MALLOC_FAIL("dropping message");
1270 				if (mp->flags & SHUTDOWN) {
1271 					(void) logerror_to_console(1,
1272 					    "unable to shutdown "
1273 					    "logger thread");
1274 				}
1275 				continue;
1276 			}
1277 			mp->flags = SHUTDOWN;
1278 			mp->hlp = &LocalHostName;
1279 			fake_shutdown = 1;
1280 			skip_shutdown++;
1281 			DPRINT2(3, "logmsg(%u): pending SHUTDOWN %d\n",
1282 			    mythreadno, skip_shutdown);
1283 		}
1284 
1285 		/*
1286 		 * is it a shutdown or flush message ?
1287 		 */
1288 		if ((mp->flags & SHUTDOWN) || (mp->flags & FLUSHMSG)) {
1289 			(void) pthread_mutex_lock(&mp->msg_mutex);
1290 
1291 			if ((mp->flags & SHUTDOWN) &&
1292 			    !fake_shutdown && skip_shutdown > 0) {
1293 				skip_shutdown--;
1294 				(void) pthread_mutex_unlock(&mp->msg_mutex);
1295 				free_msg(mp);
1296 				DPRINT2(3, "logmsg(%u): released late "
1297 				    "arrived SHUTDOWN. pending %d\n",
1298 				    mythreadno, skip_shutdown);
1299 				continue;
1300 			}
1301 
1302 			for (f = Files; f < &Files[nlogs]; f++) {
1303 				(void) pthread_mutex_lock(&f->filed_mutex);
1304 
1305 				if (f->f_type == F_UNUSED) {
1306 					(void) pthread_mutex_unlock(
1307 					    &f->filed_mutex);
1308 					continue;
1309 				}
1310 
1311 				f->f_queue_count++;
1312 				mp->refcnt++;
1313 
1314 				if (dataq_enqueue(&f->f_queue,
1315 				    (void *)mp) == -1) {
1316 					f->f_queue_count--;
1317 					mp->refcnt--;
1318 					(void) pthread_mutex_unlock(
1319 					    &f->filed_mutex);
1320 					MALLOC_FAIL("dropping message");
1321 
1322 					if (mp->flags & SHUTDOWN) {
1323 						(void) logerror_to_console(1,
1324 						    "unable to shutdown "
1325 						    "logger thread");
1326 					}
1327 
1328 					continue;
1329 				}
1330 				DPRINT3(5, "logmsg(%u): enqueued msg %p "
1331 				    "on queue %p\n", mythreadno,
1332 				    (void *)mp, (void *)&f->f_queue);
1333 				(void) pthread_mutex_unlock(&f->filed_mutex);
1334 			}
1335 
1336 			/*
1337 			 * flags value needs to be saved because mp may
1338 			 * have been freed before SHUTDOWN test below.
1339 			 */
1340 			flags = mp->flags;
1341 			refcnt = mp->refcnt;
1342 
1343 			(void) pthread_mutex_unlock(&mp->msg_mutex);
1344 			if (refcnt == 0)
1345 				free_msg(mp);
1346 
1347 			if (flags & SHUTDOWN) {
1348 				(void) pthread_mutex_lock(&hup_lock);
1349 				while (hup_state != HUP_COMPLETED) {
1350 					hup_state |= HUP_LOGMSG_SUSPENDED;
1351 					(void) pthread_cond_wait(&hup_done,
1352 					    &hup_lock);
1353 					hup_state &= ~HUP_LOGMSG_SUSPENDED;
1354 				}
1355 				hup_state = HUP_ACCEPTABLE;
1356 				(void) pthread_mutex_unlock(&hup_lock);
1357 				fake_shutdown = 0;
1358 			}
1359 			continue;
1360 		}
1361 
1362 		/*
1363 		 * Check to see if msg looks non-standard.
1364 		 */
1365 		if ((int)strlen(mp->msg) < 16 || mp->msg[3] != ' ' ||
1366 		    mp->msg[6] != ' ' || mp->msg[9] != ':' ||
1367 		    mp->msg[12] != ':' || mp->msg[15] != ' ')
1368 			mp->flags |= ADDDATE;
1369 
1370 		/* extract facility and priority level */
1371 		fac = (mp->pri & LOG_FACMASK) >> 3;
1372 		if (mp->flags & MARK)
1373 			fac = LOG_NFACILITIES;
1374 		prilev = mp->pri & LOG_PRIMASK;
1375 
1376 		DPRINT3(3, "logmsg(%u): fac = %d, pri = %d\n",
1377 		    mythreadno, fac, prilev);
1378 
1379 		/*
1380 		 * Because different devices log at different speeds,
1381 		 * it's important to hold the mutex for the current
1382 		 * message until it's been enqueued to all log files,
1383 		 * so the reference count is accurate before any
1384 		 * of the log threads can decrement it.
1385 		 */
1386 		_NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*mp))
1387 		_NOTE(COMPETING_THREADS_NOW)
1388 		(void) pthread_mutex_lock(&mp->msg_mutex);
1389 
1390 		for (f = Files; f < &Files[nlogs]; f++) {
1391 			/* skip messages that are incorrect priority */
1392 			if (f->f_pmask[fac] < (unsigned)prilev ||
1393 			    f->f_pmask[fac] == NOPRI)
1394 				continue;
1395 			if (f->f_queue_count > Q_HIGHWATER_MARK) {
1396 				DPRINT4(5, "logmsg(%u): Dropping message "
1397 				    "%p on file %p, count = %d\n",
1398 				    mythreadno, (void *)mp, (void *)f,
1399 				    f->f_queue_count);
1400 				continue;
1401 			}
1402 
1403 			/*
1404 			 * Need to grab filed_mutex before testing the f_type.
1405 			 * Otherwise logit() may set F_UNUSED after the test
1406 			 * below, and start pulling out the pending messages.
1407 			 */
1408 
1409 			(void) pthread_mutex_lock(&f->filed_mutex);
1410 
1411 			if (f->f_type == F_UNUSED ||
1412 			    (f->f_type == F_FILE && (mp->flags & IGN_FILE)) ||
1413 			    (f->f_type == F_CONSOLE &&
1414 			    (mp->flags & IGN_CONS))) {
1415 				(void) pthread_mutex_unlock(&f->filed_mutex);
1416 				continue;
1417 			}
1418 
1419 			f->f_queue_count++;
1420 			mp->refcnt++;
1421 
1422 			if (dataq_enqueue(&f->f_queue, (void *)mp) == -1) {
1423 				f->f_queue_count--;
1424 				mp->refcnt--;
1425 				(void) pthread_mutex_unlock(&f->filed_mutex);
1426 				MALLOC_FAIL("dropping message");
1427 				continue;
1428 			}
1429 
1430 			DPRINT3(5, "logmsg(%u): enqueued msg %p on queue "
1431 			    "%p\n", mythreadno, (void *)mp,
1432 			    (void *)&f->f_queue);
1433 			(void) pthread_mutex_unlock(&f->filed_mutex);
1434 		}
1435 		refcnt = mp->refcnt;
1436 		(void) pthread_mutex_unlock(&mp->msg_mutex);
1437 		if (refcnt == 0)
1438 			free_msg(mp);
1439 	}
1440 	/*NOTREACHED*/
1441 	return (NULL);
1442 }
1443 
1444 /*
1445  * function to actually write the log message to the selected file.
1446  * each file has a logger thread that runs this routine. The function
1447  * is called with a pointer to its file structure.
1448  */
1449 static void *
1450 logit(void *ap)
1451 {
1452 	struct filed *f = ap;
1453 	log_message_t *mp;
1454 	int forwardingloop = 0;
1455 	const char *errmsg = "logit(%u): %s to %s forwarding loop detected\n";
1456 	int i, currofst, prevofst, refcnt;
1457 	host_list_t *hlp;
1458 
1459 	assert(f != NULL);
1460 
1461 	DPRINT4(5, "logit(%u): logger started for \"%s\" (queue %p, filed "
1462 	    "%p)\n", f->f_thread, f->f_un.f_fname, (void *)&f->f_queue,
1463 	    (void *)f);
1464 	_NOTE(COMPETING_THREADS_NOW);
1465 
1466 	while (f->f_type != F_UNUSED) {
1467 		(void) dataq_dequeue(&f->f_queue, (void **)&mp, 0);
1468 		DPRINT3(5, "logit(%u): logger dequeued msg %p from queue "
1469 		    "%p\n", f->f_thread, (void *)mp, (void *)&f->f_queue);
1470 		(void) pthread_mutex_lock(&f->filed_mutex);
1471 		assert(f->f_queue_count > 0);
1472 		f->f_queue_count--;
1473 		(void) pthread_mutex_unlock(&f->filed_mutex);
1474 		assert(mp->refcnt > 0);
1475 
1476 		/*
1477 		 * is it a shutdown message ?
1478 		 */
1479 		if (mp->flags & SHUTDOWN) {
1480 			(void) pthread_mutex_lock(&mp->msg_mutex);
1481 			refcnt = --mp->refcnt;
1482 			(void) pthread_mutex_unlock(&mp->msg_mutex);
1483 			if (refcnt == 0)
1484 				free_msg(mp);
1485 			break;
1486 		}
1487 
1488 		/*
1489 		 * Is it a logsync message?
1490 		 */
1491 		if ((mp->flags & (FLUSHMSG | LOGSYNC)) ==
1492 		    (FLUSHMSG | LOGSYNC)) {
1493 			if (f->f_type != F_FILE)
1494 				goto out;	/* nothing to do */
1495 			(void) close(f->f_file);
1496 			f->f_file = open64(f->f_un.f_fname,
1497 			    O_WRONLY|O_APPEND|O_NOCTTY);
1498 			if (f->f_file < 0) {
1499 				f->f_type = F_UNUSED;
1500 				logerror(f->f_un.f_fname);
1501 				f->f_stat.errs++;
1502 			}
1503 			goto out;
1504 		}
1505 
1506 		/*
1507 		 * If the message flags include both flush and sync,
1508 		 * then just sync the file out to disk if appropriate.
1509 		 */
1510 		if ((mp->flags & (FLUSHMSG | SYNC_FILE)) ==
1511 		    (FLUSHMSG | SYNC_FILE)) {
1512 			if (f->f_type == F_FILE) {
1513 				DPRINT2(5, "logit(%u): got FLUSH|SYNC "
1514 				    "for filed %p\n", f->f_thread,
1515 				    (void *)f);
1516 				(void) fsync(f->f_file);
1517 			}
1518 			goto out;
1519 		}
1520 
1521 		/*
1522 		 * Otherwise if it's a standard flush message, write
1523 		 * out any saved messages to the file.
1524 		 */
1525 		if ((mp->flags & FLUSHMSG) && (f->f_prevcount > 0)) {
1526 			set_flush_msg(f);
1527 			writemsg(SAVED, f);
1528 			goto out;
1529 		}
1530 
1531 		(void) strlcpy(f->f_current.msg, mp->msg, MAXLINE+1);
1532 		(void) strlcpy(f->f_current.host, mp->hlp->hl_hosts[0],
1533 		    SYS_NMLN);
1534 		f->f_current.pri = mp->pri;
1535 		f->f_current.flags = mp->flags;
1536 		f->f_current.time = mp->ts;
1537 		f->f_msgflag &= ~CURRENT_VALID;
1538 		hlp = mp->hlp;
1539 
1540 		prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16;
1541 		currofst = (f->f_current.flags & ADDDATE) ? 0 : 16;
1542 
1543 		if (f->f_type == F_FORW) {
1544 			/*
1545 			 * Should not forward MARK messages, as they are
1546 			 * not defined outside of the current system.
1547 			 */
1548 
1549 			if (mp->flags & MARK) {
1550 				DPRINT1(1, "logit(%u): cannot forward "
1551 				    "Mark\n", f->f_thread);
1552 				goto out;
1553 			}
1554 
1555 			/*
1556 			 * can not forward message if we do
1557 			 * not have a host to forward to
1558 			 */
1559 			if (hlp == (host_list_t *)NULL)
1560 				goto out;
1561 			/*
1562 			 * a forwarding loop is created on machines
1563 			 * with multiple interfaces because the
1564 			 * network address of the sender is different
1565 			 * to the receiver even though it is the
1566 			 * same machine. Instead, if the
1567 			 * hostname the source and target are
1568 			 * the same the message if thrown away
1569 			 */
1570 			forwardingloop = 0;
1571 			for (i = 0; i < hlp->hl_cnt; i++) {
1572 				if (strcmp(hlp->hl_hosts[i],
1573 				    f->f_un.f_forw.f_hname) == 0) {
1574 					DPRINT3(1, errmsg, f->f_thread,
1575 					    f->f_un.f_forw.f_hname,
1576 					    hlp->hl_hosts[i]);
1577 					forwardingloop = 1;
1578 					break;
1579 				}
1580 			}
1581 
1582 			if (forwardingloop == 1) {
1583 				f->f_stat.cantfwd++;
1584 				goto out;
1585 			}
1586 		}
1587 
1588 		f->f_msgflag |= CURRENT_VALID;
1589 
1590 		/* check for dup message */
1591 		if (f->f_type != F_FORW &&
1592 		    (f->f_msgflag & OLD_VALID) &&
1593 		    prevofst == currofst &&
1594 		    (strcmp(f->f_prevmsg.msg + prevofst,
1595 		    f->f_current.msg + currofst) == 0) &&
1596 		    (strcmp(f->f_prevmsg.host,
1597 		    f->f_current.host) == 0)) {
1598 			/* a dup */
1599 			DPRINT2(2, "logit(%u): msg is dup - %p\n",
1600 			    f->f_thread, (void *)mp);
1601 			if (currofst == 16) {
1602 				(void) strncpy(f->f_prevmsg.msg,
1603 				    f->f_current.msg, 15); /* update time */
1604 			}
1605 			f->f_prevcount++;
1606 			f->f_stat.dups++;
1607 			f->f_stat.total++;
1608 			f->f_msgflag &= ~CURRENT_VALID;
1609 		} else {
1610 			/* new: mark or prior dups exist */
1611 			if (f->f_current.flags & MARK || f->f_prevcount > 0) {
1612 				if (f->f_prevcount > 0 && f->f_type != F_FORW) {
1613 					set_flush_msg(f);
1614 					if (f->f_msgflag & OLD_VALID) {
1615 						writemsg(SAVED, f);
1616 					}
1617 				}
1618 				if (f->f_msgflag & CURRENT_VALID)
1619 					writemsg(CURRENT, f);
1620 				if (!(mp->flags & NOCOPY))
1621 					copy_msg(f);
1622 				if (f->f_current.flags & MARK) {
1623 					DPRINT2(2, "logit(%u): msg is "
1624 					    "mark - %p)\n", f->f_thread,
1625 					    (void *)mp);
1626 					f->f_msgflag &= ~OLD_VALID;
1627 				} else {
1628 					DPRINT2(2, "logit(%u): saving "
1629 					    "message - %p\n", f->f_thread,
1630 					    (void *)mp);
1631 				}
1632 				f->f_stat.total++;
1633 			} else { /* new message */
1634 				DPRINT2(2, "logit(%u): msg is new "
1635 				    "- %p\n", f->f_thread, (void *)mp);
1636 				writemsg(CURRENT, f);
1637 				if (!(mp->flags & NOCOPY))
1638 					copy_msg(f);
1639 				f->f_stat.total++;
1640 			}
1641 		}
1642 		/*
1643 		 * if message refcnt goes to zero after we decrement
1644 		 * it here, we are the last consumer of the message,
1645 		 * and we should free it.  We need to hold the lock
1646 		 * between decrementing the count and checking for
1647 		 * zero so another thread doesn't beat us to it.
1648 		 */
1649 out:
1650 		(void) pthread_mutex_lock(&mp->msg_mutex);
1651 		refcnt = --mp->refcnt;
1652 		(void) pthread_mutex_unlock(&mp->msg_mutex);
1653 		if (refcnt == 0)
1654 			free_msg(mp);
1655 	}
1656 	/* register our exit */
1657 
1658 	/*
1659 	 * Pull out all pending messages, if they exist.
1660 	 */
1661 
1662 	(void) pthread_mutex_lock(&f->filed_mutex);
1663 
1664 	while (f->f_queue_count > 0) {
1665 		(void) dataq_dequeue(&f->f_queue, (void **)&mp, 0);
1666 		DPRINT3(5, "logit(%u): logger dequeued msg %p from queue "
1667 		    "%p\n",
1668 		    f->f_thread, (void *)mp, (void *)&f->f_queue);
1669 		(void) pthread_mutex_lock(&mp->msg_mutex);
1670 		refcnt = --mp->refcnt;
1671 		(void) pthread_mutex_unlock(&mp->msg_mutex);
1672 		if (refcnt == 0)
1673 			free_msg(mp);
1674 		f->f_queue_count--;
1675 	}
1676 
1677 	(void) pthread_mutex_unlock(&f->filed_mutex);
1678 
1679 	if (f->f_type != F_USERS && f->f_type != F_WALL &&
1680 	    f->f_type != F_UNUSED) {
1681 		if (f->f_type == F_FORW)
1682 			(void) t_close(f->f_file);
1683 		else
1684 			(void) close(f->f_file);
1685 	}
1686 
1687 	/*
1688 	 * Since f_type may have been changed before this point, we need
1689 	 * to test orig_type.
1690 	 */
1691 	if (f->f_orig_type == F_FORW) {
1692 		free(f->f_un.f_forw.f_addr.buf);
1693 	}
1694 
1695 	f->f_type = F_UNUSED;
1696 	(void) pthread_mutex_lock(&cft);
1697 	--conf_threads;
1698 	(void) pthread_mutex_unlock(&cft);
1699 	DPRINT1(5, "logit(%u): logging thread exited\n", f->f_thread);
1700 	return (NULL);
1701 }
1702 
1703 /*
1704  * change the previous message to a flush message, stating how
1705  * many repeats occurred since the last flush
1706  */
1707 static void
1708 set_flush_msg(struct filed *f)
1709 {
1710 	char tbuf[10];
1711 	int prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16;
1712 
1713 	if (f->f_prevcount == 1)
1714 		(void) strncpy(tbuf, "time", sizeof (tbuf));
1715 	else
1716 		(void) strncpy(tbuf, "times", sizeof (tbuf));
1717 
1718 	(void) snprintf(f->f_prevmsg.msg+prevofst,
1719 	    sizeof (f->f_prevmsg.msg) - prevofst,
1720 	    "last message repeated %d %s", f->f_prevcount, tbuf);
1721 	f->f_prevcount = 0;
1722 	f->f_msgflag |= OLD_VALID;
1723 }
1724 
1725 
1726 /*
1727  * the actual writing of the message is broken into a separate function
1728  * because each file has a current and saved message associated with
1729  * it (for duplicate message detection). It is necessary to be able
1730  * to write either the saved message or the current message.
1731  */
1732 static void
1733 writemsg(int selection, struct filed *f)
1734 {
1735 	char *cp, *p;
1736 	int pri;
1737 	int flags;
1738 	int l;
1739 	time_t ts;
1740 	struct t_unitdata ud;
1741 	char *eomp, *eomp2, *from, *text, *msg;
1742 	char line[MAXLINE*2];
1743 	char head[MAXLINE+1];
1744 	char tmpbuf[MAXLINE+1];
1745 	char cbuf[30];
1746 	char *filtered;
1747 	char *msgid_start, *msgid_end;
1748 	pthread_t mythreadno;
1749 	size_t	hlen, filter_len;
1750 
1751 	if (Debug) {
1752 		mythreadno = pthread_self();
1753 	}
1754 
1755 	switch (selection) {
1756 	default:
1757 	case CURRENT:		/* print current message */
1758 		msg = f->f_current.msg;
1759 		from = f->f_current.host;
1760 		pri = f->f_current.pri;
1761 		flags = f->f_current.flags;
1762 		ts = f->f_current.time;
1763 		f->f_msgflag &= ~CURRENT_VALID;
1764 		break;
1765 	case SAVED:		/* print saved message */
1766 		msg = f->f_prevmsg.msg;
1767 		from = f->f_prevmsg.host;
1768 		pri = f->f_prevmsg.pri;
1769 		flags = f->f_prevmsg.flags;
1770 		ts = f->f_prevmsg.time;
1771 		f->f_msgflag &= ~OLD_VALID;
1772 		break;
1773 	}
1774 
1775 	if (msg[0] == '\0')
1776 		return;
1777 
1778 	cp = line;
1779 
1780 	if (flags & ADDDATE)
1781 		(void) strncpy(cp, ctime_r(&ts, cbuf) + 4, 15);
1782 	else
1783 		(void) strncpy(cp, msg, 15);
1784 
1785 	line[15] = '\0';
1786 	(void) strcat(cp, " ");
1787 	(void) strcat(cp, from);
1788 	(void) strcat(cp, " ");
1789 	text = cp + strlen(cp);
1790 
1791 	if (flags & ADDDATE)
1792 		(void) strcat(cp, msg);
1793 	else
1794 		(void) strcat(cp, msg+16);
1795 	DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text);
1796 
1797 	errno = 0;
1798 	t_errno = 0;
1799 	switch (f->f_type) {
1800 	case F_UNUSED:
1801 		DPRINT1(1, "writemsg(%u): UNUSED\n", mythreadno);
1802 		break;
1803 	case F_FORW:
1804 		DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n",
1805 		    mythreadno, msg, TypeNames[f->f_type],
1806 		    f->f_un.f_forw.f_hname);
1807 
1808 		hlen = snprintf(head, sizeof (head),
1809 		    "<%d>%.15s ", pri, cp);
1810 
1811 		DPRINT2(5, "writemsg(%u): head = \"%s\"\n", mythreadno, head);
1812 		DPRINT2(5, "writemsg(%u): hlen = %d\n", mythreadno, hlen);
1813 
1814 		l = strlen(text);
1815 		p = text;
1816 
1817 		DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text);
1818 		DPRINT2(5, "writemsg(%u): strlen(text) = %d\n", mythreadno, l);
1819 
1820 		(void) strncpy(tmpbuf, head, hlen);
1821 
1822 		while (l > 0) {
1823 			size_t	len;
1824 
1825 			len = copy_frwd(tmpbuf + hlen, sizeof (tmpbuf) - hlen,
1826 			    p, l);
1827 
1828 			DPRINT2(5, "writemsg(%u): tmpbuf = \"%s\"\n",
1829 			    mythreadno, tmpbuf);
1830 			DPRINT2(5, "writemsg(%u): len = %d\n", mythreadno,
1831 			    len);
1832 			DPRINT2(5, "writemsg(%u): strlen(tmpbuf) = %d\n",
1833 			    mythreadno, strlen(tmpbuf));
1834 
1835 			ud.opt.buf = NULL;
1836 			ud.opt.len = 0;
1837 			ud.udata.buf = tmpbuf;
1838 			ud.udata.len = len + hlen;
1839 			ud.addr.maxlen = f->f_un.f_forw.f_addr.maxlen;
1840 			ud.addr.buf = f->f_un.f_forw.f_addr.buf;
1841 			ud.addr.len = f->f_un.f_forw.f_addr.len;
1842 			if (t_sndudata(f->f_file, &ud) < 0) {
1843 				if ((hup_state & HUP_INPROGRESS) &&
1844 				    f->f_type == F_UNUSED) {
1845 					break;
1846 				}
1847 				(void) t_close(f->f_file);
1848 				f->f_type = F_UNUSED;
1849 				logerror("t_sndudata");
1850 
1851 				/*
1852 				 * Since it has already failed, it's not worth
1853 				 * continuing output from the middle of
1854 				 * message string.
1855 				 */
1856 				break;
1857 			}
1858 			p += len;
1859 			l -= len;
1860 		}
1861 		break;
1862 	case F_CONSOLE:
1863 	case F_TTY:
1864 	case F_FILE:
1865 	case F_USERS:
1866 	case F_WALL:
1867 		DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n",
1868 		    mythreadno, msg, TypeNames[f->f_type],
1869 		    ((f->f_type == F_USERS) || (f->f_type == F_WALL)) ?
1870 		    "" : f->f_un.f_fname);
1871 		/*
1872 		 * filter the string in preparation for writing it
1873 		 * save the original for possible forwarding.
1874 		 * In case every byte in cp is a control character,
1875 		 * allocates large enough buffer for filtered.
1876 		 */
1877 
1878 		filter_len = strlen(cp) * 4 + 1;
1879 		filtered = (char *)malloc(filter_len);
1880 		if (!filtered) {
1881 			MALLOC_FAIL("dropping message");
1882 			/* seems we can just return */
1883 			return;
1884 		}
1885 		DPRINT3(5, "writemsg(%u): "
1886 		    "filtered allocated (%p: %d bytes)\n",
1887 		    mythreadno, (void *)filtered, filter_len);
1888 		/* -3 : we may add "\r\n" to ecomp(filtered) later */
1889 		filter_string(cp, filtered, filter_len - 3);
1890 
1891 		DPRINT2(5, "writemsg(%u): strlen(filtered) = %d\n",
1892 		    mythreadno, strlen(filtered));
1893 		/*
1894 		 * If we're writing to the console, strip out the message ID
1895 		 * to reduce visual clutter.
1896 		 */
1897 		if ((msgid_start = strstr(filtered, "[ID ")) != NULL &&
1898 		    (msgid_end = strstr(msgid_start, "] ")) != NULL &&
1899 		    f->f_type == F_CONSOLE)
1900 			(void) strcpy(msgid_start, msgid_end + 2);
1901 
1902 		eomp = filtered + strlen(filtered);
1903 
1904 		if ((f->f_type == F_USERS) || (f->f_type == F_WALL)) {
1905 			/* CSTYLED */
1906 			(void) strcat(eomp, "\r\n"); /*lint !e669*/
1907 			/*
1908 			 * Since wallmsg messes with utmpx we need
1909 			 * to guarantee single threadedness...
1910 			 */
1911 			(void) pthread_mutex_lock(&wmp);
1912 			wallmsg(f, from, filtered);
1913 			(void) pthread_mutex_unlock(&wmp);
1914 
1915 			/*
1916 			 * The contents of filtered have been copied
1917 			 * out to the struct walldev. We should free it here.
1918 			 */
1919 
1920 			free(filtered);
1921 
1922 			/* exiting the switch */
1923 			break;
1924 		} else if (f->f_type != F_FILE) {
1925 			/* CSTYLED */
1926 			(void) strncpy(eomp, "\r\n", 3); /*lint !e669*/
1927 		} else {
1928 			if ((eomp2 = strchr(filtered, '\r')) != NULL) {
1929 				(void) strncpy(eomp2, "\n", 2);
1930 			} else {
1931 				/* CSTYLED */
1932 				(void) strncpy(eomp, "\n", 2); /*lint !e669*/
1933 			}
1934 		}
1935 		if (write(f->f_file, filtered, strlen(filtered)) < 0) {
1936 			int e = errno;
1937 
1938 			if ((hup_state & HUP_INPROGRESS) &&
1939 			    f->f_type == F_UNUSED) {
1940 				free(filtered);
1941 				break;
1942 			}
1943 			(void) close(f->f_file);
1944 			/*
1945 			 * Check for EBADF on TTY's due
1946 			 * to vhangup() XXX
1947 			 */
1948 			if (e == EBADF && f->f_type != F_FILE) {
1949 				f->f_file = open(f->f_un.f_fname,
1950 				    O_WRONLY|O_APPEND|O_NOCTTY);
1951 				if (f->f_file < 0) {
1952 					f->f_type = F_UNUSED;
1953 					logerror(f->f_un.f_fname);
1954 					f->f_stat.errs++;
1955 				}
1956 				untty();
1957 			} else {
1958 				f->f_type = F_UNUSED;
1959 				f->f_stat.errs++;
1960 				errno = e;
1961 				logerror(f->f_un.f_fname);
1962 			}
1963 		} else if (flags & SYNC_FILE)
1964 			if (((pri & LOG_FACMASK) >> 3) == LOG_KERN)
1965 				(void) fsync(f->f_file);
1966 
1967 		DPRINT2(5, "writemsg(%u): freeing filtered (%p)\n",
1968 		    mythreadno, (void *)filtered);
1969 
1970 		free(filtered);
1971 		break;
1972 	}
1973 }
1974 
1975 /*
1976  *  WALLMSG -- Write a message to the world at large
1977  *
1978  *	Write the specified message to either the entire
1979  *	world, or a list of approved users.
1980  */
1981 static void
1982 wallmsg(struct filed *f, char *from, char *msg)
1983 {
1984 	int i;
1985 	size_t	len, clen;
1986 	char *buf = NULL;
1987 	struct utmpx *utxp;
1988 	time_t now;
1989 	char line[512], dev[100];
1990 	char cp[MAXLINE+1];
1991 	struct stat statbuf;
1992 	walldev_t *w;
1993 	char cbuf[30];
1994 	pthread_t mythreadno;
1995 
1996 	if (Debug) {
1997 		mythreadno = pthread_self();
1998 	}
1999 
2000 	if (access(UTMPX_FILE, R_OK) != 0 || stat(UTMPX_FILE, &statbuf) != 0) {
2001 		logerror(UTMPX_FILE);
2002 		return;
2003 	} else if (statbuf.st_uid != 0 || (statbuf.st_mode & 07777) != 0644) {
2004 		(void) snprintf(line, sizeof (line), "%s %s", UTMPX_FILE,
2005 		    "not owned by root or not mode 644.\n"
2006 		    "This file must be owned by root "
2007 		    "and not writable by\n"
2008 		    "anyone other than root.  This alert is being "
2009 		    "dropped because of\n"
2010 		    "this problem.");
2011 		logerror(line);
2012 		return;
2013 	}
2014 
2015 	if (f->f_type == F_WALL) {
2016 		(void) time(&now);
2017 		len = snprintf(line, sizeof (line),
2018 		    "\r\n\7Message from syslogd@%s "
2019 		    "at %.24s ...\r\n", from, ctime_r(&now, cbuf));
2020 		len += strlen(msg + 16);
2021 		buf = (char *)malloc(len + 1);
2022 		if (!buf) {
2023 			MALLOC_FAIL("dropping message");
2024 			return;
2025 		}
2026 		DPRINT3(5, "wallmsg(%u): buf allocated (%p: %d bytes)\n",
2027 		    mythreadno, (void *)buf, len + 1);
2028 		(void) strcpy(buf, line);
2029 		(void) strcat(buf, msg + 16);
2030 		clen = copy_frwd(cp, sizeof (cp), buf, len);
2031 		DPRINT2(5, "wallmsg(%u): clen = %d\n",
2032 		    mythreadno, clen);
2033 		DPRINT2(5, "wallmsg(%u): freeing buf (%p)\n",
2034 		    mythreadno, (void *)buf);
2035 		free(buf);
2036 	} else {
2037 		clen = copy_frwd(cp, sizeof (cp), msg, strlen(msg));
2038 		DPRINT2(5, "wallmsg(%u): clen = %d\n",
2039 		    mythreadno, clen);
2040 	}
2041 	/* scan the user login file */
2042 	setutxent();
2043 	while ((utxp = getutxent()) != NULL) {
2044 		/* is this slot used? */
2045 		if (utxp->ut_name[0] == '\0' ||
2046 		    utxp->ut_line[0] == '\0' ||
2047 		    utxp->ut_type != USER_PROCESS)
2048 			continue;
2049 		/* should we send the message to this user? */
2050 		if (f->f_type == F_USERS) {
2051 			for (i = 0; i < MAXUNAMES; i++) {
2052 				if (!f->f_un.f_uname[i][0]) {
2053 					i = MAXUNAMES;
2054 					break;
2055 				}
2056 				if (strncmp(f->f_un.f_uname[i],
2057 				    utxp->ut_name, UNAMESZ) == 0)
2058 					break;
2059 			}
2060 			if (i >= MAXUNAMES)
2061 				continue;
2062 		}
2063 
2064 		/* compute the device name */
2065 		if (utxp->ut_line[0] == '/') {
2066 			(void) strncpy(dev, utxp->ut_line, UDEVSZ);
2067 		} else {
2068 			(void) strcpy(dev, "/dev/");
2069 			(void) strncat(dev, utxp->ut_line, UDEVSZ);
2070 		}
2071 		DPRINT2(1, "wallmsg(%u): write to '%s'\n", mythreadno,
2072 		    dev);
2073 
2074 		if ((w = malloc(sizeof (walldev_t))) != NULL) {
2075 			int rc;
2076 			(void) pthread_attr_init(&w->thread_attr);
2077 			(void) pthread_attr_setdetachstate(&w->thread_attr,
2078 			    PTHREAD_CREATE_DETACHED);
2079 			(void) strncpy(w->dev, dev, PATH_MAX);
2080 			(void) strncpy(w->msg, cp, MAXLINE+1);
2081 			(void) strncpy(w->ut_name, utxp->ut_name,
2082 			    sizeof (w->ut_name));
2083 
2084 			if ((rc = pthread_create(&w->thread, &w->thread_attr,
2085 			    writetodev, (void *) w)) != 0) {
2086 				DPRINT2(5, "wallmsg(%u): wallmsg thread "
2087 				    "create failed rc = %d\n",
2088 				    mythreadno, rc);
2089 				free(w);
2090 				break;
2091 			}
2092 		} else {
2093 			MALLOC_FAIL("dropping message to user");
2094 		}
2095 	}
2096 	/* close the user login file */
2097 	endutxent();
2098 }
2099 
2100 /*
2101  * Each time we need to write to a tty device (a potentially expensive
2102  * or long-running operation) this routine gets called as a new
2103  * detached, unbound thread. This allows writes to many devices
2104  * to proceed nearly in parallel, without having to resort to
2105  * asynchronous I/O or forking.
2106  */
2107 static void *
2108 writetodev(void *ap)
2109 {
2110 	walldev_t *w = ap;
2111 	int ttyf;
2112 	int len;
2113 	struct stat statb;
2114 	struct passwd pw, *pwp;
2115 	char pwbuf[MAXLINE];
2116 	pthread_t mythreadno;
2117 
2118 	if (Debug) {
2119 		mythreadno = pthread_self();
2120 	}
2121 
2122 	DPRINT1(1, "writetodev(%u): Device writer thread started\n",
2123 	    mythreadno);
2124 
2125 	len = strlen(w->msg);
2126 
2127 	ttyf = open(w->dev, O_WRONLY|O_NOCTTY|O_NDELAY);
2128 	if (ttyf >= 0) {
2129 		if (fstat(ttyf, &statb) != 0) {
2130 			DPRINT2(1, "writetodev(%u): Can't stat '%s'\n",
2131 			    mythreadno, w->dev);
2132 			errno = 0;
2133 			logerror("Can't stat '%s'", w->dev);
2134 		} else if (!(statb.st_mode & S_IWRITE)) {
2135 			DPRINT2(1, "writetodev(%u): Can't write to "
2136 			    "'%s'\n", mythreadno, w->dev);
2137 		} else if (!isatty(ttyf)) {
2138 			DPRINT2(1, "writetodev(%u): '%s' not a tty\n",
2139 			    mythreadno, w->dev);
2140 			/*
2141 			 * We might hit dtremote here. Don't generate
2142 			 * error message.
2143 			 */
2144 		} else if (getpwuid_r(statb.st_uid, &pw, pwbuf,
2145 		    sizeof (pwbuf), &pwp) != 0) {
2146 			DPRINT2(1, "writetodev(%u): Can't determine owner "
2147 			    "of '%s'\n", mythreadno, w->dev);
2148 			errno = 0;
2149 			logerror("Can't determine owner of '%s'", w->dev);
2150 		} else if (strncmp(pw.pw_name, w->ut_name, UNAMESZ) != 0) {
2151 			DPRINT2(1, "writetodev(%u): Bad terminal owner '%s'"
2152 			    "\n", mythreadno, w->dev);
2153 			errno = 0;
2154 			logerror("%s %s owns '%s' %s %.*s",
2155 			    "Bad terminal owner;", pw.pw_name, w->dev,
2156 			    "but utmpx says", UNAMESZ, w->ut_name);
2157 		} else if (write(ttyf, w->msg, len) != len) {
2158 			DPRINT2(1, "writetodev(%u): Write failed to "
2159 			    "'%s'\n", mythreadno, w->dev);
2160 			errno = 0;
2161 			logerror("Write failed to '%s'", w->dev);
2162 		}
2163 
2164 		DPRINT2(1, "writetodev(%u): write to '%s' succeeded\n",
2165 		    mythreadno, w->dev);
2166 
2167 		(void) close(ttyf);
2168 	} else {
2169 		DPRINT2(1, "writetodev(%u): Can't open '%s'\n",
2170 		    mythreadno, w->dev);
2171 	}
2172 
2173 	(void) pthread_attr_destroy(&w->thread_attr);
2174 	free(w);
2175 
2176 	DPRINT1(1, "writetodev(%u): Device writer thread exiting\n",
2177 	    mythreadno);
2178 
2179 	pthread_exit(0);
2180 	return (NULL);
2181 	/*NOTREACHED*/
2182 }
2183 
2184 /*
2185  * Return a printable representation of a host address. If unable to
2186  * look up hostname, format the numeric address for display instead.
2187  *
2188  * First calls hnc_lookup to see if there is valid cache entry for
2189  * given network address. If it failed, cvthname looks up hostname,
2190  * and push the results into the hostname cache.
2191  */
2192 static host_list_t *
2193 cvthname(struct netbuf *nbp, struct netconfig *ncp, char *failsafe_addr)
2194 {
2195 	int i;
2196 	host_list_t *h;
2197 	struct nd_hostservlist *hsp;
2198 	struct nd_hostserv *hspp;
2199 	pthread_t mythreadno;
2200 	int hindex;
2201 	char *uap;
2202 
2203 	if (Debug) {
2204 		mythreadno = pthread_self();
2205 	}
2206 
2207 	if (Debug)
2208 		uap = taddr2uaddr(ncp, nbp);
2209 
2210 	DPRINT2(2, "cvthname(%u): looking up hostname for %s\n",
2211 	    mythreadno, uap ? uap : "<unknown>");
2212 
2213 	if ((h = hnc_lookup(nbp, ncp, &hindex)) != NULL) {
2214 		DPRINT4(2, "cvthname(%u): Cache found %p for %s (%s)\n",
2215 		    mythreadno, (void *)h, uap ? uap : "<unknown>",
2216 		    h->hl_hosts[0]);
2217 		return (h);
2218 	}
2219 	DPRINT2(2, "cvthname(%u): No cache found for %s\n",
2220 	    mythreadno, uap ? uap : "<unknown>");
2221 
2222 	if (Debug)
2223 		free(uap);
2224 
2225 	if (ncp->nc_semantics != NC_TPI_CLTS) {
2226 		return (NULL);
2227 	}
2228 
2229 	/* memory allocation failure here is fatal */
2230 	if ((h = malloc(sizeof (host_list_t))) == NULL) {
2231 		MALLOC_FAIL("host name conversion");
2232 		return (NULL);
2233 	}
2234 
2235 	if (netdir_getbyaddr(ncp, &hsp, nbp) == 0) {
2236 		if (hsp->h_cnt <= 0) {
2237 out:			netdir_free((void *)hsp, ND_HOSTSERVLIST);
2238 			free(h);
2239 			return (NULL);
2240 		}
2241 
2242 		hspp = hsp->h_hostservs;
2243 		h->hl_cnt = hsp->h_cnt;
2244 		h->hl_hosts = (char **)malloc(sizeof (char *) * (h->hl_cnt));
2245 		if (h->hl_hosts == NULL) {
2246 			MALLOC_FAIL("host name conversion");
2247 			goto out;
2248 		}
2249 
2250 		DPRINT2(2, "cvthname(%u): Found %d hostnames\n",
2251 		    mythreadno, h->hl_cnt);
2252 		for (i = 0; i < h->hl_cnt; i++) {
2253 			h->hl_hosts[i] = (char *)
2254 			    malloc(sizeof (char) * (strlen(hspp->h_host) + 1));
2255 			if (h->hl_hosts[i] == NULL) {
2256 				int j;
2257 				for (j = 0; j < i; j++) {
2258 					free(h->hl_hosts[j]);
2259 				}
2260 				free(h->hl_hosts);
2261 				MALLOC_FAIL("host name conversion");
2262 				goto out;
2263 			}
2264 			(void) strcpy(h->hl_hosts[i], hspp->h_host);
2265 			hspp++;
2266 		}
2267 		netdir_free((void *)hsp, ND_HOSTSERVLIST);
2268 	} else { /* unknown address */
2269 		h->hl_cnt = 1;
2270 		h->hl_hosts = (char **)malloc(sizeof (char *));
2271 		if (h->hl_hosts == NULL) {
2272 			free(h);
2273 			MALLOC_FAIL("host name conversion");
2274 			return (NULL);
2275 		}
2276 		h->hl_hosts[0] = (char *)malloc(strlen(failsafe_addr) + 3);
2277 		if (h->hl_hosts[0] == NULL) {
2278 			free(h->hl_hosts);
2279 			free(h);
2280 			MALLOC_FAIL("host name conversion");
2281 			return (NULL);
2282 		}
2283 		/*LINTED*/
2284 		(void) sprintf(h->hl_hosts[0], "[%s]", failsafe_addr);
2285 		DPRINT2(1, "cvthname(%u): Hostname lookup failed "
2286 		    "- using address %s instead\n",
2287 		    mythreadno, h->hl_hosts[0]);
2288 	}
2289 
2290 	h->hl_refcnt = 1;
2291 	if (pthread_mutex_init(&h->hl_mutex, NULL) != 0) {
2292 		logerror("pthread_mutex_init failed");
2293 		/* This host_list won't be shared by the cache. */
2294 		return (h);
2295 	}
2296 	hnc_register(nbp, ncp, h, hindex);
2297 	DPRINT3(2, "cvthname(%u): returning %p for %s\n",
2298 	    mythreadno, (void *)h, h->hl_hosts[0]);
2299 	return (h);
2300 }
2301 
2302 /*
2303  * Print syslogd errors some place. Need to be careful here, because
2304  * this routine is called at times when we're not initialized and
2305  * ready to log messages...in this case, fall back to using the console.
2306  */
2307 void
2308 logerror(const char *type, ...)
2309 {
2310 	char buf[MAXLINE+1];
2311 	pthread_t mythreadno;
2312 	int flag;
2313 	va_list ap;
2314 
2315 	if (Debug) {
2316 		mythreadno = pthread_self();
2317 	}
2318 
2319 	va_start(ap, type);
2320 	logerror_format(type, buf, ap);
2321 	va_end(ap);
2322 	DPRINT2(1, "logerror(%u): %s\n", mythreadno, buf);
2323 
2324 	(void) pthread_mutex_lock(&logerror_lock);
2325 	if (!interrorlog) {
2326 		flag = 0;
2327 		if (logerror_to_console(1, buf) == 0) {
2328 			/* has written to the console */
2329 			flag = IGN_CONS;
2330 		}
2331 		(void) logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE|flag, 1);
2332 	} else {
2333 		if (logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE, 0) == -1) {
2334 			(void) logerror_to_console(1, buf);
2335 		}
2336 	}
2337 	(void) pthread_mutex_unlock(&logerror_lock);
2338 
2339 	errno = 0;
2340 	t_errno = 0;
2341 }
2342 
2343 static void
2344 logerror_format(const char *type, char *buf, va_list ap)
2345 {
2346 	char tmpbuf[MAXLINE + 1];
2347 	pthread_t mythreadno;
2348 
2349 	if (Debug) {
2350 		mythreadno = pthread_self();
2351 	}
2352 
2353 	(void) vsnprintf(tmpbuf, MAXLINE, type, ap);
2354 
2355 	if (t_errno == 0 || t_errno == TSYSERR) {
2356 		char *errstr;
2357 
2358 		if (errno == 0) {
2359 			(void) snprintf(buf, MAXLINE, "syslogd: %.*s",
2360 			    MAXLINE, tmpbuf);
2361 		} else if ((errstr = strerror(errno)) == (char *)NULL) {
2362 			(void) snprintf(buf, MAXLINE, "syslogd: %s: error"
2363 			    " %d", tmpbuf, errno);
2364 		} else {
2365 			(void) snprintf(buf, MAXLINE, "syslogd: %s: %s",
2366 			    tmpbuf, errstr);
2367 		}
2368 	} else {
2369 		if (t_errno > t_nerr) {
2370 			(void) snprintf(buf, MAXLINE, "syslogd: %s:"
2371 			    " t_error %d", tmpbuf, t_errno);
2372 		} else {
2373 			(void) snprintf(buf, MAXLINE, "syslogd: %s: %s",
2374 			    tmpbuf, t_errlist[t_errno]);
2375 		}
2376 	}
2377 
2378 	DPRINT2(5, "logerror_format(%u): out %s\n", mythreadno, buf);
2379 }
2380 
2381 static int
2382 logerror_to_console(int nonblock, const char *buf)
2383 {
2384 	int cfd, modes;
2385 	pthread_t mythreadno;
2386 	int ret = 0, len;
2387 	char tmpbuf[MAXLINE + 1];
2388 
2389 	if (Debug) {
2390 		mythreadno = pthread_self();
2391 	}
2392 
2393 	DPRINT2(1, "logerror_to_console(%u): %s\n", mythreadno, buf);
2394 
2395 	/*
2396 	 * must use open here instead of fopen, because
2397 	 * we need the O_NOCTTY behavior - otherwise we
2398 	 * could hang the console at boot time
2399 	 */
2400 
2401 	modes = (nonblock) ?
2402 	    O_WRONLY|O_APPEND|O_NOCTTY|O_NONBLOCK :
2403 	    O_WRONLY|O_APPEND|O_NOCTTY;
2404 
2405 	if (((cfd = open(sysmsg, modes)) >= 0) ||
2406 	    ((cfd = open(ctty, modes)) >= 0)) {
2407 		(void) snprintf(tmpbuf, MAXLINE, "%s\n", buf);
2408 		len = strlen(tmpbuf);
2409 		if (write(cfd, tmpbuf, len) != len) {
2410 			ret = 1;
2411 		}
2412 		(void) close(cfd);
2413 	} else {
2414 		ret = 1;
2415 
2416 		/* punt */
2417 		DPRINT1(1, "logerror_console(%u): can't open console\n",
2418 		    mythreadno);
2419 	}
2420 	return (ret);
2421 }
2422 
2423 /*
2424  * copy current message to saved message in filed structure.
2425  */
2426 static void
2427 copy_msg(struct filed *f)
2428 {
2429 	(void) strlcpy(f->f_prevmsg.msg, f->f_current.msg, MAXLINE+1);
2430 	(void) strlcpy(f->f_prevmsg.host, f->f_current.host, SYS_NMLN);
2431 	f->f_prevmsg.pri = f->f_current.pri;
2432 	f->f_prevmsg.flags = f->f_current.flags;
2433 	f->f_prevmsg.time = f->f_current.time;
2434 	f->f_msgflag |= OLD_VALID;
2435 }
2436 
2437 
2438 /*
2439  * function to free a host_list_t struct that was allocated
2440  * out of cvthname(). There is a special case where we don't
2441  * free the hostname list in LocalHostName, because that's
2442  * our own addresses, and we just want to have to look it
2443  * up once and save it.  Also don't free it if it's
2444  * NullHostName, because that's a special one we use if
2445  * name service lookup fails.
2446  *
2447  * By having hostname cache, now host_list_t will be shared
2448  * by messages and hostname cache. hl_refcnt is used for
2449  * the purpose.
2450  */
2451 static void
2452 freehl(host_list_t *h)
2453 {
2454 	int i, refcnt;
2455 	pthread_t mythreadno;
2456 
2457 	if (Debug) {
2458 		mythreadno = pthread_self();
2459 	}
2460 
2461 	DPRINT2(2, "freehl(%u): releasing %p\n", mythreadno, (void *)h);
2462 
2463 	if (h == NULL || h == &LocalHostName || h == &NullHostName) {
2464 		return;
2465 	}
2466 
2467 	(void) pthread_mutex_lock(&h->hl_mutex);
2468 	refcnt = --h->hl_refcnt;
2469 	(void) pthread_mutex_unlock(&h->hl_mutex);
2470 
2471 	if (refcnt != 0) {
2472 		DPRINT3(5, "freehl(%u): %p has reference %d\n",
2473 		    mythreadno, (void *)h, refcnt);
2474 		return;
2475 	}
2476 
2477 	(void) pthread_mutex_destroy(&h->hl_mutex);
2478 
2479 	DPRINT2(5, "freehl(%u): freeing %p\n", mythreadno, (void *)h);
2480 
2481 	for (i = 0; i < h->hl_cnt; i++) {
2482 		free(h->hl_hosts[i]);
2483 	}
2484 
2485 	free(h->hl_hosts);
2486 	free(h);
2487 }
2488 
2489 /*
2490  * Create the door file and the pid file in /var/run.  If the filesystem
2491  * containing /etc is writable, create symlinks /etc/.syslog_door and
2492  * /etc/syslog.pid to them.  On systems that do not support /var/run, create
2493  * /etc/.syslog_door and /etc/syslog.pid directly.
2494  *
2495  * Note: it is not considered fatal to fail to create the pid file or its
2496  * symlink.  Attempts to use them in the usual way will fail, of course, but
2497  * syslogd will function nicely without it (not so for the door file).
2498  */
2499 
2500 static void
2501 open_door(void)
2502 {
2503 	struct stat buf;
2504 	door_info_t info;
2505 	char line[MAXLINE+1];
2506 	pthread_t mythreadno;
2507 	int err;
2508 
2509 	if (Debug) {
2510 		mythreadno = pthread_self();
2511 	}
2512 
2513 	/*
2514 	 * first see if another syslogd is running by trying
2515 	 * a door call - if it succeeds, there is already
2516 	 * a syslogd process active
2517 	 */
2518 
2519 	if (!DoorCreated) {
2520 		int door;
2521 
2522 		if ((door = open(DoorFileName, O_RDONLY)) >= 0) {
2523 			DPRINT2(5, "open_door(%u): %s opened "
2524 			    "successfully\n", mythreadno, DoorFileName);
2525 
2526 			if (door_info(door, &info) >= 0) {
2527 				DPRINT2(5, "open_door(%u): "
2528 				    "door_info:info.di_target = %ld\n",
2529 				    mythreadno, info.di_target);
2530 
2531 				if (info.di_target > 0) {
2532 					(void) sprintf(line, "syslogd pid %ld"
2533 					    " already running. Cannot "
2534 					    "start another syslogd pid %ld",
2535 					    info.di_target, getpid());
2536 					DPRINT2(5, "open_door(%u): error: "
2537 					    "%s\n", mythreadno, line);
2538 					errno = 0;
2539 					logerror(line);
2540 					exit(1);
2541 				}
2542 			}
2543 
2544 			(void) close(door);
2545 		} else {
2546 			if (lstat(DoorFileName, &buf) < 0) {
2547 				err = errno;
2548 
2549 				DPRINT3(5, "open_door(%u): lstat() of %s "
2550 				    "failed, errno=%d\n",
2551 				    mythreadno, DoorFileName, err);
2552 
2553 				if ((door = creat(DoorFileName, 0644)) < 0) {
2554 					err = errno;
2555 					(void) snprintf(line, sizeof (line),
2556 					    "creat() of %s failed - fatal",
2557 					    DoorFileName);
2558 					DPRINT3(1, "open_door(%u): error: %s, "
2559 					    "errno=%d\n", mythreadno, line,
2560 					    err);
2561 					errno = err;
2562 					logerror(line);
2563 					delete_doorfiles();
2564 					exit(1);
2565 				}
2566 
2567 				(void) fchmod(door,
2568 				    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
2569 
2570 				DPRINT2(5, "open_door(%u): creat() of %s "
2571 				    "succeeded\n", mythreadno,
2572 				    DoorFileName);
2573 
2574 				(void) close(door);
2575 			}
2576 		}
2577 
2578 		if (strcmp(DoorFileName, DOORFILE) == 0) {
2579 			if (lstat(OLD_DOORFILE, &buf) == 0) {
2580 				DPRINT2(5, "open_door(%u): lstat() of %s "
2581 				    "succeeded\n", mythreadno,
2582 				    OLD_DOORFILE);
2583 
2584 				if (S_ISDIR(buf.st_mode)) {
2585 					(void) snprintf(line, sizeof (line),
2586 					    "%s is a directory - fatal",
2587 					    OLD_DOORFILE);
2588 					DPRINT2(1, "open_door(%u): error: "
2589 					    "%s\n", mythreadno, line);
2590 					errno = 0;
2591 					logerror(line);
2592 					delete_doorfiles();
2593 					exit(1);
2594 				}
2595 
2596 				DPRINT2(5, "open_door(%u): %s is not a "
2597 				    "directory\n",
2598 				    mythreadno, OLD_DOORFILE);
2599 
2600 				if (unlink(OLD_DOORFILE) < 0) {
2601 					err = errno;
2602 					(void) snprintf(line, sizeof (line),
2603 					    "unlink() of %s failed",
2604 					    OLD_DOORFILE);
2605 					DPRINT2(5, "open_door(%u): %s\n",
2606 					    mythreadno, line);
2607 
2608 					if (err != EROFS) {
2609 						DPRINT3(1, "open_door(%u): "
2610 						    "error: %s, "
2611 						    "errno=%d\n",
2612 						    mythreadno, line, err);
2613 						(void) strcat(line, " - fatal");
2614 						errno = err;
2615 						logerror(line);
2616 						delete_doorfiles();
2617 						exit(1);
2618 					}
2619 
2620 					DPRINT1(5, "open_door(%u): unlink "
2621 					    "failure OK on RO file "
2622 					    "system\n", mythreadno);
2623 				}
2624 			} else {
2625 				DPRINT2(5, "open_door(%u): file %s doesn't "
2626 				    "exist\n", mythreadno, OLD_DOORFILE);
2627 			}
2628 
2629 			if (symlink(RELATIVE_DOORFILE, OLD_DOORFILE) < 0) {
2630 				err = errno;
2631 				(void) snprintf(line, sizeof (line),
2632 				    "symlink %s -> %s failed", OLD_DOORFILE,
2633 				    RELATIVE_DOORFILE);
2634 				DPRINT2(5, "open_door(%u): %s\n", mythreadno,
2635 				    line);
2636 
2637 				if (err != EROFS) {
2638 					DPRINT3(1, "open_door(%u): error: %s, "
2639 					    "errno=%d\n", mythreadno, line,
2640 					    err);
2641 					errno = err;
2642 					(void) strcat(line, " - fatal");
2643 					logerror(line);
2644 					delete_doorfiles();
2645 					exit(1);
2646 				}
2647 
2648 				DPRINT1(5, "open_door(%u): symlink failure OK "
2649 				    "on RO file system\n", mythreadno);
2650 			} else {
2651 				DPRINT3(5, "open_door(%u): symlink %s -> %s "
2652 				    "succeeded\n", mythreadno,
2653 				    OLD_DOORFILE, RELATIVE_DOORFILE);
2654 			}
2655 		}
2656 
2657 		if ((DoorFd = door_create(server, 0,
2658 		    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
2659 			err = errno;
2660 			(void) sprintf(line, "door_create() failed - fatal");
2661 			DPRINT3(1, "open_door(%u): error: %s, errno=%d\n",
2662 			    mythreadno, line, err);
2663 			errno = err;
2664 			logerror(line);
2665 			delete_doorfiles();
2666 			exit(1);
2667 		}
2668 		(void) door_setparam(DoorFd, DOOR_PARAM_DATA_MAX, 0);
2669 		DPRINT2(5, "open_door(%u): door_create() succeeded, "
2670 		    "DoorFd=%d\n", mythreadno, DoorFd);
2671 
2672 		DoorCreated = 1;
2673 	}
2674 
2675 	(void) fdetach(DoorFileName);	/* just in case... */
2676 
2677 	(void) door_server_create(door_server_pool);
2678 
2679 	if (fattach(DoorFd, DoorFileName) < 0) {
2680 		err = errno;
2681 		(void) snprintf(line, sizeof (line), "fattach() of fd"
2682 		    " %d to %s failed - fatal", DoorFd, DoorFileName);
2683 		DPRINT3(1, "open_door(%u): error: %s, errno=%d\n", mythreadno,
2684 		    line, err);
2685 		errno = err;
2686 		logerror(line);
2687 		delete_doorfiles();
2688 		exit(1);
2689 	}
2690 
2691 	DPRINT2(5, "open_door(%u): attached server() to %s\n", mythreadno,
2692 	    DoorFileName);
2693 
2694 	/*
2695 	 * create pidfile anyway, so those using it to control
2696 	 * syslogd (with kill `cat /etc/syslog.pid` perhaps)
2697 	 * don't get broken.
2698 	 */
2699 
2700 	if (!PidfileCreated) {
2701 		int pidfd;
2702 
2703 		PidfileCreated = 1;
2704 
2705 		if ((pidfd = open(PidFileName, O_RDWR|O_CREAT|O_TRUNC, 0644))
2706 		    < 0) {
2707 			err = errno;
2708 			(void) snprintf(line, sizeof (line),
2709 			    "open() of %s failed", PidFileName);
2710 			DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n",
2711 			    mythreadno, line, err);
2712 			errno = err;
2713 			logerror(line);
2714 			return;
2715 		}
2716 
2717 		(void) fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
2718 		(void) sprintf(line, "%ld\n", getpid());
2719 
2720 		if (write(pidfd, line, strlen(line)) < 0) {
2721 			err = errno;
2722 			(void) snprintf(line, sizeof (line),
2723 			    "write to %s on fd %d failed", PidFileName, pidfd);
2724 			DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n",
2725 			    mythreadno, line, err);
2726 			errno = err;
2727 			logerror(line);
2728 			return;
2729 		}
2730 
2731 		(void) close(pidfd);
2732 
2733 		DPRINT2(5, "open_door(%u): %s created\n",
2734 		    mythreadno, PidFileName);
2735 
2736 		if (strcmp(PidFileName, PIDFILE) == 0) {
2737 			if (lstat(OLD_PIDFILE, &buf) == 0) {
2738 				DPRINT2(5, "open_door(%u): lstat() of %s "
2739 				    "succeded\n", mythreadno, OLD_PIDFILE);
2740 
2741 				if (S_ISDIR(buf.st_mode)) {
2742 					(void) snprintf(line, sizeof (line),
2743 					    "file %s is a directory",
2744 					    OLD_PIDFILE);
2745 					DPRINT2(1, "open_door(%u): warning: "
2746 					    "%s\n", mythreadno, line);
2747 					errno = 0;
2748 					logerror(line);
2749 					return;
2750 				}
2751 
2752 				if (unlink(OLD_PIDFILE) < 0) {
2753 					err = errno;
2754 					(void) snprintf(line, sizeof (line),
2755 					    "unlink() of %s failed",
2756 					    OLD_PIDFILE);
2757 					DPRINT2(5, "open_door(%u): %s\n",
2758 					    mythreadno, line);
2759 
2760 					if (err != EROFS) {
2761 						DPRINT3(1, "open_door (%u): "
2762 						    "warning: %s, "
2763 						    "errno=%d\n",
2764 						    mythreadno, line, err);
2765 						errno = err;
2766 						logerror(line);
2767 						return;
2768 					}
2769 
2770 					DPRINT1(5, "open_door(%u): unlink "
2771 					    "failure OK on RO file "
2772 					    "system\n", mythreadno);
2773 				}
2774 			} else {
2775 				DPRINT2(5, "open_door(%u): file %s doesn't "
2776 				    "exist\n", mythreadno, OLD_PIDFILE);
2777 			}
2778 
2779 			if (symlink(RELATIVE_PIDFILE, OLD_PIDFILE) < 0) {
2780 				err = errno;
2781 				(void) snprintf(line, sizeof (line),
2782 				    "symlink %s -> %s failed", OLD_PIDFILE,
2783 				    RELATIVE_PIDFILE);
2784 				DPRINT2(5, "open_door(%u): %s\n", mythreadno,
2785 				    line);
2786 
2787 				if (err != EROFS) {
2788 					DPRINT3(1, "open_door(%u): warning: "
2789 					    "%s, errno=%d\n", mythreadno,
2790 					    line, err);
2791 					errno = err;
2792 					logerror(line);
2793 					return;
2794 				}
2795 
2796 				DPRINT1(5, "open_door(%u): symlink failure OK "
2797 				    "on RO file system\n", mythreadno);
2798 				return;
2799 			}
2800 
2801 			DPRINT3(5, "open_door(%u): symlink %s -> %s "
2802 			    "succeeded\n", mythreadno, OLD_PIDFILE,
2803 			    RELATIVE_PIDFILE);
2804 		}
2805 	}
2806 }
2807 
2808 /*
2809  * the 'server' function that we export via the door. It does
2810  * nothing but return.
2811  */
2812 /*ARGSUSED*/
2813 static void
2814 server(void *cookie, char *argp, size_t arg_size,
2815     door_desc_t *dp, uint_t n)
2816 {
2817 	(void) door_return(NULL, 0, NULL, 0);
2818 	/* NOTREACHED */
2819 }
2820 
2821 /*ARGSUSED*/
2822 static void *
2823 create_door_thr(void *arg)
2824 {
2825 	(void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
2826 	(void) door_return(NULL, 0, NULL, 0);
2827 
2828 	/*
2829 	 * If there is an error in door_return(), it will return here and
2830 	 * the thread will exit. Hence we need to decrement door_server_cnt.
2831 	 */
2832 	(void) pthread_mutex_lock(&door_server_cnt_lock);
2833 	door_server_cnt--;
2834 	(void) pthread_mutex_unlock(&door_server_cnt_lock);
2835 	return (NULL);
2836 }
2837 
2838 /*
2839  * Max number of door server threads for syslogd. Since door is used
2840  * to check the health of syslogd, we don't need large number of
2841  * server threads.
2842  */
2843 #define	MAX_DOOR_SERVER_THR	3
2844 
2845 /*
2846  * Manage door server thread pool.
2847  */
2848 /*ARGSUSED*/
2849 static void
2850 door_server_pool(door_info_t *dip)
2851 {
2852 	(void) pthread_mutex_lock(&door_server_cnt_lock);
2853 	if (door_server_cnt <= MAX_DOOR_SERVER_THR &&
2854 	    pthread_create(NULL, &door_thr_attr, create_door_thr, NULL) == 0) {
2855 		door_server_cnt++;
2856 		(void) pthread_mutex_unlock(&door_server_cnt_lock);
2857 		return;
2858 	}
2859 
2860 	(void) pthread_mutex_unlock(&door_server_cnt_lock);
2861 }
2862 
2863 /*
2864  * checkm4 - used to verify that the external utilities that
2865  * syslogd depends on are where we expect them to be.
2866  * Returns 0 if all utilities are found, > 0 if any are missing.
2867  * Also logs errors so user knows what's missing
2868  */
2869 static int
2870 checkm4(void)
2871 {
2872 	int notfound = 0;
2873 	int saverrno;
2874 	pthread_t mythreadno;
2875 
2876 	if (Debug) {
2877 		mythreadno = pthread_self();
2878 	}
2879 
2880 	if (access("/usr/ccs/bin/m4", X_OK) < 0) {
2881 		saverrno = errno;
2882 		logerror("/usr/ccs/bin/m4");
2883 		DPRINT2(1, "checkm4(%u): /usr/ccs/bin/m4 - access "
2884 		    "returned %d\n", mythreadno, saverrno);
2885 		notfound++;
2886 	}
2887 
2888 	return (notfound);
2889 }
2890 
2891 /*
2892  *  INIT -- Initialize syslogd from configuration table, start up
2893  *  input and logger threads. This routine is called only once.
2894  */
2895 static void
2896 init(void)
2897 {
2898 	struct utsname *up;
2899 	pthread_attr_t sys_attr, net_attr, log_attr, hnl_attr;
2900 	int nthread;
2901 	pthread_t mythreadno;
2902 
2903 	if (Debug) {
2904 		mythreadno = pthread_self();
2905 	}
2906 
2907 	DPRINT1(2, "init(%u): initializing\n", mythreadno);
2908 
2909 	/* hand-craft a host_list_t entry for our local host name */
2910 	if ((up = malloc(sizeof (struct utsname))) == NULL) {
2911 		MALLOC_FAIL_EXIT;
2912 	}
2913 	(void) uname(up);
2914 	LocalHostName.hl_cnt = 1;
2915 	if ((LocalHostName.hl_hosts = malloc(sizeof (char *))) == NULL) {
2916 		MALLOC_FAIL_EXIT;
2917 	}
2918 	if ((LocalHostName.hl_hosts[0] = strdup(up->nodename)) == NULL) {
2919 		free(LocalHostName.hl_hosts);
2920 		MALLOC_FAIL_EXIT;
2921 	}
2922 	free(up);
2923 	/* also hand craft one for use if name resolution fails */
2924 	NullHostName.hl_cnt = 1;
2925 	if ((NullHostName.hl_hosts = malloc(sizeof (char *))) == NULL) {
2926 		MALLOC_FAIL_EXIT;
2927 	}
2928 	if ((NullHostName.hl_hosts[0] = strdup("name lookup failed")) == NULL) {
2929 		MALLOC_FAIL_EXIT;
2930 	}
2931 
2932 	hnc_init(0);
2933 
2934 	/*
2935 	 * Note that getnets will allocate network resources, but won't be
2936 	 * binding UDP port. This is because, there could be a race
2937 	 * condition between door. If we bind here, one syslogd could grab
2938 	 * UDP port first, but later another syslogd could take over without
2939 	 * getting UDP port but grab the door file. The 2nd syslogd could
2940 	 * continue to run without listening network.
2941 	 * bindnet() will be called after door was successfully opened.
2942 	 */
2943 	getnets();
2944 
2945 	/*
2946 	 * Start up configured theads
2947 	 */
2948 	conf_init();
2949 
2950 	/*
2951 	 * allocate thread stacks for the persistant threads
2952 	 */
2953 	nthread = (turnoff == 0) ? 4 : 2;
2954 
2955 	if ((stack_ptr = alloc_stacks(nthread)) == NULL) {
2956 		logerror("alloc_stacks failed - fatal");
2957 		exit(1);
2958 	}
2959 
2960 	if (Debug) {
2961 		dumpstats(STDOUT_FILENO);
2962 	}
2963 
2964 	(void) dataq_init(&inputq);	/* init the input queue */
2965 
2966 	if (pthread_attr_init(&sys_attr) != 0 ||
2967 	    pthread_attr_init(&log_attr) != 0 ||
2968 	    pthread_attr_init(&net_attr) != 0 ||
2969 	    pthread_attr_init(&hnl_attr) != 0 ||
2970 	    pthread_attr_init(&door_thr_attr) != 0) {
2971 		logerror("pthread_attr_init failed - fatal");
2972 		exit(1);
2973 	}
2974 
2975 	(void) pthread_attr_setscope(&sys_attr, PTHREAD_SCOPE_PROCESS);
2976 	(void) pthread_attr_setscope(&log_attr, PTHREAD_SCOPE_PROCESS);
2977 	(void) pthread_attr_setscope(&net_attr, PTHREAD_SCOPE_PROCESS);
2978 	(void) pthread_attr_setscope(&hnl_attr, PTHREAD_SCOPE_PROCESS);
2979 	(void) pthread_attr_setscope(&door_thr_attr, PTHREAD_SCOPE_SYSTEM);
2980 	(void) pthread_attr_setdetachstate(&door_thr_attr,
2981 	    PTHREAD_CREATE_DETACHED);
2982 
2983 	/* 1: logmsg thread */
2984 	(void) pthread_attr_setstacksize(&log_attr, stacksize);
2985 	(void) pthread_attr_setstackaddr(&log_attr, stack_ptr);
2986 	stack_ptr += stacksize + redzonesize;
2987 	if (pthread_create(&log_thread, &log_attr, logmsg, NULL) != 0) {
2988 		logerror("pthread_create failed - fatal");
2989 		exit(1);
2990 	}
2991 
2992 	/*
2993 	 * open the log device, and pull up all pending message
2994 	 * from the log driver.
2995 	 */
2996 	prepare_sys_poll();
2997 
2998 	/*
2999 	 * Now we can deliver the pending internal error messages.
3000 	 */
3001 	enable_errorlog();
3002 
3003 	/* 2: sys_poll thread */
3004 	(void) pthread_attr_setstacksize(&sys_attr, stacksize);
3005 	(void) pthread_attr_setstackaddr(&sys_attr, stack_ptr);
3006 	stack_ptr += stacksize + redzonesize;
3007 	if (pthread_create(&sys_thread, &sys_attr, sys_poll, NULL) != 0) {
3008 		logerror("pthread_create failed - fatal");
3009 		exit(1);
3010 	}
3011 
3012 	/*
3013 	 * We've started the sys_poll() and logmsg() threads.  Now we are ready
3014 	 * to open the door.  This cannot happen before spawning sys_poll(),
3015 	 * because after opening the door, syslog() will no longer take care of
3016 	 * LOG_CONS.  Therefor, we should pull up all pending log messages and
3017 	 * activate sys_poll() before opening the door, so that log driver
3018 	 * won't drop messages.
3019 	 */
3020 	open_door();
3021 
3022 	DPRINT1(1, "init(%u): accepting messages from local system\n",
3023 	    mythreadno);
3024 
3025 	if (turnoff == 0) {
3026 		/* init the hostname lookup queue */
3027 		(void) dataq_init(&hnlq);
3028 
3029 		/* 3: hostname lookup thread */
3030 		(void) pthread_attr_setstacksize(&hnl_attr, stacksize);
3031 		(void) pthread_attr_setstackaddr(&hnl_attr, stack_ptr);
3032 		stack_ptr += stacksize + redzonesize;
3033 		if (pthread_create(&hnl_thread, &hnl_attr,
3034 		    hostname_lookup, NULL) != 0) {
3035 			logerror("pthread_create failed - fatal");
3036 			exit(1);
3037 		}
3038 
3039 		/* 4: net_poll thread */
3040 		(void) pthread_attr_setstacksize(&net_attr, stacksize);
3041 		(void) pthread_attr_setstackaddr(&net_attr, stack_ptr);
3042 		stack_ptr += stacksize + redzonesize;
3043 
3044 		/* grab UDP port */
3045 		bindnet();
3046 
3047 		if (pthread_create(&net_thread, &net_attr, net_poll,
3048 		    NULL) != 0) {
3049 			logerror("pthread_create failed - fatal");
3050 			exit(1);
3051 		}
3052 		DPRINT1(1, "init(%u): accepting messages from remote\n",
3053 		    mythreadno);
3054 	}
3055 
3056 	(void) pthread_attr_destroy(&sys_attr);
3057 	(void) pthread_attr_destroy(&net_attr);
3058 	(void) pthread_attr_destroy(&log_attr);
3059 	(void) pthread_attr_destroy(&hnl_attr);
3060 
3061 	curalarm = MarkInterval * 60 / MARKCOUNT;
3062 	(void) alarm((unsigned)curalarm);
3063 	DPRINT2(2, "init(%u): Next alarm in %d seconds\n",
3064 	    mythreadno, curalarm);
3065 	DPRINT1(1, "init(%u): syslogd: started\n", mythreadno);
3066 }
3067 
3068 /*
3069  * will print a bunch of debugging stats on 'fd'
3070  */
3071 static void
3072 dumpstats(int fd)
3073 {
3074 	FILE *out;
3075 	struct filed *f;
3076 	int i;
3077 	char users[1024];
3078 	char cbuf[30];
3079 	char *dashes = "------------------------";
3080 	static int conversion_printed;
3081 
3082 	if ((out = fdopen(fd, "w+")) == NULL)
3083 		return;
3084 
3085 	(void) fprintf(out, "\nSyslogd started: %s",
3086 	    ctime_r(&start_time, cbuf));
3087 	(void) fprintf(out, "Input message count: system %d, network %d\n",
3088 	    sys_msg_count, net_msg_count);
3089 	(void) fprintf(out, "# Outputs: %d\n\n", nlogs);
3090 
3091 	(void) fprintf(out, "%s priority = [file, facility] %s\n\n",
3092 	    dashes, dashes);
3093 
3094 	for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3095 		(void) fprintf(out, "%d ", i / 10);
3096 	}
3097 	(void) fprintf(out, "\n");
3098 	for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3099 		(void) fprintf(out, "%d ", i % 10);
3100 	}
3101 	(void) fprintf(out, "\n");
3102 	for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3103 		(void) fprintf(out, "--");
3104 	}
3105 	(void) fprintf(out, "\n");
3106 
3107 	for (f = Files; f < &Files[nlogs]; f++) {
3108 		for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3109 			if (f->f_pmask[i] == NOPRI)
3110 				(void) fprintf(out, "X ");
3111 			else
3112 				(void) fprintf(out, "%d ",
3113 				    f->f_pmask[i]);
3114 		}
3115 		(void) fprintf(out, "%s: ", TypeNames[f->f_type]);
3116 		switch (f->f_type) {
3117 		case F_FILE:
3118 		case F_TTY:
3119 		case F_CONSOLE:
3120 			(void) fprintf(out, "%s", f->f_un.f_fname);
3121 			break;
3122 		case F_FORW:
3123 			(void) fprintf(out, "%s", f->f_un.f_forw.f_hname);
3124 			break;
3125 		case F_USERS:
3126 			for (i = 0; i < MAXUNAMES &&
3127 			    *f->f_un.f_uname[i]; i++) {
3128 				if (!i)
3129 					(void) fprintf(out, "%s",
3130 					    f->f_un.f_uname[i]);
3131 				else
3132 					(void) fprintf(out, ", %s",
3133 					    f->f_un.f_uname[i]);
3134 			}
3135 			break;
3136 		}
3137 		(void) fprintf(out, "\n");
3138 	}
3139 
3140 	if (!conversion_printed) {
3141 		(void) fprintf(out, "\nFacilities:\n");
3142 
3143 		for (i = 0; FacNames[i].c_val != -1; i++) {
3144 			(void) fprintf(out, "  [%02d] %s: %3d\n", i,
3145 			    FacNames[i].c_name, FacNames[i].c_val);
3146 		}
3147 
3148 		(void) fprintf(out, "\nPriorities:\n");
3149 
3150 		for (i = 0; PriNames[i].c_val != -1; i++) {
3151 			(void) fprintf(out, "  [%02d] %s: %3d\n", i,
3152 			    PriNames[i].c_name, PriNames[i].c_val);
3153 		}
3154 
3155 		conversion_printed = 1;
3156 	}
3157 
3158 	(void) fprintf(out, "\n\n\n\t\tPer File Statistics\n");
3159 	(void) fprintf(out, "%-24s\tTot\tDups\tNofwd\tErrs\n", "File");
3160 	(void) fprintf(out, "%-24s\t---\t----\t-----\t----\n", "----");
3161 	for (f = Files; f < &Files[nlogs]; f++) {
3162 		switch (f->f_type) {
3163 		case F_FILE:
3164 		case F_TTY:
3165 		case F_CONSOLE:
3166 			(void) fprintf(out, "%-24s", f->f_un.f_fname);
3167 			break;
3168 		case F_WALL:
3169 			(void) fprintf(out, "%-24s", TypeNames[f->f_type]);
3170 			break;
3171 		case F_FORW:
3172 			(void) fprintf(out, "%-24s", f->f_un.f_forw.f_hname);
3173 			break;
3174 		case F_USERS:
3175 			for (i = 0; i < MAXUNAMES &&
3176 			    *f->f_un.f_uname[i]; i++) {
3177 				if (!i)
3178 					(void) strcpy(users,
3179 					    f->f_un.f_uname[i]);
3180 				else {
3181 					(void) strcat(users, ",");
3182 					(void) strcat(users,
3183 					    f->f_un.f_uname[i]);
3184 				}
3185 			}
3186 			(void) fprintf(out, "%-24s", users);
3187 			break;
3188 		}
3189 		(void) fprintf(out, "\t%d\t%d\t%d\t%d\n",
3190 		    f->f_stat.total, f->f_stat.dups,
3191 		    f->f_stat.cantfwd, f->f_stat.errs);
3192 	}
3193 	(void) fprintf(out, "\n\n");
3194 	if (Debug && fd == 1)
3195 		return;
3196 	(void) fclose(out);
3197 }
3198 
3199 /*
3200  * conf_init - This routine is code seperated from the
3201  * init routine in order to be re-callable when we get
3202  * a SIGHUP signal.
3203  */
3204 static void
3205 conf_init(void)
3206 {
3207 	char *p;
3208 	int i;
3209 	struct filed *f;
3210 	char *m4argv[4];
3211 	int m4argc = 0;
3212 	conf_t cf;
3213 	pthread_t mythreadno;
3214 
3215 	if (Debug) {
3216 		mythreadno = pthread_self();
3217 	}
3218 
3219 	DPRINT1(2, "conf_init(%u): starting logger threads\n",
3220 	    mythreadno);
3221 
3222 	m4argv[m4argc++] = "m4";
3223 
3224 	if (amiloghost() == 1) {
3225 		DPRINT1(1, "conf_init(%u): I am loghost\n", mythreadno);
3226 		m4argv[m4argc++] = "-DLOGHOST=1";
3227 	}
3228 
3229 	m4argv[m4argc++] = ConfFile;
3230 	m4argv[m4argc] = NULL;
3231 
3232 	/*
3233 	 * Make sure the configuration file and m4 exist, and then parse
3234 	 * the configuration file with m4.  If any of these fail, resort
3235 	 * to our hardcoded fallback configuration.
3236 	 */
3237 
3238 	if (access(ConfFile, R_OK) == -1) {
3239 		DPRINT2(1, "conf_init(%u): %s does not exist\n", mythreadno,
3240 		    ConfFile);
3241 		logerror("can't open configuration file");
3242 		/* CSTYLED */
3243 		Files = (struct filed *) &fallback; /*lint !e545 */
3244 		cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3245 		cfline("*.PANIC\t*", 0, &Files[1]);
3246 		nlogs = 2;
3247 		goto nofile;
3248 	}
3249 
3250 	if (checkm4() != 0 || conf_open(&cf, "/usr/ccs/bin/m4", m4argv) == -1) {
3251 		DPRINT2(1, "conf_init(%u): cannot open %s\n", mythreadno,
3252 		    ConfFile);
3253 		/* CSTYLED */
3254 		Files = (struct filed *) &fallback; /*lint !e545 */
3255 		cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3256 		cfline("*.PANIC\t*", 0, &Files[1]);
3257 		nlogs = 2;
3258 		goto nofile;
3259 	}
3260 
3261 	/* Count the number of lines which are not blanks or comments */
3262 	nlogs = 0;
3263 	while ((p = conf_read(&cf)) != NULL) {
3264 		if (p[0] != '\0' && p[0] != '#')
3265 			nlogs++;
3266 	}
3267 
3268 	Files = (struct filed *)malloc(sizeof (struct filed) * nlogs);
3269 
3270 	if (!Files) {
3271 		DPRINT1(1, "conf_init(%u): malloc failed - can't "
3272 		    "allocate 'Files' array\n", mythreadno);
3273 		MALLOC_FAIL("loading minimum configuration");
3274 		/* CSTYLED */
3275 		Files = (struct filed *) &fallback; /*lint !e545 */
3276 		cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3277 		cfline("*.PANIC\t*", 0, &Files[1]);
3278 		nlogs = 2;
3279 		conf_close(&cf);
3280 		goto nofile;
3281 	}
3282 
3283 	/*
3284 	 *  Foreach line in the conf table, open that file.
3285 	 */
3286 	conf_rewind(&cf);
3287 	f = Files;
3288 	i = 0;
3289 	while (((p = conf_read(&cf)) != NULL) && (f < &Files[nlogs])) {
3290 		i++;
3291 		/* check for end-of-section */
3292 		if (p[0] == '\0' || p[0] == '#')
3293 			continue;
3294 
3295 		cfline(p, i, f);
3296 		if (f->f_type == F_UNUSED)
3297 			nlogs--;
3298 		else
3299 			f++;
3300 	}
3301 
3302 	conf_close(&cf);
3303 
3304 	/*
3305 	 * See if marks are to be written to any files.  If so, set up a
3306 	 * timeout for marks.
3307 	 */
3308 nofile:
3309 	Marking = 0;
3310 
3311 	/*
3312 	 * allocate thread stacks - one for each logger thread.
3313 	 */
3314 	if ((cstack_ptr = alloc_stacks(nlogs)) == NULL) {
3315 		logerror("alloc_stacks failed - fatal");
3316 		exit(1);
3317 	}
3318 
3319 	/* And now one thread for each configured file */
3320 	for (f = Files; f < &Files[nlogs]; f++) {
3321 		if (filed_init(f) != 0) {
3322 			logerror("pthread_create failed - fatal");
3323 			exit(1);
3324 		}
3325 
3326 		(void) pthread_mutex_lock(&cft);
3327 		++conf_threads;
3328 		(void) pthread_mutex_unlock(&cft);
3329 
3330 		if (f->f_type != F_UNUSED &&
3331 		    f->f_pmask[LOG_NFACILITIES] != NOPRI)
3332 			Marking = 1;
3333 	}
3334 }
3335 
3336 /*
3337  * filed init - initialize fields in a file descriptor struct
3338  * this is called before multiple threads are running, so no mutex
3339  * needs to be held at this time.
3340  */
3341 static int
3342 filed_init(struct filed *f)
3343 {
3344 	pthread_attr_t stack_attr;
3345 	pthread_t mythreadno;
3346 
3347 	if (Debug) {
3348 		mythreadno = pthread_self();
3349 	}
3350 
3351 	if (pthread_mutex_init(&f->filed_mutex, NULL) != 0) {
3352 		logerror("pthread_mutex_init failed");
3353 		return (-1);
3354 	}
3355 
3356 	DPRINT2(5, "filed_init(%u): dataq_init for queue %p\n",
3357 	    mythreadno, (void *)&f->f_queue);
3358 	(void) dataq_init(&f->f_queue);
3359 
3360 	if (pthread_attr_init(&stack_attr) != 0) {
3361 		logerror("pthread_attr_init failed");
3362 		return (-1);
3363 	}
3364 
3365 	(void) pthread_attr_setstacksize(&stack_attr, stacksize);
3366 	(void) pthread_attr_setstackaddr(&stack_attr, cstack_ptr);
3367 	cstack_ptr += stacksize + redzonesize;
3368 
3369 	f->f_msgflag = 0;
3370 	f->f_prevmsg.msg[0] = '\0';
3371 	f->f_prevmsg.flags = 0;
3372 	f->f_prevmsg.pri = 0;
3373 	f->f_prevmsg.host[0] = '\0';
3374 
3375 	f->f_current.msg[0] = '\0';
3376 	f->f_current.flags = 0;
3377 	f->f_current.pri = 0;
3378 	f->f_current.host[0] = '\0';
3379 
3380 	f->f_prevcount = 0;
3381 
3382 	f->f_stat.flag = 0;
3383 	f->f_stat.total = 0;
3384 	f->f_stat.dups = 0;
3385 	f->f_stat.cantfwd = 0;
3386 	f->f_stat.errs = 0;
3387 
3388 	if (pthread_create(&f->f_thread, NULL, logit, (void *)f) != 0) {
3389 		logerror("pthread_create failed");
3390 		(void) pthread_attr_destroy(&stack_attr);
3391 		return (-1);
3392 	}
3393 
3394 	(void) pthread_attr_destroy(&stack_attr);
3395 	return (0);
3396 }
3397 
3398 
3399 /*
3400  * Crack a configuration file line
3401  */
3402 static void
3403 cfline(char *line, int lineno, struct filed *f)
3404 {
3405 	char *p;
3406 	char *q;
3407 	int i;
3408 	char *bp;
3409 	int pri;
3410 	char buf[MAXLINE];
3411 	char ebuf[SYS_NMLN+1+40];
3412 	mode_t fmode, omode = O_WRONLY|O_APPEND|O_NOCTTY;
3413 	struct stat64 sbuf;
3414 	pthread_t mythreadno;
3415 
3416 	if (Debug) {
3417 		mythreadno = pthread_self();
3418 	}
3419 
3420 	DPRINT2(1, "cfline(%u): (%s)\n", mythreadno, line);
3421 
3422 	errno = 0;	/* keep errno related stuff out of logerror messages */
3423 
3424 	/* clear out file entry */
3425 	bzero((char *)f, sizeof (*f));
3426 	for (i = 0; i <= LOG_NFACILITIES; i++)
3427 		f->f_pmask[i] = NOPRI;
3428 
3429 	/* scan through the list of selectors */
3430 	for (p = line; *p && *p != '\t'; ) {
3431 
3432 		/* find the end of this facility name list */
3433 		for (q = p; *q && *q != '\t' && *q++ != '.'; )
3434 			continue;
3435 
3436 		/* collect priority name */
3437 		for (bp = buf; *q && !strchr("\t,;", *q); )
3438 			*bp++ = *q++;
3439 		*bp = '\0';
3440 
3441 		/* skip cruft */
3442 		while (strchr(", ;", *q))
3443 			q++;
3444 
3445 		/* decode priority name */
3446 		pri = decode(buf, PriNames);
3447 		if (pri < 0) {
3448 			logerror("line %d: unknown priority name \"%s\"",
3449 			    lineno, buf);
3450 			return;
3451 		}
3452 
3453 		/* scan facilities */
3454 		while (*p && !strchr("\t.;", *p)) {
3455 			for (bp = buf; *p && !strchr("\t,;.", *p); )
3456 				*bp++ = *p++;
3457 			*bp = '\0';
3458 			if (*buf == '*')
3459 				for (i = 0; i < LOG_NFACILITIES; i++)
3460 					f->f_pmask[i] = (uchar_t)pri;
3461 			else {
3462 				i = decode(buf, FacNames);
3463 				if (i < 0) {
3464 					logerror("line %d: unknown facility"
3465 					    " name \"%s\"", lineno, buf);
3466 					return;
3467 				}
3468 				f->f_pmask[i >> 3] = (uchar_t)pri;
3469 			}
3470 			while (*p == ',' || *p == ' ')
3471 				p++;
3472 		}
3473 
3474 		p = q;
3475 	}
3476 
3477 	/* skip to action part */
3478 	while (*p == '\t' || *p == ' ')
3479 		p++;
3480 
3481 	switch (*p) {
3482 	case '\0':
3483 		errno = 0;
3484 		logerror("line %d: no action part", lineno);
3485 		break;
3486 
3487 	case '@':
3488 		(void) strlcpy(f->f_un.f_forw.f_hname, ++p, SYS_NMLN);
3489 		if (logforward(f, ebuf, sizeof (ebuf)) != 0) {
3490 			logerror("line %d: %s", lineno, ebuf);
3491 			break;
3492 		}
3493 		f->f_type = F_FORW;
3494 		break;
3495 
3496 	case '/':
3497 		(void) strlcpy(f->f_un.f_fname, p, MAXPATHLEN);
3498 		if (stat64(p, &sbuf) < 0) {
3499 			logerror(p);
3500 			break;
3501 		}
3502 		/*
3503 		 * don't block trying to open a pipe
3504 		 * with no reader on the other end
3505 		 */
3506 		fmode = 0; 	/* reset each pass */
3507 		if (S_ISFIFO(sbuf.st_mode))
3508 			fmode = O_NONBLOCK;
3509 
3510 		f->f_file = open64(p, omode|fmode);
3511 		if (f->f_file < 0) {
3512 			if (fmode && errno == ENXIO) {
3513 				errno = 0;
3514 				logerror("%s - no reader", p);
3515 			} else
3516 				logerror(p);
3517 			break;
3518 		}
3519 
3520 		/*
3521 		 * Fifos are initially opened NONBLOCK
3522 		 * to insure we don't hang, but once
3523 		 * we are open, we need to change the
3524 		 * behavior back to blocking, otherwise
3525 		 * we may get write errors, and the log
3526 		 * will get closed down the line.
3527 		 */
3528 		if (S_ISFIFO(sbuf.st_mode))
3529 			(void) fcntl(f->f_file, F_SETFL, omode);
3530 
3531 		if (isatty(f->f_file)) {
3532 			f->f_type = F_TTY;
3533 			untty();
3534 		} else
3535 			f->f_type = F_FILE;
3536 
3537 		if ((strcmp(p, ctty) == 0) || (strcmp(p, sysmsg) == 0))
3538 			f->f_type = F_CONSOLE;
3539 		break;
3540 
3541 	case '*':
3542 		f->f_type = F_WALL;
3543 		break;
3544 
3545 	default:
3546 		for (i = 0; i < MAXUNAMES && *p; i++) {
3547 			for (q = p; *q && *q != ','; )
3548 				q++;
3549 			(void) strlcpy(f->f_un.f_uname[i], p, UNAMESZ);
3550 			if ((q - p) > UNAMESZ)
3551 				f->f_un.f_uname[i][UNAMESZ] = '\0';
3552 			else
3553 				f->f_un.f_uname[i][q - p] = '\0';
3554 			while (*q == ',' || *q == ' ')
3555 				q++;
3556 			p = q;
3557 		}
3558 		f->f_type = F_USERS;
3559 		break;
3560 	}
3561 	f->f_orig_type = f->f_type;
3562 }
3563 
3564 
3565 /*
3566  *  Decode a symbolic name to a numeric value
3567  */
3568 static int
3569 decode(char *name, struct code *codetab)
3570 {
3571 	struct code *c;
3572 	char *p;
3573 	char buf[40];
3574 
3575 	if (isdigit(*name))
3576 		return (atoi(name));
3577 
3578 	(void) strncpy(buf, name, sizeof (buf) - 1);
3579 	for (p = buf; *p; p++)
3580 		if (isupper(*p))
3581 			*p = tolower(*p);
3582 	for (c = codetab; c->c_name; c++)
3583 		if (!(strcmp(buf, c->c_name)))
3584 			return (c->c_val);
3585 
3586 	return (-1);
3587 }
3588 
3589 static int
3590 ismyaddr(struct netbuf *nbp)
3591 {
3592 	int i;
3593 
3594 	if (nbp == NULL)
3595 		return (0);
3596 
3597 	for (i = 1; i < Ninputs; i++) {
3598 		if (same_addr(nbp, Myaddrs[i]))
3599 			return (1);
3600 	}
3601 	return (0);
3602 }
3603 
3604 static void
3605 getnets(void)
3606 {
3607 	struct nd_hostserv hs;
3608 	struct netconfig *ncp;
3609 	struct nd_addrlist *nap;
3610 	struct netbuf *nbp;
3611 	int i, inputs;
3612 	void *handle;
3613 	char *uap;
3614 	pthread_t mythreadno;
3615 
3616 	if (Debug) {
3617 		mythreadno = pthread_self();
3618 	}
3619 
3620 	if (turnoff) {
3621 		DPRINT1(1, "getnets(%u): network is being turned off\n",
3622 		    mythreadno);
3623 		return;
3624 	}
3625 
3626 	hs.h_host = HOST_SELF;
3627 	hs.h_serv = "syslog";
3628 
3629 	if ((handle = setnetconfig()) == NULL) {
3630 		return;
3631 	}
3632 
3633 	while ((ncp = getnetconfig(handle)) != NULL) {
3634 		if (ncp->nc_semantics != NC_TPI_CLTS) {
3635 			continue;
3636 		}
3637 
3638 		if (netdir_getbyname(ncp, &hs, &nap) != 0) {
3639 			continue;
3640 		}
3641 
3642 		if (nap == NULL || nap->n_cnt <= 0) {
3643 			DPRINT1(1, "getnets(%u): found no address\n",
3644 			    mythreadno);
3645 			netdir_free((void *)nap, ND_ADDRLIST);
3646 			continue;
3647 		}
3648 
3649 		if (Debug) {
3650 			DPRINT2(1, "getnets(%u): found %d addresses",
3651 			    mythreadno, nap->n_cnt);
3652 			DPRINT0(1, ", they are: ");
3653 			nbp = nap->n_addrs;
3654 
3655 			for (i = 0; i < nap->n_cnt; i++) {
3656 				if ((uap = taddr2uaddr(ncp, nbp)) != NULL) {
3657 					DPRINT1(1, "%s ", uap);
3658 					free(uap);
3659 				}
3660 				nbp++;
3661 			}
3662 
3663 			DPRINT0(1, "\n");
3664 		}
3665 
3666 		inputs = Ninputs + nap->n_cnt;
3667 
3668 		Nfd = realloc(Nfd, inputs * sizeof (struct pollfd));
3669 		Ncf = realloc(Ncf, inputs * sizeof (struct netconfig));
3670 		Myaddrs = realloc(Myaddrs, inputs * sizeof (struct netbuf *));
3671 		Udp = realloc(Udp, inputs * sizeof (struct t_unitdata *));
3672 		Errp = realloc(Errp, inputs * sizeof (struct t_uderr *));
3673 
3674 		/*
3675 		 * all malloc failures here are fatal
3676 		 */
3677 		if (Nfd == NULL || Ncf == NULL || Myaddrs == NULL ||
3678 		    Udp == NULL || Errp == NULL) {
3679 			MALLOC_FAIL_EXIT;
3680 		}
3681 
3682 		nbp = nap->n_addrs;
3683 
3684 		for (i = 0; i < nap->n_cnt; i++, nbp++) {
3685 			char ebuf[128];
3686 
3687 			if (addnet(ncp, nbp) == 0) {
3688 				/* no error */
3689 				continue;
3690 			}
3691 
3692 			(void) strcpy(ebuf, "Unable to configure syslog port");
3693 
3694 			if ((uap = taddr2uaddr(ncp, nbp)) != NULL) {
3695 				size_t l = strlen(ebuf);
3696 				(void) snprintf(ebuf + l, sizeof (ebuf) - l,
3697 				    " for %s", uap);
3698 			}
3699 
3700 			DPRINT2(1, "getnets(%u): %s",
3701 			    mythreadno, ebuf);
3702 
3703 			if (uap) {
3704 				free(uap);
3705 			}
3706 
3707 			logerror(ebuf);
3708 			/*
3709 			 * Here maybe syslogd can quit. However, syslogd
3710 			 * has been ignoring this error and keep running.
3711 			 * So we won't break it.
3712 			 */
3713 		}
3714 
3715 		netdir_free((void *)nap, ND_ADDRLIST);
3716 	}
3717 
3718 	(void) endnetconfig(handle);
3719 }
3720 
3721 /*
3722  * Open the network device, and allocate necessary resources.
3723  * Myaddrs will also be filled, so that we can call ismyaddr() before
3724  * being bound to the network.
3725  */
3726 static int
3727 addnet(struct netconfig *ncp, struct netbuf *nbp)
3728 {
3729 	int fd;
3730 	struct netbuf *bp;
3731 
3732 	fd = t_open(ncp->nc_device, O_RDWR, NULL);
3733 
3734 	if (fd < 0) {
3735 		return (1);
3736 	}
3737 
3738 	(void) memcpy(&Ncf[Ninputs], ncp, sizeof (struct netconfig));
3739 
3740 	/*LINTED*/
3741 	Udp[Ninputs] = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ADDR);
3742 
3743 	if (Udp[Ninputs] == NULL) {
3744 		(void) t_close(fd);
3745 		return (1);
3746 	}
3747 
3748 	/*LINTED*/
3749 	Errp[Ninputs] = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ADDR);
3750 
3751 	if (Errp[Ninputs] == NULL) {
3752 		(void) t_close(fd);
3753 		(void) t_free((char *)Udp[Ninputs], T_UNITDATA);
3754 		return (1);
3755 	}
3756 
3757 	if ((bp = malloc(sizeof (struct netbuf))) == NULL ||
3758 	    (bp->buf = malloc(nbp->len)) == NULL) {
3759 		MALLOC_FAIL("allocating address buffer");
3760 		(void) t_close(fd);
3761 		(void) t_free((char *)Udp[Ninputs], T_UNITDATA);
3762 		(void) t_free((char *)Errp[Ninputs], T_UDERROR);
3763 
3764 		if (bp) {
3765 			free(bp);
3766 		}
3767 
3768 		return (1);
3769 	}
3770 
3771 	bp->len = nbp->len;
3772 	(void) memcpy(bp->buf, nbp->buf, nbp->len);
3773 	Myaddrs[Ninputs] = bp;
3774 
3775 	Nfd[Ninputs].fd = fd;
3776 	Nfd[Ninputs].events = POLLIN;
3777 	Ninputs++;
3778 	return (0);
3779 }
3780 
3781 /*
3782  * Allocate UDP buffer to minimize packet loss.
3783  */
3784 static void
3785 set_udp_buffer(int fd)
3786 {
3787 	struct t_optmgmt req, resp;
3788 	struct opthdr *opt;
3789 	size_t optsize, bsize = 256 * 1024;
3790 	pthread_t mythreadno;
3791 
3792 	if (Debug) {
3793 		mythreadno = pthread_self();
3794 	}
3795 
3796 	optsize = sizeof (struct opthdr) + sizeof (int);
3797 	if ((opt = malloc(optsize)) == NULL) {
3798 		MALLOC_FAIL("will have no udp buffer");
3799 		return;
3800 	}
3801 	opt->level = SOL_SOCKET;
3802 	opt->name = SO_RCVBUF;
3803 	opt->len = sizeof (int);
3804 	*(int *)(opt + 1) = bsize;
3805 
3806 	req.flags = T_NEGOTIATE;
3807 	req.opt.len = optsize;
3808 	req.opt.buf = (char *)opt;
3809 
3810 	resp.flags = 0;
3811 	resp.opt.maxlen = optsize;
3812 	resp.opt.buf = (char *)opt;
3813 
3814 	while (t_optmgmt(fd, &req, &resp) == -1 || resp.flags != T_SUCCESS) {
3815 		if (t_errno != TSYSERR || errno != ENOBUFS) {
3816 			bsize = 0;
3817 			break;
3818 		}
3819 		bsize >>= 1;
3820 		if (bsize < 8192) {
3821 			break;
3822 		}
3823 		*(int *)(opt + 1) = bsize;
3824 	}
3825 	if (bsize == 0) {
3826 		logerror("failed to allocate UDP buffer");
3827 	}
3828 	DPRINT3(1, "set_udp_buffer(%u): allocate %d for fd %d\n",
3829 	    mythreadno, bsize, fd);
3830 	free(opt);
3831 }
3832 
3833 /*
3834  * Attach the network, and allocate UDP buffer for the interface.
3835  */
3836 static void
3837 bindnet(void)
3838 {
3839 	struct t_bind bind, *bound;
3840 	int cnt, i;
3841 	char *uap;
3842 	pthread_t mythreadno;
3843 
3844 	if (Debug) {
3845 		mythreadno = pthread_self();
3846 	}
3847 
3848 	cnt = 0;
3849 
3850 	while (cnt < Ninputs) {
3851 		char ebuf[128];
3852 
3853 		/*LINTED*/
3854 		bound  = (struct t_bind *)t_alloc(Nfd[cnt].fd, T_BIND, T_ADDR);
3855 		bind.addr = *Myaddrs[cnt];
3856 		bind.qlen = 0;
3857 
3858 		if (t_bind(Nfd[cnt].fd, &bind, bound) == 0) {
3859 			if (same_addr(&bind.addr, &bound->addr)) {
3860 				(void) t_free((char *)bound, T_BIND);
3861 				set_udp_buffer(Nfd[cnt].fd);
3862 				cnt++;
3863 				continue;
3864 			}
3865 		}
3866 
3867 		/* failed to bind port */
3868 		(void) t_free((char *)bound, T_BIND);
3869 
3870 		(void) strcpy(ebuf, "Unable to bind syslog port");
3871 
3872 		uap = taddr2uaddr(&Ncf[cnt], Myaddrs[cnt]);
3873 		if (uap) {
3874 			i = strlen(ebuf);
3875 			(void) snprintf(ebuf + i, sizeof (ebuf) - i,
3876 			    " for %s", uap);
3877 		}
3878 
3879 		DPRINT2(1, "bindnet(%u): failed to bind port (%s)\n",
3880 		    mythreadno, uap ? uap : "<unknown>");
3881 
3882 		if (uap) {
3883 			free(uap);
3884 		}
3885 
3886 		errno = 0;
3887 		logerror(ebuf);
3888 
3889 		(void) t_close(Nfd[cnt].fd);
3890 		free(Myaddrs[cnt]->buf);
3891 		free(Myaddrs[cnt]);
3892 		(void) t_free((char *)Udp[cnt], T_UNITDATA);
3893 		(void) t_free((char *)Errp[cnt], T_UDERROR);
3894 
3895 		for (i = cnt; i < (Ninputs-1); i++) {
3896 			Nfd[i] = Nfd[i + 1];
3897 			Ncf[i] = Ncf[i + 1];
3898 			Myaddrs[i] = Myaddrs[i + 1];
3899 			Udp[i] = Udp[i + 1];
3900 			Errp[i] = Errp[i + 1];
3901 		}
3902 
3903 		Ninputs--;
3904 	}
3905 }
3906 
3907 static int
3908 logforward(struct filed *f, char *ebuf, size_t elen)
3909 {
3910 	struct nd_hostserv hs;
3911 	struct netbuf *nbp;
3912 	struct netconfig *ncp;
3913 	struct nd_addrlist *nap;
3914 	void *handle;
3915 	char *hp;
3916 
3917 	hp = f->f_un.f_forw.f_hname;
3918 	hs.h_host = hp;
3919 	hs.h_serv = "syslog";
3920 
3921 	if ((handle = setnetconfig()) == NULL) {
3922 		(void) strlcpy(ebuf,
3923 		    "unable to rewind the netconfig database", elen);
3924 		errno = 0;
3925 		return (-1);
3926 	}
3927 	nap = (struct nd_addrlist *)NULL;
3928 	while ((ncp = getnetconfig(handle)) != NULL) {
3929 		if (ncp->nc_semantics == NC_TPI_CLTS) {
3930 			if (netdir_getbyname(ncp, &hs, &nap) == 0) {
3931 				if (!nap)
3932 					continue;
3933 				nbp = nap->n_addrs;
3934 				break;
3935 			}
3936 		}
3937 	}
3938 	if (ncp == NULL) {
3939 		(void) endnetconfig(handle);
3940 		(void) snprintf(ebuf, elen,
3941 		    "WARNING: %s could not be resolved", hp);
3942 		errno = 0;
3943 		return (-1);
3944 	}
3945 	if (nap == (struct nd_addrlist *)NULL) {
3946 		(void) endnetconfig(handle);
3947 		(void) snprintf(ebuf, elen, "unknown host %s", hp);
3948 		errno = 0;
3949 		return (-1);
3950 	}
3951 	/* CSTYLED */
3952 	if (ismyaddr(nbp)) { /*lint !e644 */
3953 		netdir_free((void *)nap, ND_ADDRLIST);
3954 		(void) endnetconfig(handle);
3955 		(void) snprintf(ebuf, elen,
3956 		    "host %s is this host - logging loop", hp);
3957 		errno = 0;
3958 		return (-1);
3959 	}
3960 	f->f_un.f_forw.f_addr.buf = malloc(nbp->len);
3961 	if (f->f_un.f_forw.f_addr.buf == NULL) {
3962 		netdir_free((void *)nap, ND_ADDRLIST);
3963 		(void) endnetconfig(handle);
3964 		(void) strlcpy(ebuf, "malloc failed", elen);
3965 		return (-1);
3966 	}
3967 	bcopy(nbp->buf, f->f_un.f_forw.f_addr.buf, nbp->len);
3968 	f->f_un.f_forw.f_addr.len = nbp->len;
3969 	f->f_file = t_open(ncp->nc_device, O_RDWR, NULL);
3970 	if (f->f_file < 0) {
3971 		netdir_free((void *)nap, ND_ADDRLIST);
3972 		(void) endnetconfig(handle);
3973 		free(f->f_un.f_forw.f_addr.buf);
3974 		(void) strlcpy(ebuf, "t_open", elen);
3975 		return (-1);
3976 	}
3977 	netdir_free((void *)nap, ND_ADDRLIST);
3978 	(void) endnetconfig(handle);
3979 	if (t_bind(f->f_file, NULL, NULL) < 0) {
3980 		(void) strlcpy(ebuf, "t_bind", elen);
3981 		free(f->f_un.f_forw.f_addr.buf);
3982 		(void) t_close(f->f_file);
3983 		return (-1);
3984 	}
3985 	return (0);
3986 }
3987 
3988 static int
3989 amiloghost(void)
3990 {
3991 	struct nd_hostserv hs;
3992 	struct netconfig *ncp;
3993 	struct nd_addrlist *nap;
3994 	struct netbuf *nbp;
3995 	int i, fd;
3996 	void *handle;
3997 	char *uap;
3998 	struct t_bind bind, *bound;
3999 	pthread_t mythreadno;
4000 
4001 	if (Debug) {
4002 		mythreadno = pthread_self();
4003 	}
4004 
4005 	/*
4006 	 * we need to know if we are running on the loghost. This is
4007 	 * checked by binding to the address associated with "loghost"
4008 	 * and "syslogd" service over the connectionless transport
4009 	 */
4010 	hs.h_host = "loghost";
4011 	hs.h_serv = "syslog";
4012 
4013 	if ((handle = setnetconfig()) == NULL) {
4014 		return (0);
4015 	}
4016 
4017 	while ((ncp = getnetconfig(handle)) != NULL) {
4018 		if (ncp->nc_semantics != NC_TPI_CLTS) {
4019 			continue;
4020 		}
4021 
4022 		if (netdir_getbyname(ncp, &hs, &nap) != 0) {
4023 			continue;
4024 		}
4025 
4026 		if (nap == NULL) {
4027 			continue;
4028 		}
4029 
4030 		nbp = nap->n_addrs;
4031 
4032 		for (i = 0; i < nap->n_cnt; i++) {
4033 			if ((uap = taddr2uaddr(ncp, nbp)) != (char *)NULL) {
4034 				DPRINT2(1, "amiloghost(%u): testing %s\n",
4035 				    mythreadno, uap);
4036 			}
4037 
4038 			free(uap);
4039 
4040 			fd = t_open(ncp->nc_device, O_RDWR, NULL);
4041 
4042 			if (fd < 0) {
4043 				netdir_free((void *)nap, ND_ADDRLIST);
4044 				(void) endnetconfig(handle);
4045 				return (0);
4046 			}
4047 
4048 			/*LINTED*/
4049 			bound = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
4050 			bind.addr = *nbp;
4051 			bind.qlen = 0;
4052 
4053 			if (t_bind(fd, &bind, bound) == 0) {
4054 				(void) t_close(fd);
4055 				(void) t_free((char *)bound, T_BIND);
4056 				netdir_free((void *)nap, ND_ADDRLIST);
4057 				(void) endnetconfig(handle);
4058 				return (1);
4059 			} else {
4060 				(void) t_close(fd);
4061 				(void) t_free((char *)bound, T_BIND);
4062 			}
4063 
4064 			nbp++;
4065 		}
4066 
4067 		netdir_free((void *)nap, ND_ADDRLIST);
4068 	}
4069 
4070 	(void) endnetconfig(handle);
4071 	return (0);
4072 }
4073 
4074 int
4075 same_addr(struct netbuf *na, struct netbuf *nb)
4076 {
4077 	char *a, *b;
4078 	size_t n;
4079 
4080 	assert(na->buf != NULL && nb->buf != NULL);
4081 
4082 	if (na->len != nb->len) {
4083 		return (0);
4084 	}
4085 
4086 	a = na->buf;
4087 	b = nb->buf;
4088 	n = nb->len;
4089 
4090 	while (n-- > 0) {
4091 		if (*a++ != *b++) {
4092 			return (0);
4093 		}
4094 	}
4095 
4096 	return (1);
4097 }
4098 
4099 /*
4100  * allocates a new message structure, initializes it
4101  * and returns a pointer to it
4102  */
4103 static log_message_t *
4104 new_msg(void)
4105 {
4106 	log_message_t *lm;
4107 	pthread_t mythreadno;
4108 
4109 	if (Debug) {
4110 		mythreadno = pthread_self();
4111 	}
4112 
4113 	if ((lm = malloc(sizeof (log_message_t))) == NULL)
4114 		return ((log_message_t *)NULL);
4115 
4116 	_NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*lm))
4117 
4118 	if (pthread_mutex_init(&lm->msg_mutex, NULL) != 0)
4119 		return ((log_message_t *)NULL);
4120 	lm->refcnt = 0;
4121 	lm->pri = 0;
4122 	lm->flags = 0;
4123 	lm->hlp = NULL;
4124 	lm->msg[0] = '\0';
4125 	lm->ptr = NULL;
4126 
4127 	DPRINT2(3, "new_msg(%u): creating msg %p\n", mythreadno, (void *)lm);
4128 	return (lm);
4129 }
4130 
4131 /*
4132  * frees a message structure - should only be called if
4133  * the refcount is 0
4134  */
4135 static void
4136 free_msg(log_message_t *lm)
4137 {
4138 	pthread_t mythreadno;
4139 
4140 	if (Debug) {
4141 		mythreadno = pthread_self();
4142 	}
4143 
4144 	assert(lm != NULL && lm->refcnt == 0);
4145 	if (lm->hlp != NULL)
4146 		freehl(lm->hlp);
4147 	DPRINT2(3, "free_msg(%u): freeing msg %p\n", mythreadno, (void *)lm);
4148 	free(lm);
4149 }
4150 
4151 /*
4152  *  Make sure that the message makes sense in the current locale, and
4153  *  does not contain stray control characters.
4154  */
4155 static void
4156 filter_string(char *mbstr, char *filtered, size_t max)
4157 {
4158 	size_t	cs = 0;
4159 	size_t	mb_cur_max;
4160 	unsigned char	*p = (unsigned char *)mbstr;
4161 	pthread_t mythreadno = 0;
4162 
4163 	if (Debug) {
4164 		mythreadno = pthread_self();
4165 	}
4166 
4167 	assert(mbstr != NULL && filtered != NULL);
4168 
4169 	/*
4170 	 * Since the access to MB_CUR_MAX is expensive (because
4171 	 * MB_CUR_MAX lives in a global area), it should be
4172 	 * restrained for the better performance.
4173 	 */
4174 	mb_cur_max = (size_t)MB_CUR_MAX;
4175 	if (mb_cur_max > 1) {
4176 		/* multibyte locale */
4177 		int	mlen;
4178 		wchar_t	wc;
4179 
4180 		while (*p != '\0') {
4181 			if ((mlen = mbtowc(&wc, (char *)p,
4182 			    mb_cur_max)) == -1) {
4183 				/*
4184 				 * Invalid byte sequence found.
4185 				 *
4186 				 * try to print one byte
4187 				 * in ASCII format.
4188 				 */
4189 				DPRINT2(9, "filter_string(%u): Invalid "
4190 				    "MB sequence: %ld\n", mythreadno,
4191 				    wc);
4192 
4193 				if (!putctrlc(*p++, &filtered, &cs, max)) {
4194 					/* not enough buffer */
4195 					goto end;
4196 				} else {
4197 					continue;
4198 				}
4199 			} else {
4200 				/*
4201 				 * Since *p is not a null byte here,
4202 				 * mbtowc should have never returned 0.
4203 				 *
4204 				 * A valid wide character found.
4205 				 */
4206 
4207 				if (wc != L'\t' && iswcntrl(wc)) {
4208 					/*
4209 					 * non-tab, non-newline, and
4210 					 * control character found.
4211 					 *
4212 					 * try to print this wide character
4213 					 * in ASCII-format.
4214 					 */
4215 					char	*q = filtered;
4216 
4217 					DPRINT2(9, "filter_string(%u): MB"
4218 					    " control character: %ld\n",
4219 					    mythreadno, wc);
4220 
4221 					while (mlen--) {
4222 						if (!putctrlc(*p++, &filtered,
4223 						    &cs, max)) {
4224 							/*
4225 							 * not enough buffer in
4226 							 * filtered
4227 							 *
4228 							 * cancel already
4229 							 * stored bytes in
4230 							 * filtered for this
4231 							 * wide character.
4232 							 */
4233 							filtered = q;
4234 							goto end;
4235 						}
4236 					}
4237 					continue;
4238 				} else {
4239 					/*
4240 					 * tab, newline, or non-control
4241 					 * character found.
4242 					 */
4243 					if (cs + mlen < max) {
4244 						/* enough buffer */
4245 						cs += mlen;
4246 						while (mlen--) {
4247 							*filtered++ = *p++;
4248 						}
4249 						continue;
4250 					} else {
4251 						/* not enough buffer */
4252 						goto end;
4253 					}
4254 				}
4255 			}
4256 		}
4257 	} else {
4258 		/* singlebyte locale */
4259 
4260 		while (*p != '\0') {
4261 			if (*p != '\t' && iscntrl(*p)) {
4262 				/*
4263 				 * non-tab, non-newline,
4264 				 * and control character found.
4265 				 *
4266 				 * try to print this singlebyte character
4267 				 * in ASCII format.
4268 				 */
4269 				DPRINT2(9, "filter_string(%u): control "
4270 				    "character: %d\n", mythreadno, *p);
4271 
4272 				if (!putctrlc(*p++, &filtered, &cs, max)) {
4273 					/* not enough buffer */
4274 					goto end;
4275 				} else {
4276 					continue;
4277 				}
4278 			} else if (*p != '\t' && !isprint(*p)) {
4279 				/*
4280 				 * non-tab and non printable character found
4281 				 * this check is required for the C locale
4282 				 */
4283 				DPRINT2(9, "filter_string(%u): non-printable "
4284 				    "character: %d\n", mythreadno, *p);
4285 				if (!putctrlc(*p++, &filtered, &cs, max)) {
4286 					/* not enough buffer */
4287 					goto end;
4288 				} else {
4289 					continue;
4290 				}
4291 			} else {
4292 				/*
4293 				 * tab, newline, non-control character, or
4294 				 * printable found.
4295 				 */
4296 				if (cs + 1 < max) {
4297 					*filtered++ = *p++;
4298 					cs++;
4299 					continue;
4300 				} else {
4301 					/* not enough buffer */
4302 					goto end;
4303 				}
4304 			}
4305 		}
4306 	}
4307 
4308 end:
4309 	*filtered = '\0';
4310 
4311 	if (cs >= 2 &&
4312 	    filtered[-2] == '\\' && filtered[-1] == 'n') {
4313 		filtered[-2] = '\0';
4314 	}
4315 }
4316 
4317 static char *
4318 alloc_stacks(int numstacks)
4319 {
4320 	size_t pagesize, mapsize;
4321 	char *stack_top;
4322 	char *addr;
4323 	int i;
4324 
4325 	pagesize = (size_t)sysconf(_SC_PAGESIZE);
4326 	/*
4327 	 * stacksize and redzonesize are global so threads
4328 	 * can be created elsewhere and refer to the sizes
4329 	 */
4330 	stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) +
4331 	    DEFAULT_STACKSIZE, pagesize);
4332 	redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize);
4333 
4334 	/*
4335 	 * allocate an additional "redzonesize" chunk in addition
4336 	 * to what we require, so we can create a redzone at the
4337 	 * bottom of the last stack as well.
4338 	 */
4339 	mapsize = redzonesize + numstacks * (stacksize + redzonesize);
4340 	stack_top = mmap(NULL, mapsize, PROT_READ|PROT_WRITE,
4341 	    MAP_PRIVATE|MAP_ANON, -1, 0);
4342 	if (stack_top == MAP_FAILED)
4343 		return (NULL);
4344 
4345 	addr = stack_top;
4346 	/*
4347 	 * this loop is intentionally <= instead of <, so we can
4348 	 * protect the redzone at the bottom of the last stack
4349 	 */
4350 	for (i = 0; i <= numstacks; i++) {
4351 		(void) mprotect(addr, redzonesize, PROT_NONE);
4352 		addr += stacksize + redzonesize;
4353 	}
4354 	return ((char *)(stack_top + redzonesize));
4355 }
4356 
4357 static void
4358 dealloc_stacks(int numstacks)
4359 {
4360 	size_t pagesize, mapsize;
4361 
4362 	pagesize = (size_t)sysconf(_SC_PAGESIZE);
4363 
4364 	stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) +
4365 	    DEFAULT_STACKSIZE, pagesize);
4366 
4367 	redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize);
4368 
4369 	mapsize = redzonesize + numstacks * (stacksize + redzonesize);
4370 	(void) munmap(cstack_ptr - mapsize, mapsize);
4371 }
4372 
4373 static void
4374 filed_destroy(struct filed *f)
4375 {
4376 	(void) dataq_destroy(&f->f_queue);
4377 	(void) pthread_mutex_destroy(&f->filed_mutex);
4378 }
4379 
4380 static void
4381 close_door(void)
4382 {
4383 	pthread_t mythreadno;
4384 
4385 	if (Debug) {
4386 		mythreadno = pthread_self();
4387 	}
4388 
4389 	(void) fdetach(DoorFileName);
4390 
4391 	DPRINT2(5, "close_door(%u): detached server() from %s\n",
4392 	    mythreadno, DoorFileName);
4393 }
4394 
4395 static void
4396 delete_doorfiles(void)
4397 {
4398 	pthread_t mythreadno;
4399 	struct stat sb;
4400 	int err;
4401 	char line[MAXLINE+1];
4402 
4403 	if (Debug) {
4404 		mythreadno = pthread_self();
4405 	}
4406 
4407 
4408 	if (lstat(DoorFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4409 		if (unlink(DoorFileName) < 0) {
4410 			err = errno;
4411 			(void) snprintf(line, sizeof (line),
4412 			    "unlink() of %s failed - fatal", DoorFileName);
4413 			errno = err;
4414 			logerror(line);
4415 			DPRINT3(1, "delete_doorfiles(%u): error: %s, "
4416 			    "errno=%d\n", mythreadno, line, err);
4417 			exit(1);
4418 		}
4419 
4420 		DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4421 		    mythreadno, DoorFileName);
4422 	}
4423 
4424 	if (strcmp(DoorFileName, DOORFILE) == 0) {
4425 		if (lstat(OLD_DOORFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4426 			if (unlink(OLD_DOORFILE) < 0) {
4427 				err = errno;
4428 				(void) snprintf(line, sizeof (line),
4429 				    "unlink() of %s failed", OLD_DOORFILE);
4430 				DPRINT2(5, "delete_doorfiles(%u): %s\n",
4431 				    mythreadno, line);
4432 
4433 				if (err != EROFS) {
4434 					errno = err;
4435 					(void) strlcat(line, " - fatal",
4436 					    sizeof (line));
4437 					logerror(line);
4438 					DPRINT3(1, "delete_doorfiles(%u): "
4439 					    "error: %s, errno=%d\n",
4440 					    mythreadno, line, err);
4441 					exit(1);
4442 				}
4443 
4444 				DPRINT1(5, "delete_doorfiles(%u): unlink() "
4445 				    "failure OK on RO file system\n",
4446 				    mythreadno);
4447 			}
4448 
4449 			DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4450 			    mythreadno, OLD_DOORFILE);
4451 		}
4452 	}
4453 
4454 	if (lstat(PidFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4455 		if (unlink(PidFileName) < 0) {
4456 			err = errno;
4457 			(void) snprintf(line, sizeof (line),
4458 			    "unlink() of %s failed - fatal", PidFileName);
4459 			errno = err;
4460 			logerror(line);
4461 			DPRINT3(1, "delete_doorfiles(%u): error: %s, "
4462 			    "errno=%d\n", mythreadno, line, err);
4463 			exit(1);
4464 		}
4465 
4466 		DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno,
4467 		    PidFileName);
4468 	}
4469 
4470 	if (strcmp(PidFileName, PIDFILE) == 0) {
4471 		if (lstat(OLD_PIDFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4472 			if (unlink(OLD_PIDFILE) < 0) {
4473 				err = errno;
4474 				(void) snprintf(line, sizeof (line),
4475 				    "unlink() of %s failed", OLD_PIDFILE);
4476 				DPRINT2(5, "delete_doorfiles(%u): %s, \n",
4477 				    mythreadno, line);
4478 
4479 				if (err != EROFS) {
4480 					errno = err;
4481 					(void) strlcat(line, " - fatal",
4482 					    sizeof (line));
4483 					logerror(line);
4484 					DPRINT3(1, "delete_doorfiles(%u): "
4485 					    "error: %s, errno=%d\n",
4486 					    mythreadno, line, err);
4487 					exit(1);
4488 				}
4489 
4490 				DPRINT1(5, "delete_doorfiles(%u): unlink "
4491 				    "failure OK on RO file system\n",
4492 				    mythreadno);
4493 			}
4494 
4495 			DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4496 			    mythreadno, OLD_PIDFILE);
4497 		}
4498 	}
4499 
4500 	if (DoorFd != -1) {
4501 		(void) door_revoke(DoorFd);
4502 	}
4503 
4504 	DPRINT2(1, "delete_doorfiles(%u): revoked door: DoorFd=%d\n",
4505 	    mythreadno, DoorFd);
4506 }
4507 
4508 
4509 /*ARGSUSED*/
4510 static void
4511 signull(int sig, siginfo_t *sip, void *utp)
4512 {
4513 	DPRINT1(1, "signull(%u): THIS CALL SHOULD NEVER HAPPEN\n",
4514 	    pthread_self());
4515 	/*
4516 	 * Do nothing, as this is a place-holder used in conjunction with
4517 	 * sigaction()/sigwait() to ensure that the proper disposition is
4518 	 * given to the signals we handle in main().
4519 	 */
4520 }
4521 
4522 /*
4523  * putctrlc returns zero, if failed due to not enough buffer.
4524  * Otherwise, putctrlc returns non-zero.
4525  *
4526  * c:     a byte to print in ASCII format
4527  * **buf: a pointer to the pointer to the output buffer.
4528  * *cl:   current length of characters in the output buffer
4529  * max:   maximum length of the buffer
4530  */
4531 
4532 static int
4533 putctrlc(int c, char **buf, size_t *cl, size_t max)
4534 {
4535 	char	*p = *buf;
4536 
4537 	if (c == '\n') {
4538 		if (*cl + 2 < max) {
4539 			*p++ = '\\';
4540 			*p++ = 'n';
4541 			*cl += 2;
4542 			*buf = p;
4543 			return (2);
4544 		} else {
4545 			return (0);
4546 		}
4547 	} else if (c < 0200) {
4548 		/* ascii control character */
4549 		if (*cl + 2 < max) {
4550 			*p++ = '^';
4551 			*p++ = c ^ 0100;
4552 			*cl += 2;
4553 			*buf = p;
4554 			return (2);
4555 		} else {
4556 			return (0);
4557 		}
4558 	} else {
4559 		if (*cl + 4 < max) {
4560 			*p++ = '\\';
4561 			*p++ = ((c >> 6) & 07) + '0';
4562 			*p++ = ((c >> 3) & 07) + '0';
4563 			*p++ = (c & 07) + '0';
4564 			*cl += 4;
4565 			*buf = p;
4566 			return (4);
4567 		} else {
4568 			return (0);
4569 		}
4570 	}
4571 }
4572 
4573 /*
4574  * findnl_bkwd:
4575  *	Scans each character in buf until it finds the last newline in buf,
4576  *	or the scanned character becomes the last COMPLETE character in buf.
4577  *	Returns the number of scanned bytes.
4578  *
4579  *	buf - pointer to a buffer containing the message string
4580  *	len - the length of the buffer
4581  */
4582 size_t
4583 findnl_bkwd(const char *buf, const size_t len)
4584 {
4585 	const char *p;
4586 	size_t	mb_cur_max;
4587 	pthread_t mythreadno;
4588 
4589 	if (Debug) {
4590 		mythreadno = pthread_self();
4591 	}
4592 
4593 	if (len == 0) {
4594 		return (0);
4595 	}
4596 
4597 	mb_cur_max = MB_CUR_MAX;
4598 
4599 	if (mb_cur_max == 1) {
4600 		/* single-byte locale */
4601 		for (p = buf + len - 1; p != buf; p--) {
4602 			if (*p == '\n') {
4603 				return ((size_t)(p - buf));
4604 			}
4605 		}
4606 		return ((size_t)len);
4607 	} else {
4608 		/* multi-byte locale */
4609 		int mlen;
4610 		const char *nl;
4611 		size_t	rem;
4612 
4613 		p = buf;
4614 		nl = NULL;
4615 		for (rem = len; rem >= mb_cur_max; ) {
4616 			mlen = mblen(p, mb_cur_max);
4617 			if (mlen == -1) {
4618 				/*
4619 				 * Invalid character found.
4620 				 */
4621 				DPRINT1(9, "findnl_bkwd(%u): Invalid MB "
4622 				    "sequence\n", mythreadno);
4623 				/*
4624 				 * handle as a single byte character.
4625 				 */
4626 				p++;
4627 				rem--;
4628 			} else {
4629 				/*
4630 				 * It's guaranteed that *p points to
4631 				 * the 1st byte of a multibyte character.
4632 				 */
4633 				if (*p == '\n') {
4634 					nl = p;
4635 				}
4636 				p += mlen;
4637 				rem -= mlen;
4638 			}
4639 		}
4640 		if (nl) {
4641 			return ((size_t)(nl - buf));
4642 		}
4643 		/*
4644 		 * no newline nor null byte found.
4645 		 * Also it's guaranteed that *p points to
4646 		 * the 1st byte of a (multibyte) character
4647 		 * at this point.
4648 		 */
4649 		return (len - rem);
4650 	}
4651 }
4652 
4653 /*
4654  * copynl_frwd:
4655  *	Scans each character in buf and copies the scanned character to obuf
4656  *	until it finds a null byte or a newline, or
4657  *	the number of the remaining bytes in obuf gets to exceed obuflen
4658  *	if copying the scanned character to obuf.
4659  *	Returns the number of scanned bytes.
4660  *
4661  *	obuf - buffer to be copied the scanned character
4662  *	obuflen - the size of obuf
4663  *	buf - pointer to a buffer containing the message string
4664  *	len - the length of the buffer
4665  */
4666 size_t
4667 copynl_frwd(char *obuf, const size_t obuflen,
4668 	    const char *buf, const size_t len)
4669 {
4670 	const char *p;
4671 	char	*q = obuf;
4672 	size_t	olen = 0;
4673 	size_t	mb_cur_max;
4674 	pthread_t mythreadno;
4675 
4676 	if (Debug) {
4677 		mythreadno = pthread_self();
4678 	}
4679 
4680 	if (len == 0) {
4681 		return (0);
4682 	}
4683 
4684 	mb_cur_max = MB_CUR_MAX;
4685 
4686 	if (mb_cur_max == 1) {
4687 		/* single-byte locale */
4688 		for (p = buf; *p; ) {
4689 			if (obuflen > olen + 1) {
4690 				if (*p != '\n') {
4691 					*q++ = *p++;
4692 					olen++;
4693 				} else {
4694 					*q = '\0';
4695 					return ((size_t)(p - buf));
4696 				}
4697 			} else {
4698 				*q = '\0';
4699 				return ((size_t)(p - buf));
4700 			}
4701 		}
4702 		*q = '\0';
4703 		return ((size_t)(p - buf));
4704 	} else {
4705 		/* multi-byte locale */
4706 		int mlen;
4707 
4708 		for (p = buf; *p; ) {
4709 			mlen = mblen(p, mb_cur_max);
4710 			if (mlen == -1) {
4711 				/*
4712 				 * Invalid character found.
4713 				 */
4714 				DPRINT1(9, "copynl_frwd(%u): Invalid MB "
4715 				    "sequence\n", mythreadno);
4716 				/*
4717 				 * handle as a single byte character.
4718 				 */
4719 				if (obuflen > olen + 1) {
4720 					*q++ = *p++;
4721 					olen++;
4722 				} else {
4723 					*q = '\0';
4724 					return ((size_t)(p - buf));
4725 				}
4726 			} else {
4727 				/*
4728 				 * It's guaranteed that *p points to
4729 				 * the 1st byte of a multibyte character.
4730 				 */
4731 				if (*p == '\n') {
4732 					*q = '\0';
4733 					return ((size_t)(p - buf));
4734 				}
4735 				if (obuflen > olen + mlen) {
4736 					int	n;
4737 					for (n = 0; n < mlen; n++) {
4738 						*q++ = *p++;
4739 					}
4740 					olen += mlen;
4741 				} else {
4742 					*q = '\0';
4743 					return ((size_t)(p - buf));
4744 				}
4745 			}
4746 		}
4747 		/*
4748 		 * no newline nor null byte found.
4749 		 * Also it's guaranteed that *p points to
4750 		 * the 1st byte of a (multibyte) character
4751 		 * at this point.
4752 		 */
4753 		*q = '\0';
4754 		return ((size_t)(p - buf));
4755 	}
4756 }
4757 
4758 /*
4759  * copy_frwd:
4760  *	Scans each character in buf and copies the scanned character to obuf
4761  *	until the number of the remaining bytes in obuf gets to exceed obuflen
4762  *	if copying the scanned character to obuf.
4763  *	Returns the number of scanned (copied) bytes.
4764  *
4765  *	obuf - buffer to be copied the scanned character
4766  *	obuflen - the size of obuf
4767  *	buf - pointer to a buffer containing the message string
4768  *	len - the length of the buffer
4769  */
4770 size_t
4771 copy_frwd(char *obuf, const size_t obuflen,
4772 	const char *buf, const size_t len)
4773 {
4774 	const char *p;
4775 	char	*q = obuf;
4776 	size_t	olen = 0;
4777 	size_t	mb_cur_max;
4778 	pthread_t mythreadno;
4779 
4780 	if (Debug) {
4781 		mythreadno = pthread_self();
4782 	}
4783 
4784 	if (len == 0) {
4785 		return (0);
4786 	}
4787 
4788 	mb_cur_max = MB_CUR_MAX;
4789 
4790 	if (mb_cur_max == 1) {
4791 		/* single-byte locale */
4792 		if (obuflen > len) {
4793 			(void) memcpy(obuf, buf, len);
4794 			obuf[len] = '\0';
4795 			return ((size_t)len);
4796 		} else {
4797 			(void) memcpy(obuf, buf, obuflen - 1);
4798 			obuf[obuflen - 1] = '\0';
4799 			return (obuflen - 1);
4800 		}
4801 	} else {
4802 		/* multi-byte locale */
4803 		int mlen;
4804 
4805 		for (p = buf; *p; ) {
4806 			mlen = mblen(p, mb_cur_max);
4807 			if (mlen == -1) {
4808 				/*
4809 				 * Invalid character found.
4810 				 */
4811 				DPRINT1(9, "copy_frwd(%u): Invalid MB "
4812 				    "sequence\n", mythreadno);
4813 				/*
4814 				 * handle as a single byte character.
4815 				 */
4816 				if (obuflen > olen + 1) {
4817 					*q++ = *p++;
4818 					olen++;
4819 				} else {
4820 					*q = '\0';
4821 					return ((size_t)(p - buf));
4822 				}
4823 			} else {
4824 				if (obuflen > olen + mlen) {
4825 					int	n;
4826 					for (n = 0; n < mlen; n++) {
4827 						*q++ = *p++;
4828 					}
4829 					olen += mlen;
4830 				} else {
4831 					*q = '\0';
4832 					return ((size_t)(p - buf));
4833 				}
4834 			}
4835 		}
4836 		*q = '\0';
4837 		return ((size_t)(p - buf));
4838 	}
4839 }
4840 
4841 /*
4842  * properties:
4843  *	Get properties from SMF framework.
4844  */
4845 static void
4846 properties(void)
4847 {
4848 	scf_simple_prop_t *prop;
4849 	uint8_t *bool;
4850 
4851 	if ((prop = scf_simple_prop_get(NULL, NULL, "config",
4852 	    "log_from_remote")) != NULL) {
4853 		if ((bool = scf_simple_prop_next_boolean(prop)) != NULL) {
4854 			if (*bool == 0)
4855 				turnoff = 1; /* log_from_remote = false */
4856 			else
4857 				turnoff = 0; /* log_from_remote = true */
4858 		}
4859 		scf_simple_prop_free(prop);
4860 		DPRINT1(1, "properties: setting turnoff to %s\n",
4861 		    turnoff ? "true" : "false");
4862 	}
4863 }
4864 
4865 /*
4866  * close all the input devices.
4867  */
4868 static void
4869 shutdown_input(void)
4870 {
4871 	int cnt;
4872 
4873 	shutting_down = 1;
4874 
4875 	for (cnt = 0; cnt < Ninputs; cnt++) {
4876 		(void) t_close(Nfd[cnt].fd);
4877 	}
4878 
4879 	(void) close(Pfd.fd);
4880 }
4881 
4882 /*
4883  * This is for the one thread that dedicates to resolve the
4884  * hostname. This will get the messages from net_poll() through
4885  * hnlq, and resolve the hostname, and push the messages back
4886  * into the inputq.
4887  */
4888 /*ARGSUSED*/
4889 static void *
4890 hostname_lookup(void *ap)
4891 {
4892 	char *uap;
4893 	log_message_t *mp;
4894 	host_info_t *hip;
4895 	char failsafe_addr[SYS_NMLN + 1];
4896 	pthread_t mythreadno;
4897 
4898 	if (Debug) {
4899 		mythreadno = pthread_self();
4900 	}
4901 
4902 	DPRINT1(1, "hostname_lookup(%u): hostname_lookup started\n",
4903 	    mythreadno);
4904 
4905 	for (;;) {
4906 		(void) dataq_dequeue(&hnlq, (void **)&mp, 0);
4907 
4908 		DPRINT3(5, "hostname_lookup(%u): dequeued msg %p"
4909 		    " from queue %p\n", mythreadno, (void *)mp,
4910 		    (void *)&hnlq);
4911 
4912 		hip = (host_info_t *)mp->ptr;
4913 		if ((uap = taddr2uaddr(hip->ncp, &hip->addr)) != NULL) {
4914 			(void) strlcpy(failsafe_addr, uap, SYS_NMLN);
4915 			free(uap);
4916 		} else {
4917 			(void) strlcpy(failsafe_addr, "<unknown>", SYS_NMLN);
4918 		}
4919 
4920 		mp->hlp = cvthname(&hip->addr, hip->ncp, failsafe_addr);
4921 
4922 		if (mp->hlp == NULL) {
4923 			mp->hlp = &NullHostName;
4924 		}
4925 
4926 		free(hip->addr.buf);
4927 		free(hip);
4928 		mp->ptr = NULL;
4929 
4930 		if (dataq_enqueue(&inputq, (void *)mp) == -1) {
4931 			MALLOC_FAIL("dropping message from remote");
4932 			free_msg(mp);
4933 			continue;
4934 		}
4935 
4936 		DPRINT3(5, "hostname_lookup(%u): enqueued msg %p on queue "
4937 		    "%p\n", mythreadno, (void *)mp, (void *)&inputq);
4938 	}
4939 
4940 	/*NOTREACHED*/
4941 	return (NULL);
4942 }
4943 
4944 /*
4945  * Does all HUP(re-configuration) process.
4946  */
4947 static void
4948 reconfigure()
4949 {
4950 	int cnt, loop, drops;
4951 	int really_stuck;
4952 	int console_stuck = 0;
4953 	struct filed *f;
4954 	char buf[LINE_MAX];
4955 	struct utsname up;
4956 	char cbuf[30];
4957 	time_t tim;
4958 	pthread_t mythreadno;
4959 
4960 	if (Debug) {
4961 		mythreadno = pthread_self();
4962 	}
4963 
4964 	/* If we get here then we must need to regen */
4965 	flushmsg(0);
4966 
4967 	if (logmymsg(LOG_SYSLOG|LOG_INFO, "syslogd: configuration restart",
4968 	    ADDDATE, 0) == -1) {
4969 		MALLOC_FAIL("dropping message");
4970 	}
4971 
4972 	/*
4973 	 * make sure the logmsg thread is not in the waiting state.
4974 	 * Otherwise, changing hup_state will prevent the logmsg thread
4975 	 * getting out from the waiting loop.
4976 	 */
4977 
4978 	if (Debug) {
4979 		tim = time(NULL);
4980 		DPRINT2(3, "reconfigure(%u): %.15s: awaiting logmsg()"
4981 		    " moving to the safe place\n",
4982 		    mythreadno, ctime_r(&tim, cbuf)+4);
4983 	}
4984 
4985 	for (loop = 0; loop < LOOP_MAX; loop++) {
4986 		/* we don't need the mutex to read */
4987 		if (hup_state == HUP_ACCEPTABLE)
4988 			break;
4989 		(void) sleep(1);
4990 	}
4991 	if (hup_state != HUP_ACCEPTABLE) {
4992 		goto thread_stuck;
4993 	}
4994 
4995 	if (Debug) {
4996 		tim = time(NULL);
4997 		DPRINT2(3, "reconfigure(%u): %.15s: logmsg() will accept HUP\n",
4998 		    mythreadno, ctime_r(&tim, cbuf)+4);
4999 	}
5000 
5001 	/*
5002 	 * Prevent logging until we are truly done processing the HUP
5003 	 */
5004 	(void) pthread_mutex_lock(&hup_lock);
5005 	hup_state = HUP_INPROGRESS;
5006 	(void) pthread_mutex_unlock(&hup_lock);
5007 
5008 	/*
5009 	 * We will be going into a critical state. Any error message
5010 	 * from syslogd needs to be dumped to the console by default
5011 	 * immediately. Also, those error messages are quened in a temporary
5012 	 * queue to be able to post into the regular stream later.
5013 	 */
5014 	disable_errorlog();
5015 
5016 	if (Debug) {
5017 		tim = time(NULL);
5018 		DPRINT2(3, "reconfigure(%u): %.15s: sending SHUTDOWN\n",
5019 		    mythreadno, ctime_r(&tim, cbuf)+4);
5020 	}
5021 
5022 	/* stop configured threads */
5023 	if (shutdown_msg() == -1) {
5024 		/*
5025 		 * No memory, message will be dumped to the console.
5026 		 */
5027 		MALLOC_FAIL("unable to restart syslogd");
5028 		goto out;
5029 	}
5030 
5031 	/* make sure logmsg() is in suspended state */
5032 	for (loop = 0; loop < LOOP_INTERVAL; loop++) {
5033 		if (hup_state & HUP_LOGMSG_SUSPENDED)
5034 			break;
5035 		(void) sleep(1);
5036 	}
5037 
5038 	if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
5039 		if (Debug) {
5040 			tim = time(NULL);
5041 			DPRINT2(3, "reconfigure(%u): %.15s: logmsg() does not "
5042 			    "stop. enforcing\n",
5043 			    mythreadno, ctime_r(&tim, cbuf)+4);
5044 		}
5045 
5046 		/* probably we have too long input queue, or really stuck */
5047 		(void) pthread_mutex_lock(&hup_lock);
5048 		hup_state |= HUP_SUSP_LOGMSG_REQD;
5049 		(void) pthread_mutex_unlock(&hup_lock);
5050 
5051 		for (loop = 0; loop < LOOP_MAX; loop++) {
5052 			if (hup_state & HUP_LOGMSG_SUSPENDED)
5053 				break;
5054 			(void) sleep(1);
5055 		}
5056 		if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
5057 			if (Debug) {
5058 				tim = time(NULL);
5059 				DPRINT2(3, "reconfigure(%u): %.15s: logmsg()"
5060 				    " does not stop. give up\n",
5061 				    mythreadno, ctime_r(&tim, cbuf)+4);
5062 			}
5063 			logerror("could not suspend logmsg - fatal");
5064 			goto thread_stuck;
5065 		}
5066 	}
5067 
5068 	if (Debug) {
5069 		tim = time(NULL);
5070 		DPRINT2(3, "reconfigure(%u): %.15s: logmsg() suspended\n",
5071 		    mythreadno, ctime_r(&tim, cbuf)+4);
5072 	}
5073 
5074 	/*
5075 	 * Will wait for LOOP_MAX secs with watching queue lengths for the
5076 	 * each logger threads. If they have backlogs, and no change in the
5077 	 * length of queue found in 30 seconds, those will be counted as
5078 	 * "really stuck".
5079 	 * If all running logger threads become "really stuck" state, there
5080 	 * should be no worth waiting for them to quit.
5081 	 * In that case, we will go ahead and close out file descriptors to
5082 	 * have them pull out from hanging system call, and give them a last
5083 	 * chance(LOOP_INTERVAL sec) to quit.
5084 	 */
5085 
5086 	if (Debug) {
5087 		tim = time(NULL);
5088 		DPRINT2(3, "reconfigure(%u): %.15s: awaiting logit() to be"
5089 		    " shutdown\n", mythreadno, ctime_r(&tim, cbuf)+4);
5090 	}
5091 
5092 	cnt = 0;
5093 	really_stuck = 0;
5094 	while (cnt < (LOOP_MAX/LOOP_INTERVAL) &&
5095 	    conf_threads > really_stuck) {
5096 
5097 		/* save initial queue count */
5098 		for (f = Files; f < &Files[nlogs]; f++) {
5099 			f->f_prev_queue_count = (f->f_type == F_UNUSED) ?
5100 			    -1 : f->f_queue_count;
5101 		}
5102 
5103 		for (loop = 0; loop < LOOP_INTERVAL; loop++) {
5104 			if (conf_threads == 0)
5105 				break;
5106 			(void) sleep(1);
5107 		}
5108 
5109 		if (conf_threads == 0)
5110 			break;
5111 
5112 		if (Debug) {
5113 			tim = time(NULL);
5114 			DPRINT3(3, "reconfigure(%u): %.15s: "
5115 			    "%d threads are still alive.\n",
5116 			    mythreadno, ctime_r(&tim, cbuf)+4,
5117 			    conf_threads);
5118 		}
5119 
5120 		really_stuck = 0;
5121 		for (f = Files; f < &Files[nlogs]; f++) {
5122 			if (f->f_type == F_UNUSED) {
5123 				f->f_prev_queue_count = -1;
5124 				continue;
5125 			}
5126 			if (f->f_prev_queue_count == f->f_queue_count) {
5127 				really_stuck++;
5128 				f->f_prev_queue_count = 1;
5129 				DPRINT2(3, "reconfigure(%u): "
5130 				    "tid=%d is really stuck.\n",
5131 				    mythreadno, f->f_thread);
5132 			} else {
5133 				f->f_prev_queue_count = 0;
5134 				DPRINT2(3, "reconfigure(%u): "
5135 				    "tid=%d is still active.\n",
5136 				    mythreadno, f->f_thread);
5137 			}
5138 		}
5139 		/*
5140 		 * Here we have one of following values in the
5141 		 * f_prev_queue_count:
5142 		 *  0: logger thread is still actively working.
5143 		 *  1: logger thread is really stuck.
5144 		 * -1: logger thread has already died.
5145 		 */
5146 
5147 		cnt++;
5148 	}
5149 
5150 	if (Debug) {
5151 		tim = time(NULL);
5152 		DPRINT2(3, "reconfigure(%u): %.15s:"
5153 		    " complete awaiting logit()\n",
5154 		    mythreadno, ctime_r(&tim, cbuf)+4);
5155 		DPRINT3(3, "reconfigure(%u): %d threads alive."
5156 		    " %d threads stuck\n",
5157 		    mythreadno, conf_threads, really_stuck);
5158 	}
5159 
5160 	/*
5161 	 * Still running? If so, mark it as UNUSED, and close
5162 	 * the fd so that logger threads can bail out from the loop.
5163 	 */
5164 	drops = 0;
5165 	if (conf_threads) {
5166 		for (f = Files; f < &Files[nlogs]; f++) {
5167 			if (f->f_type == F_CONSOLE &&
5168 			    f->f_prev_queue_count == 1) {
5169 				/* console is really stuck */
5170 				console_stuck = 1;
5171 			}
5172 			if (f->f_type == F_USERS || f->f_type == F_WALL ||
5173 			    f->f_type == F_UNUSED)
5174 				continue;
5175 			cnt = f->f_queue_count;
5176 			drops += (cnt > 0) ? cnt - 1: 0;
5177 			f->f_type = F_UNUSED;
5178 
5179 			if (f->f_orig_type == F_FORW)
5180 				(void) t_close(f->f_file);
5181 			else
5182 				(void) close(f->f_file);
5183 		}
5184 
5185 		if (Debug) {
5186 			tim = time(NULL);
5187 			DPRINT1(3, "reconfigure(%u): terminating logit()\n",
5188 			    mythreadno);
5189 		}
5190 
5191 		/* last chance to exit */
5192 		for (loop = 0; loop < LOOP_MAX; loop++) {
5193 			if (conf_threads == 0)
5194 				break;
5195 			(void) sleep(1);
5196 		}
5197 
5198 		if (Debug) {
5199 			tim = time(NULL);
5200 			DPRINT3(3, "reconfigure(%u): %.15s: %d alive\n",
5201 			    mythreadno, ctime_r(&tim, cbuf)+4,
5202 			    conf_threads);
5203 		}
5204 	}
5205 
5206 	if (conf_threads == 0 && drops) {
5207 		errno = 0;
5208 		logerror("Could not completely output pending messages"
5209 		    " while preparing re-configuration");
5210 		logerror("discarded %d messages and restart configuration.",
5211 		    drops);
5212 		if (Debug) {
5213 			tim = time(NULL);
5214 			DPRINT3(3, "reconfigure(%u): %.15s: "
5215 			    "discarded %d messages\n",
5216 			    mythreadno, ctime_r(&tim, cbuf)+4, drops);
5217 		}
5218 	}
5219 
5220 	/*
5221 	 * If all threads still haven't exited
5222 	 * something is stuck or hosed. We just
5223 	 * have no option but to exit.
5224 	 */
5225 	if (conf_threads) {
5226 thread_stuck:
5227 		if (Debug) {
5228 			tim = time(NULL);
5229 			DPRINT2(3, "reconfigure(%u): %.15s: really stuck\n",
5230 			    mythreadno, ctime_r(&tim, cbuf)+4);
5231 		}
5232 
5233 		shutdown_input();
5234 		delete_doorfiles();
5235 		(void) uname(&up);
5236 
5237 		(void) snprintf(buf, sizeof (buf),
5238 		    "syslogd(%s): some logger thread(s) "
5239 		    "are stuck%s; syslogd is shutting down.",
5240 		    up.nodename,
5241 		    console_stuck ? " (including the console)" : "");
5242 
5243 		if (console_stuck) {
5244 			FILE *m = popen(MAILCMD, "w");
5245 
5246 			if (m != NULL) {
5247 				(void) fprintf(m, "%s\n", buf);
5248 				(void) pclose(m);
5249 			}
5250 		}
5251 
5252 		disable_errorlog();
5253 		logerror(buf);
5254 		exit(1);
5255 	}
5256 
5257 	/* Free up some resources */
5258 	if (Files != (struct filed *)&fallback) {
5259 		for (f = Files; f < &Files[nlogs]; f++) {
5260 			(void) pthread_join(f->f_thread, NULL);
5261 			filed_destroy(f);
5262 		}
5263 		free(Files);
5264 	}
5265 
5266 	dealloc_stacks(nlogs);
5267 
5268 	if (Debug) {
5269 		tim = time(NULL);
5270 		DPRINT2(3, "reconfigure(%u): %.15s: cleanup complete\n",
5271 		    mythreadno, ctime_r(&tim, cbuf)+4);
5272 	}
5273 
5274 	hnc_init(1);	/* purge hostname cache */
5275 	conf_init();	/* start reconfigure */
5276 
5277 out:;
5278 	/* Now should be ready to dispatch error messages from syslogd. */
5279 	enable_errorlog();
5280 
5281 	/* Wake up the log thread */
5282 
5283 	if (Debug) {
5284 		tim = time(NULL);
5285 		DPRINT2(3, "reconfigure(%u): %.15s: resuming logmsg()\n",
5286 		    mythreadno, ctime_r(&tim, cbuf)+4);
5287 	}
5288 
5289 	(void) pthread_mutex_lock(&hup_lock);
5290 	hup_state = HUP_COMPLETED;
5291 	(void) pthread_cond_signal(&hup_done);
5292 	(void) pthread_mutex_unlock(&hup_lock);
5293 }
5294 
5295 /*
5296  * The following function implements simple hostname cache mechanism.
5297  * Host name cache is implemented through hash table bucket chaining method.
5298  * Collisions are handled by bucket chaining.
5299  *
5300  * hnc_init():
5301  * 	allocate and initialize the cache. If reinit is set,
5302  *	invalidate all cache entries.
5303  * hnc_look():
5304  *	It hashes the ipaddress gets the index and walks thru the
5305  *	single linked list. if cached entry was found, it will
5306  *	put in the head of the list, and return.While going through
5307  *	the entries, an entry which has already expired will be invalidated.
5308  * hnc_register():
5309  *	Hashes the ipaddress finds the index and puts current entry to the list.
5310  * hnc_unreg():
5311  *	invalidate the cachep.
5312  */
5313 
5314 static void
5315 hnc_init(int reinit)
5316 {
5317 	struct hostname_cache **hpp;
5318 	pthread_t mythreadno;
5319 	int i;
5320 
5321 	if (Debug) {
5322 		mythreadno = pthread_self();
5323 	}
5324 
5325 	if (reinit) {
5326 		(void) pthread_mutex_lock(&hnc_mutex);
5327 
5328 		for (i = 0; i < hnc_size; i++) {
5329 			for (hpp = &hnc_cache[i]; *hpp != NULL; ) {
5330 				hnc_unreg(hpp);
5331 			}
5332 		}
5333 
5334 		(void) pthread_mutex_unlock(&hnc_mutex);
5335 		DPRINT1(2, "hnc_init(%u): hostname cache re-configured\n",
5336 		    mythreadno);
5337 	} else {
5338 
5339 		hnc_cache = calloc(hnc_size, sizeof (struct hostname_cache *));
5340 
5341 		if (hnc_cache == NULL) {
5342 			MALLOC_FAIL("hostname cache");
5343 			logerror("hostname cache disabled");
5344 			return;
5345 		}
5346 
5347 		DPRINT3(1, "hnc_init(%u): hostname cache configured %d entry"
5348 		    " ttl:%d\n", mythreadno, hnc_size, hnc_ttl);
5349 	}
5350 }
5351 
5352 static host_list_t *
5353 hnc_lookup(struct netbuf *nbp, struct netconfig *ncp, int *hindex)
5354 {
5355 	struct hostname_cache **hpp, *hp;
5356 	time_t now;
5357 	pthread_t mythreadno;
5358 	int index;
5359 
5360 	if (Debug) {
5361 		mythreadno = pthread_self();
5362 	}
5363 
5364 	if (hnc_cache == NULL) {
5365 		return (NULL);
5366 	}
5367 
5368 	(void) pthread_mutex_lock(&hnc_mutex);
5369 	now = time(0);
5370 
5371 	*hindex = index = addr_hash(nbp);
5372 
5373 	for (hpp = &hnc_cache[index]; (hp = *hpp) != NULL; ) {
5374 		DPRINT4(10, "hnc_lookup(%u): check %p on %p for %s\n",
5375 		    mythreadno, (void *)hp->h, (void *)hp,
5376 		    hp->h->hl_hosts[0]);
5377 
5378 		if (hp->expire < now) {
5379 			DPRINT2(9, "hnc_lookup(%u): purge %p\n",
5380 			    mythreadno, (void *)hp);
5381 			hnc_unreg(hpp);
5382 			continue;
5383 		}
5384 
5385 		if (ncp == hp->ncp && same_addr(&hp->addr, nbp)) {
5386 			/*
5387 			 * found!
5388 			 * Put the entry at the top.
5389 			 */
5390 
5391 			if (hp != hnc_cache[index]) {
5392 				/* unlink from active list */
5393 				*hpp = (*hpp)->next;
5394 				/* push it onto the top */
5395 				hp->next = hnc_cache[index];
5396 				hnc_cache[index] = hp;
5397 			}
5398 
5399 			(void) pthread_mutex_lock(&hp->h->hl_mutex);
5400 			hp->h->hl_refcnt++;
5401 			(void) pthread_mutex_unlock(&hp->h->hl_mutex);
5402 
5403 			DPRINT4(9, "hnc_lookup(%u): found %p on %p for %s\n",
5404 			    mythreadno, (void *)hp->h, (void *)hp,
5405 			    hp->h->hl_hosts[0]);
5406 
5407 			(void) pthread_mutex_unlock(&hnc_mutex);
5408 			return (hp->h);
5409 		}
5410 
5411 		hpp = &hp->next;
5412 	}
5413 
5414 	(void) pthread_mutex_unlock(&hnc_mutex);
5415 	return (NULL);
5416 }
5417 
5418 static void
5419 hnc_register(struct netbuf *nbp, struct netconfig *ncp,
5420 		    host_list_t *h, int hindex)
5421 {
5422 	struct hostname_cache **hpp, **tailp, *hp, *entry;
5423 	void *addrbuf;
5424 	time_t now;
5425 	pthread_t mythreadno;
5426 	int i;
5427 
5428 	if (Debug) {
5429 		mythreadno = pthread_self();
5430 	}
5431 
5432 	if (hnc_cache == NULL) {
5433 		return;
5434 	}
5435 
5436 	if ((addrbuf = malloc(nbp->len)) == NULL) {
5437 		MALLOC_FAIL("pushing hostname cache");
5438 		return;
5439 	}
5440 
5441 	if ((entry = malloc(sizeof (struct hostname_cache))) == NULL) {
5442 		MALLOC_FAIL("pushing hostname entry");
5443 		free(addrbuf);
5444 		return;
5445 	}
5446 
5447 	(void) pthread_mutex_lock(&hnc_mutex);
5448 
5449 	i = 0;
5450 
5451 	now = time(0);
5452 	/*
5453 	 * first go through active list, and discard the
5454 	 * caches which has been invalid. Count number of
5455 	 * non-expired buckets.
5456 	 */
5457 
5458 	for (hpp = &hnc_cache[hindex]; (hp = *hpp) != NULL; ) {
5459 		tailp = hpp;
5460 
5461 		if (hp->expire < now) {
5462 			DPRINT2(9, "hnc_register(%u): discard %p\n",
5463 			    mythreadno, (void *)hp);
5464 			hnc_unreg(hpp);
5465 		} else {
5466 			i++;
5467 			hpp = &hp->next;
5468 		}
5469 	}
5470 
5471 	/*
5472 	 * If max limit of chained hash buckets has been used up
5473 	 * delete the least active element in the chain.
5474 	 */
5475 	if (i == MAX_BUCKETS) {
5476 		hnc_unreg(tailp);
5477 	}
5478 
5479 	(void) memcpy(addrbuf, nbp->buf, nbp->len);
5480 	entry->addr.len = nbp->len;
5481 	entry->addr.buf = addrbuf;
5482 	entry->ncp = ncp;
5483 	entry->h = h;
5484 	entry->expire = time(NULL) + hnc_ttl;
5485 
5486 	/* insert it at the top */
5487 	entry->next = hnc_cache[hindex];
5488 	hnc_cache[hindex] = entry;
5489 
5490 	/*
5491 	 * As far as cache is valid, corresponding host_list must
5492 	 * also be valid. Increments the refcnt to avoid freeing
5493 	 * host_list.
5494 	 */
5495 	h->hl_refcnt++;
5496 	DPRINT4(9, "hnc_register(%u): reg %p onto %p for %s\n",
5497 	    mythreadno, (void *)entry->h, (void *)entry, entry->h->hl_hosts[0]);
5498 	(void) pthread_mutex_unlock(&hnc_mutex);
5499 }
5500 
5501 static void
5502 hnc_unreg(struct hostname_cache **hpp)
5503 {
5504 	struct hostname_cache *hp = *hpp;
5505 	pthread_t mythreadno;
5506 
5507 	if (Debug) {
5508 		mythreadno = pthread_self();
5509 	}
5510 
5511 	DPRINT4(9, "hnc_unreg(%u): unreg %p on %p for %s\n",
5512 	    mythreadno, (void *)hp->h, (void *)hp, hp->h->hl_hosts[0]);
5513 	free(hp->addr.buf);
5514 	freehl(hp->h);
5515 
5516 	/* unlink from active list */
5517 	*hpp = (*hpp)->next;
5518 
5519 	free(hp);
5520 }
5521 
5522 /*
5523  * Once this is called, error messages through logerror() will go to
5524  * the console immediately. Also, messages are queued into the tmpq
5525  * to be able to later put them into inputq.
5526  */
5527 static void
5528 disable_errorlog()
5529 {
5530 	(void) dataq_init(&tmpq);
5531 
5532 	(void) pthread_mutex_lock(&logerror_lock);
5533 	interrorlog = 0;
5534 	(void) pthread_mutex_unlock(&logerror_lock);
5535 }
5536 
5537 /*
5538  * Turn internal error messages to regular input stream.
5539  * All pending messages are pulled and pushed into the regular
5540  * input queue.
5541  */
5542 static void
5543 enable_errorlog()
5544 {
5545 	log_message_t *mp;
5546 
5547 	(void) pthread_mutex_lock(&logerror_lock);
5548 	interrorlog = 1;
5549 	(void) pthread_mutex_unlock(&logerror_lock);
5550 
5551 	/*
5552 	 * push all the pending messages into inputq.
5553 	 */
5554 	while (dataq_dequeue(&tmpq, (void **)&mp, 1) == 0) {
5555 		(void) dataq_enqueue(&inputq, mp);
5556 	}
5557 	(void) dataq_destroy(&tmpq);
5558 }
5559 
5560 /*
5561  * Generate a hash value of the given address and derive
5562  * an index into the hnc_cache hashtable.
5563  * The hashing method is similar to what Java does for strings.
5564  */
5565 static int
5566 addr_hash(struct netbuf *nbp)
5567 {
5568 	char *uap;
5569 	int i;
5570 	unsigned long hcode = 0;
5571 
5572 	uap = nbp->buf;
5573 
5574 	if (uap == NULL) {
5575 		return (0);
5576 	}
5577 
5578 	/*
5579 	 * Compute a hashcode of the address string
5580 	 */
5581 	for (i = 0; i < nbp->len; i++)
5582 		hcode = (31 * hcode) + uap[i];
5583 
5584 	/*
5585 	 * Scramble the hashcode for better distribution
5586 	 */
5587 	hcode += ~(hcode << 9);
5588 	hcode ^=  (hcode >> 14);
5589 	hcode +=  (hcode << 4);
5590 	hcode ^=  (hcode >> 10);
5591 
5592 	return ((int)(hcode % hnc_size));
5593 }
5594