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