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