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