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