Merge commit 'b1e7e97d3b60469b243b3b2e22c7d8cbd11c7c90'
[unleashed.git] / usr / src / cmd / syslogd / syslogd.c
blob17f2219a88f79169d1f7e5279f5b6cab1df99fb7
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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
29 * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
30 * All Rights Reserved
34 * University Copyright- Copyright (c) 1982, 1986, 1988
35 * The Regents of the University of California
36 * All Rights Reserved
38 * University Acknowledgment- Portions of this document are derived from
39 * software developed by the University of California, Berkeley, and its
40 * contributors.
44 * syslogd -- log system messages
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.
51 * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
52 * cause it to reconfigure.
54 * Defined Constants:
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.
62 #include <unistd.h>
63 #include <errno.h>
64 #include <sys/types.h>
65 #include <stdio.h>
66 #include <stdio_ext.h>
67 #include <stdlib.h>
68 #include <ctype.h>
69 #include <signal.h>
70 #include <string.h>
71 #include <strings.h>
72 #include <libscf.h>
73 #include <netconfig.h>
74 #include <netdir.h>
75 #include <pwd.h>
76 #include <sys/socket.h>
77 #include <tiuser.h>
78 #include <utmpx.h>
79 #include <limits.h>
80 #include <pthread.h>
81 #include <fcntl.h>
82 #include <stropts.h>
83 #include <assert.h>
84 #include <sys/statvfs.h>
86 #include <sys/param.h>
87 #include <sys/sysmacros.h>
88 #include <sys/syslog.h>
89 #include <sys/strlog.h>
90 #include <sys/stat.h>
91 #include <sys/time.h>
92 #include <sys/utsname.h>
93 #include <sys/poll.h>
94 #include <sys/wait.h>
95 #include <sys/resource.h>
96 #include <sys/mman.h>
97 #include <door.h>
99 #include <wchar.h>
100 #include <locale.h>
101 #include <stdarg.h>
103 #include "dataq.h"
104 #include "conf.h"
105 #include "syslogd.h"
107 #define DOORFILE "/var/run/syslog_door"
108 #define RELATIVE_DOORFILE "../var/run/syslog_door"
109 #define OLD_DOORFILE "/etc/.syslog_door"
111 #define PIDFILE "/var/run/syslog.pid"
112 #define RELATIVE_PIDFILE "../var/run/syslog.pid"
113 #define OLD_PIDFILE "/etc/syslog.pid"
115 static char *LogName = "/dev/log";
116 static char *ConfFile = "/etc/syslog.conf";
117 static char ctty[] = "/dev/console";
118 static char sysmsg[] = "/dev/sysmsg";
119 static int DoorFd = -1;
120 static int DoorCreated = 0;
121 static int PidfileCreated = 0;
122 static char *DoorFileName = DOORFILE;
123 static char *PidFileName = PIDFILE;
126 * configuration file directives
129 static struct code PriNames[] = {
130 "panic", LOG_EMERG,
131 "emerg", LOG_EMERG,
132 "alert", LOG_ALERT,
133 "crit", LOG_CRIT,
134 "err", LOG_ERR,
135 "error", LOG_ERR,
136 "warn", LOG_WARNING,
137 "warning", LOG_WARNING,
138 "notice", LOG_NOTICE,
139 "info", LOG_INFO,
140 "debug", LOG_DEBUG,
141 "none", NOPRI,
142 NULL, -1
145 static struct code FacNames[] = {
146 "kern", LOG_KERN,
147 "user", LOG_USER,
148 "mail", LOG_MAIL,
149 "daemon", LOG_DAEMON,
150 "auth", LOG_AUTH,
151 "security", LOG_AUTH,
152 "mark", LOG_MARK,
153 "syslog", LOG_SYSLOG,
154 "lpr", LOG_LPR,
155 "news", LOG_NEWS,
156 "uucp", LOG_UUCP,
157 "altcron", LOG_ALTCRON,
158 "authpriv", LOG_AUTHPRIV,
159 "ftp", LOG_FTP,
160 "ntp", LOG_NTP,
161 "audit", LOG_AUDIT,
162 "console", LOG_CONSOLE,
163 "cron", LOG_CRON,
164 "local0", LOG_LOCAL0,
165 "local1", LOG_LOCAL1,
166 "local2", LOG_LOCAL2,
167 "local3", LOG_LOCAL3,
168 "local4", LOG_LOCAL4,
169 "local5", LOG_LOCAL5,
170 "local6", LOG_LOCAL6,
171 "local7", LOG_LOCAL7,
172 NULL, -1
175 static char *TypeNames[7] = {
176 "UNUSED", "FILE", "TTY", "CONSOLE",
177 "FORW", "USERS", "WALL"
181 * we allocate our own thread stacks so we can create them
182 * without the MAP_NORESERVE option. We need to be sure
183 * we have stack space even if the machine runs out of swap
186 #define DEFAULT_STACKSIZE (100 * 1024) /* 100 k stack */
187 #define DEFAULT_REDZONESIZE (8 * 1024) /* 8k redzone */
189 static pthread_mutex_t wmp = PTHREAD_MUTEX_INITIALIZER; /* wallmsg lock */
191 static pthread_mutex_t cft = PTHREAD_MUTEX_INITIALIZER;
192 static int conf_threads = 0;
194 static pthread_mutex_t hup_lock = PTHREAD_MUTEX_INITIALIZER;
195 static pthread_cond_t hup_done = PTHREAD_COND_INITIALIZER;
197 static pthread_mutex_t logerror_lock = PTHREAD_MUTEX_INITIALIZER;
199 #define HUP_ACCEPTABLE 0x0000 /* can start SIGHUP process */
200 #define HUP_INPROGRESS 0x0001 /* SIGHUP process in progress */
201 #define HUP_COMPLETED 0x0002 /* SIGHUP process completed */
202 #define HUP_SUSP_LOGMSG_REQD 0x1000 /* request to suspend */
203 #define HUP_LOGMSG_SUSPENDED 0x2000 /* logmsg is suspended */
204 static int hup_state = HUP_ACCEPTABLE;
206 static size_t stacksize; /* thread stack size */
207 static size_t redzonesize; /* thread stack redzone size */
208 static char *stack_ptr; /* ptr to allocated stacks */
209 static char *cstack_ptr; /* ptr to conf_thr stacks */
211 static time_t start_time;
213 static pthread_t sys_thread; /* queues messages from us */
214 static pthread_t net_thread; /* queues messages from the net */
215 static pthread_t log_thread; /* message processing thread */
216 static pthread_t hnl_thread; /* hostname lookup thread */
218 static dataq_t inputq; /* the input queue */
219 static dataq_t tmpq; /* temporary queue for err msg */
220 static dataq_t hnlq; /* hostname lookup queue */
222 static struct filed fallback[2];
223 static struct filed *Files;
224 static int nlogs;
225 static int Debug; /* debug flag */
226 static host_list_t LocalHostName; /* our hostname */
227 static host_list_t NullHostName; /* in case of lookup failure */
228 static int debuglev = 1; /* debug print level */
229 static int interrorlog; /* internal error logging */
231 static int MarkInterval = 20; /* interval between marks (mins) */
232 static int Marking = 0; /* non-zero if marking some file */
233 static int Ninputs = 0; /* number of network inputs */
234 static int curalarm = 0; /* current timeout value (secs) */
235 static int sys_msg_count = 0; /* total msgs rcvd from local log */
236 static int sys_init_msg_count = 0; /* initially received */
237 static int net_msg_count = 0; /* total msgs rcvd from net */
239 static struct pollfd Pfd; /* Pollfd for local the log device */
240 static struct pollfd *Nfd; /* Array of pollfds for udp ports */
241 static struct netconfig *Ncf;
242 static struct netbuf **Myaddrs;
243 static struct t_unitdata **Udp;
244 static struct t_uderr **Errp;
245 static int turnoff = 0;
246 static int shutting_down;
248 /* for managing door server threads */
249 static pthread_mutex_t door_server_cnt_lock = PTHREAD_MUTEX_INITIALIZER;
250 static uint_t door_server_cnt = 0;
251 static pthread_attr_t door_thr_attr;
253 static struct hostname_cache **hnc_cache;
254 static pthread_mutex_t hnc_mutex = PTHREAD_MUTEX_INITIALIZER;
255 static size_t hnc_size = DEF_HNC_SIZE;
256 static unsigned int hnc_ttl = DEF_HNC_TTL;
258 #define DPRINT0(d, m) if ((Debug) && debuglev >= (d)) \
259 (void) fprintf(stderr, m)
260 #define DPRINT1(d, m, a) if ((Debug) && debuglev >= (d)) \
261 (void) fprintf(stderr, m, a)
262 #define DPRINT2(d, m, a, b) if ((Debug) && debuglev >= (d)) \
263 (void) fprintf(stderr, m, a, b)
264 #define DPRINT3(d, m, a, b, c) if ((Debug) && debuglev >= (d)) \
265 (void) fprintf(stderr, m, a, b, c)
266 #define DPRINT4(d, m, a, b, c, e) if ((Debug) && debuglev >= (d)) \
267 (void) fprintf(stderr, m, a, b, c, e)
268 #define MALLOC_FAIL(x) \
269 logerror("malloc failed: " x)
270 #define MALLOC_FAIL_EXIT \
271 logerror("malloc failed - fatal"); \
272 exit(1)
275 #define MAILCMD "mailx -s \"syslogd shut down\" root"
278 * Number of seconds to wait before giving up on threads that won't
279 * shutdown: (that's right, 10 minutes!)
281 #define LOOP_MAX (10 * 60)
284 * Interval(sec) to check the status of output queue while processing
285 * HUP signal.
287 #define LOOP_INTERVAL (15)
290 main(int argc, char **argv)
292 int i;
293 char *pstr;
294 int sig, fd;
295 int tflag = 0, Tflag = 0;
296 sigset_t sigs, allsigs;
297 struct rlimit rlim;
298 char *debugstr;
299 int mcount = 0;
300 struct sigaction act;
301 pthread_t mythreadno = 0;
302 char cbuf [30];
303 struct stat sb;
305 #ifdef DEBUG
306 #define DEBUGDIR "/var/tmp"
307 if (chdir(DEBUGDIR))
308 DPRINT2(1, "main(%u): Unable to cd to %s\n", mythreadno,
309 DEBUGDIR);
310 #endif /* DEBUG */
312 (void) setlocale(LC_ALL, "");
314 if ((debugstr = getenv("SYSLOGD_DEBUG")) != NULL)
315 if ((debuglev = atoi(debugstr)) == 0)
316 debuglev = 1;
318 #if ! defined(TEXT_DOMAIN) /* should be defined by cc -D */
319 #define TEXT_DOMAIN "SYS_TEST"
320 #endif
321 (void) textdomain(TEXT_DOMAIN);
323 (void) time(&start_time);
325 if (lstat("/var/run", &sb) != 0 || !(S_ISDIR(sb.st_mode))) {
326 DoorFileName = OLD_DOORFILE;
327 PidFileName = OLD_PIDFILE;
330 properties();
332 while ((i = getopt(argc, argv, "df:p:m:tT")) != EOF) {
333 switch (i) {
334 case 'f': /* configuration file */
335 ConfFile = optarg;
336 break;
338 case 'd': /* debug */
339 Debug++;
340 break;
342 case 'p': /* path */
343 LogName = optarg;
344 break;
346 case 'm': /* mark interval */
347 for (pstr = optarg; *pstr; pstr++) {
348 if (! (isdigit(*pstr))) {
349 (void) fprintf(stderr,
350 "Illegal interval\n");
351 usage();
354 MarkInterval = atoi(optarg);
355 if (MarkInterval < 1 || MarkInterval > INT_MAX) {
356 (void) fprintf(stderr,
357 "Interval must be between 1 and %d\n",
358 INT_MAX);
359 usage();
361 break;
362 case 't': /* turn off remote reception */
363 tflag++;
364 turnoff++;
365 break;
366 case 'T': /* turn on remote reception */
367 Tflag++;
368 turnoff = 0;
369 break;
370 default:
371 usage();
375 if (optind < argc)
376 usage();
378 if (tflag && Tflag) {
379 (void) fprintf(stderr, "specify only one of -t and -T\n");
380 usage();
384 * close all fd's except 0-2
387 closefrom(3);
389 if (!Debug) {
390 if (fork())
391 return (0);
392 (void) close(0);
393 (void) open("/", O_RDONLY);
394 (void) dup2(0, 1);
395 (void) dup2(0, 2);
396 untty();
399 if (Debug) {
400 mythreadno = pthread_self();
404 * DO NOT call logerror() until tmpq is initialized.
406 disable_errorlog();
409 * ensure that file descriptor limit is "high enough"
411 (void) getrlimit(RLIMIT_NOFILE, &rlim);
412 if (rlim.rlim_cur < rlim.rlim_max)
413 rlim.rlim_cur = rlim.rlim_max;
414 if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
415 logerror("Unable to increase file descriptor limit.");
416 (void) enable_extended_FILE_stdio(-1, -1);
418 /* block all signals from all threads initially */
419 (void) sigfillset(&allsigs);
420 (void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL);
422 DPRINT2(1, "main(%u): Started at time %s", mythreadno,
423 ctime_r(&start_time, cbuf));
425 init(); /* read configuration, start threads */
427 DPRINT1(1, "main(%u): off & running....\n", mythreadno);
429 /* now set up to catch signals we care about */
431 (void) sigemptyset(&sigs);
432 (void) sigaddset(&sigs, SIGHUP); /* reconfigure */
433 (void) sigaddset(&sigs, SIGALRM); /* mark & flush timer */
434 (void) sigaddset(&sigs, SIGTERM); /* exit */
435 (void) sigaddset(&sigs, SIGINT); /* exit if debugging */
436 (void) sigaddset(&sigs, SIGQUIT); /* exit if debugging */
437 (void) sigaddset(&sigs, SIGPIPE); /* catch & discard */
438 (void) sigaddset(&sigs, SIGUSR1); /* dump debug stats */
441 * We must set up to catch these signals, even though sigwait
442 * will get them before the isr does. Setting SA_SIGINFO ensures
443 * that signals will be enqueued.
446 act.sa_flags = SA_SIGINFO;
447 act.sa_sigaction = signull;
449 (void) sigaction(SIGHUP, &act, NULL);
450 (void) sigaction(SIGALRM, &act, NULL);
451 (void) sigaction(SIGTERM, &act, NULL);
452 (void) sigaction(SIGINT, &act, NULL);
453 (void) sigaction(SIGQUIT, &act, NULL);
454 (void) sigaction(SIGPIPE, &act, NULL);
455 (void) sigaction(SIGUSR1, &act, NULL);
457 /* we now turn into the signal handling thread */
459 DPRINT1(2, "main(%u): now handling signals\n", mythreadno);
460 for (;;) {
461 (void) sigwait(&sigs, &sig);
462 DPRINT2(2, "main(%u): received signal %d\n", mythreadno, sig);
463 switch (sig) {
464 case SIGALRM:
465 DPRINT1(1, "main(%u): Got SIGALRM\n",
466 mythreadno);
467 flushmsg(NOCOPY);
468 if (Marking && (++mcount % MARKCOUNT == 0)) {
469 if (logmymsg(LOG_INFO, "-- MARK --",
470 ADDDATE|MARK|NOCOPY, 0) == -1) {
471 MALLOC_FAIL(
472 "dropping MARK message");
475 mcount = 0;
477 curalarm = MarkInterval * 60 / MARKCOUNT;
478 (void) alarm((unsigned)curalarm);
479 DPRINT2(2, "main(%u): Next alarm in %d "
480 "seconds\n", mythreadno, curalarm);
481 break;
482 case SIGHUP:
483 DPRINT1(1, "main(%u): got SIGHUP - "
484 "reconfiguring\n", mythreadno);
486 reconfigure();
488 DPRINT1(1, "main(%u): done processing SIGHUP\n",
489 mythreadno);
490 break;
491 case SIGQUIT:
492 case SIGINT:
493 if (!Debug) {
494 /* allow these signals if debugging */
495 break;
497 /* FALLTHROUGH */
498 case SIGTERM:
499 DPRINT2(1, "main(%u): going down on signal %d\n",
500 mythreadno, sig);
501 (void) alarm(0);
502 flushmsg(0);
503 errno = 0;
504 t_errno = 0;
505 logerror("going down on signal %d", sig);
506 disable_errorlog(); /* force msg to console */
507 (void) shutdown_msg(); /* stop threads */
508 shutdown_input();
509 close_door();
510 delete_doorfiles();
511 return (0);
512 case SIGUSR1: /* secret debug dump mode */
513 /* if in debug mode, use stdout */
515 if (Debug) {
516 dumpstats(STDOUT_FILENO);
517 break;
519 /* otherwise dump to a debug file */
520 if ((fd = open(DEBUGFILE,
521 (O_WRONLY|O_CREAT|O_TRUNC|O_EXCL),
522 0644)) < 0)
523 break;
524 dumpstats(fd);
525 (void) close(fd);
526 break;
527 default:
528 DPRINT2(2, "main(%u): unexpected signal %d\n",
529 mythreadno, sig);
530 break;
536 * Attempts to open the local log device
537 * and return a file descriptor.
539 static int
540 openklog(char *name, int mode)
542 int fd;
543 struct strioctl str;
544 pthread_t mythreadno;
546 if (Debug) {
547 mythreadno = pthread_self();
550 if ((fd = open(name, mode)) < 0) {
551 logerror("cannot open %s", name);
552 DPRINT3(1, "openklog(%u): cannot create %s (%d)\n",
553 mythreadno, name, errno);
554 return (-1);
556 str.ic_cmd = I_CONSLOG;
557 str.ic_timout = 0;
558 str.ic_len = 0;
559 str.ic_dp = NULL;
560 if (ioctl(fd, I_STR, &str) < 0) {
561 logerror("cannot register to log console messages");
562 DPRINT2(1, "openklog(%u): cannot register to log "
563 "console messages (%d)\n", mythreadno, errno);
564 return (-1);
566 return (fd);
571 * Open the log device, and pull up all pending messages.
573 static void
574 prepare_sys_poll()
576 int nfds, funix;
578 if ((funix = openklog(LogName, O_RDONLY)) < 0) {
579 logerror("can't open kernel log device - fatal");
580 exit(1);
583 Pfd.fd = funix;
584 Pfd.events = POLLIN;
586 for (;;) {
587 nfds = poll(&Pfd, 1, 0);
588 if (nfds <= 0) {
589 if (sys_init_msg_count > 0)
590 flushmsg(SYNC_FILE);
591 break;
594 if (Pfd.revents & POLLIN) {
595 getkmsg(0);
596 } else if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
597 logerror("kernel log driver poll error");
598 break;
605 * this thread listens to the local stream log driver for log messages
606 * generated by this host, formats them, and queues them to the logger
607 * thread.
609 /*ARGSUSED*/
610 static void *
611 sys_poll(void *ap)
613 int nfds;
614 static int klogerrs = 0;
615 pthread_t mythreadno;
617 if (Debug) {
618 mythreadno = pthread_self();
621 DPRINT1(1, "sys_poll(%u): sys_thread started\n", mythreadno);
624 * Process messages, blocking on poll because timeout is set
625 * to INFTIM. When poll returns with a message, call getkmsg
626 * to pull up one message from the log driver and enqueue it
627 * with the sync flag set.
630 sys_init_msg_count = 0;
632 for (;;) {
633 errno = 0;
634 t_errno = 0;
636 nfds = poll(&Pfd, 1, INFTIM);
638 if (nfds == 0)
639 continue;
641 if (nfds < 0) {
642 if (errno != EINTR)
643 logerror("poll");
644 continue;
646 if (Pfd.revents & POLLIN) {
647 getkmsg(INFTIM);
648 } else {
649 if (shutting_down) {
650 pthread_exit(0);
652 if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) {
653 logerror("kernel log driver poll error");
654 (void) close(Pfd.fd);
655 Pfd.fd = -1;
659 while (Pfd.fd == -1 && klogerrs++ < 10) {
660 Pfd.fd = openklog(LogName, O_RDONLY);
662 if (klogerrs >= 10) {
663 logerror("can't reopen kernel log device - fatal");
664 exit(1);
667 /*NOTREACHED*/
668 return (NULL);
672 * Pull up one message from log driver.
674 static void
675 getkmsg(int timeout)
677 int flags = 0, i;
678 char *lastline;
679 struct strbuf ctl, dat;
680 struct log_ctl hdr;
681 char buf[MAXLINE+1];
682 size_t buflen;
683 size_t len;
684 char tmpbuf[MAXLINE+1];
685 pthread_t mythreadno;
687 if (Debug) {
688 mythreadno = pthread_self();
691 dat.maxlen = MAXLINE;
692 dat.buf = buf;
693 ctl.maxlen = sizeof (struct log_ctl);
694 ctl.buf = (caddr_t)&hdr;
696 while ((i = getmsg(Pfd.fd, &ctl, &dat, &flags)) == MOREDATA) {
697 lastline = &dat.buf[dat.len];
698 *lastline = '\0';
700 DPRINT2(5, "getkmsg:(%u): getmsg: dat.len = %d\n",
701 mythreadno, dat.len);
702 buflen = strlen(buf);
703 len = findnl_bkwd(buf, buflen);
705 (void) memcpy(tmpbuf, buf, len);
706 tmpbuf[len] = '\0';
709 * Format sys will enqueue the log message.
710 * Set the sync flag if timeout != 0, which
711 * means that we're done handling all the
712 * initial messages ready during startup.
714 if (timeout == 0) {
715 formatsys(&hdr, tmpbuf, 0);
716 sys_init_msg_count++;
717 } else {
718 formatsys(&hdr, tmpbuf, 1);
720 sys_msg_count++;
722 if (len != buflen) {
723 /* If anything remains in buf */
724 size_t remlen;
726 if (buf[len] == '\n') {
727 /* skip newline */
728 len++;
732 * Move the remaining bytes to
733 * the beginnning of buf.
736 remlen = buflen - len;
737 (void) memcpy(buf, &buf[len], remlen);
738 dat.maxlen = MAXLINE - remlen;
739 dat.buf = &buf[remlen];
740 } else {
741 dat.maxlen = MAXLINE;
742 dat.buf = buf;
746 if (i == 0 && dat.len > 0) {
747 dat.buf[dat.len] = '\0';
749 * Format sys will enqueue the log message.
750 * Set the sync flag if timeout != 0, which
751 * means that we're done handling all the
752 * initial messages ready during startup.
754 DPRINT2(5, "getkmsg(%u): getmsg: dat.maxlen = %d\n",
755 mythreadno, dat.maxlen);
756 DPRINT2(5, "getkmsg(%u): getmsg: dat.len = %d\n",
757 mythreadno, dat.len);
758 DPRINT2(5, "getkmsg(%u): getmsg: strlen(dat.buf) = %d\n",
759 mythreadno, strlen(dat.buf));
760 DPRINT2(5, "getkmsg(%u): getmsg: dat.buf = \"%s\"\n",
761 mythreadno, dat.buf);
762 DPRINT2(5, "getkmsg(%u): buf len = %d\n",
763 mythreadno, strlen(buf));
764 if (timeout == 0) {
765 formatsys(&hdr, buf, 0);
766 sys_init_msg_count++;
767 } else {
768 formatsys(&hdr, buf, 1);
770 sys_msg_count++;
771 } else if (i < 0 && errno != EINTR) {
772 if (!shutting_down) {
773 logerror("kernel log driver read error");
775 (void) close(Pfd.fd);
776 Pfd.fd = -1;
781 * this thread polls all the network interfaces for syslog messages
782 * forwarded to us, tags them with the hostname they are received
783 * from, and queues them to the logger thread.
785 /*ARGSUSED*/
786 static void *
787 net_poll(void *ap)
789 int nfds, i;
790 int flags = 0;
791 struct t_unitdata *udp;
792 struct t_uderr *errp;
793 char buf[MAXLINE+1];
794 char *uap;
795 log_message_t *mp;
796 host_info_t *hinfo;
797 pthread_t mythreadno;
799 if (Debug) {
800 mythreadno = pthread_self();
803 DPRINT1(1, "net_poll(%u): net_thread started\n", mythreadno);
805 for (;;) {
806 errno = 0;
807 t_errno = 0;
808 nfds = poll(Nfd, Ninputs, -1);
809 if (nfds == 0)
810 continue;
812 if (nfds < 0) {
813 if (errno != EINTR)
814 logerror("poll");
815 continue;
817 for (i = 0; nfds > 0 && i < Ninputs; i++) {
818 if ((Nfd[i].revents & POLLIN) == 0) {
819 if (shutting_down) {
820 pthread_exit(0);
822 if (Nfd[i].revents &
823 (POLLNVAL|POLLHUP|POLLERR)) {
824 logerror("POLLNVAL|POLLHUP|POLLERR");
825 (void) t_close(Nfd[i].fd);
826 Nfd[i].fd = -1;
827 nfds--;
829 continue;
832 udp = Udp[i];
833 udp->udata.buf = buf;
834 udp->udata.maxlen = MAXLINE;
835 udp->udata.len = 0;
836 flags = 0;
837 if (t_rcvudata(Nfd[i].fd, udp, &flags) < 0) {
838 errp = Errp[i];
839 if (t_errno == TLOOK) {
840 if (t_rcvuderr(Nfd[i].fd, errp) < 0) {
841 if (!shutting_down) {
842 logerror("t_rcvuderr");
844 (void) t_close(Nfd[i].fd);
845 Nfd[i].fd = -1;
847 } else {
848 if (!shutting_down) {
849 logerror("t_rcvudata");
851 (void) t_close(Nfd[i].fd);
852 Nfd[i].fd = -1;
854 nfds--;
855 if (shutting_down) {
856 pthread_exit(0);
858 continue;
860 nfds--;
862 if (udp->udata.len == 0) {
863 if (Debug) {
864 uap = NULL;
865 if (udp->addr.len > 0) {
866 uap = taddr2uaddr(&Ncf[i],
867 &udp->addr);
869 DPRINT2(1, "net_poll(%u):"
870 " received empty packet"
871 " from %s\n", mythreadno,
872 uap ? uap : "<unknown>");
873 free(uap);
875 continue; /* No data */
877 if (udp->addr.len == 0) {
879 * The previous message was larger than
880 * MAXLINE, and T_MORE should have been set.
881 * Further data needs to be discarded as
882 * we've already received MAXLINE.
884 DPRINT1(1, "net_poll(%u): discarding packet "
885 "exceeds max line size\n", mythreadno);
886 continue;
889 net_msg_count++;
891 if ((mp = new_msg()) == NULL) {
892 MALLOC_FAIL("dropping message from "
893 "remote");
894 continue;
897 buf[udp->udata.len] = '\0';
898 formatnet(&udp->udata, mp);
900 if (Debug) {
901 uap = taddr2uaddr(&Ncf[i], &udp->addr);
902 DPRINT2(1, "net_poll(%u): received message"
903 " from %s\n", mythreadno,
904 uap ? uap : "<unknown>");
905 free(uap);
907 if ((hinfo = malloc(sizeof (*hinfo))) == NULL ||
908 (hinfo->addr.buf =
909 malloc(udp->addr.len)) == NULL) {
910 MALLOC_FAIL("dropping message from "
911 "remote");
912 if (hinfo) {
913 free(hinfo);
915 free_msg(mp);
916 continue;
919 hinfo->ncp = &Ncf[i];
920 hinfo->addr.len = udp->addr.len;
921 (void) memcpy(hinfo->addr.buf, udp->addr.buf,
922 udp->addr.len);
923 mp->ptr = hinfo;
924 if (dataq_enqueue(&hnlq, (void *)mp) == -1) {
925 MALLOC_FAIL("dropping message from "
926 "remote");
927 free_msg(mp);
928 free(hinfo->addr.buf);
929 free(hinfo);
930 continue;
932 DPRINT3(5, "net_poll(%u): enqueued msg %p "
933 "on queue %p\n", mythreadno, (void *)mp,
934 (void *)&hnlq);
937 /*NOTREACHED*/
938 return (NULL);
941 static void
942 usage(void)
944 (void) fprintf(stderr,
945 "usage: syslogd [-d] [-t|-T] [-mmarkinterval] [-ppath]"
946 " [-fconffile]\n");
947 exit(1);
950 static void
951 untty(void)
953 if (!Debug)
954 (void) setsid();
958 * generate a log message internally. The original version of syslogd
959 * simply called logmsg directly, but because everything is now based
960 * on message passing, we need an internal way to generate and queue
961 * log messages from within syslogd itself.
963 static int
964 logmymsg(int pri, char *msg, int flags, int pending)
966 log_message_t *mp;
967 pthread_t mythreadno;
968 dataq_t *qptr;
970 if (Debug) {
971 mythreadno = pthread_self();
974 if ((mp = new_msg()) == NULL) {
975 return (-1);
978 mp->pri = pri;
979 mp->hlp = &LocalHostName;
980 (void) strlcpy(mp->msg, msg, MAXLINE+1);
981 mp->flags = flags;
982 (void) time(&mp->ts);
984 qptr = pending ? &tmpq : &inputq;
985 if (dataq_enqueue(qptr, (void *)mp) == -1) {
986 free_msg(mp);
987 return (-1);
990 DPRINT3(5, "logmymsg(%u): enqueued msg %p on queue %p\n",
991 mythreadno, (void *)mp, (void *)qptr);
992 DPRINT2(5, "logmymsg(%u): Message content: %s\n", mythreadno, msg);
993 return (0);
997 * Generate an internal shutdown message
999 static int
1000 shutdown_msg(void)
1002 pthread_t mythreadno;
1003 log_message_t *mp;
1005 if (Debug) {
1006 mythreadno = pthread_self();
1009 if ((mp = new_msg()) == NULL) {
1010 return (-1);
1013 mp->flags = SHUTDOWN;
1014 mp->hlp = &LocalHostName;
1016 if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1017 free_msg(mp);
1018 return (-1);
1021 DPRINT3(5, "shutdown_msg(%u): enqueued msg %p on queue %p\n",
1022 mythreadno, (void *)mp, (void *)&inputq);
1023 return (0);
1027 * Generate an internal flush message
1029 static void
1030 flushmsg(int flags)
1032 log_message_t *mp;
1033 pthread_t mythreadno;
1035 if (Debug) {
1036 mythreadno = pthread_self();
1039 if ((mp = new_msg()) == NULL) {
1040 MALLOC_FAIL("dropping flush msg");
1041 return;
1044 mp->flags = FLUSHMSG | flags;
1045 mp->hlp = &LocalHostName;
1047 if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1048 free_msg(mp);
1049 MALLOC_FAIL("dropping flush msg");
1050 return;
1053 DPRINT4(5, "flush_msg(%u): enqueued msg %p on queue %p, flags "
1054 "0x%x\n", mythreadno, (void *)mp, (void *)&inputq, flags);
1058 * Do some processing on messages received from the net
1060 static void
1061 formatnet(struct netbuf *nbp, log_message_t *mp)
1063 char *p;
1064 int pri;
1065 pthread_t mythreadno;
1067 if (Debug) {
1068 mythreadno = pthread_self();
1071 DPRINT2(5, "formatnet(%u): called for msg %p\n", mythreadno,
1072 (void *)mp);
1074 mp->flags = NETWORK;
1075 (void) time(&mp->ts);
1077 /* test for special codes */
1078 pri = DEFUPRI;
1079 p = nbp->buf;
1080 DPRINT2(9, "formatnet(%u): Message content:\n>%s<\n", mythreadno,
1082 if (*p == '<' && isdigit(*(p+1))) {
1083 pri = 0;
1084 while (isdigit(*++p))
1085 pri = 10 * pri + (*p - '0');
1086 if (*p == '>')
1087 ++p;
1088 if (pri <= 0 || pri >= (LOG_NFACILITIES << 3))
1089 pri = DEFUPRI;
1092 mp->pri = pri;
1093 (void) strlcpy(mp->msg, p, MAXLINE+1);
1097 * Do some processing on messages generated by this host
1098 * and then enqueue the log message.
1100 static void
1101 formatsys(struct log_ctl *lp, char *msg, int sync)
1103 char *p, *q;
1104 char line[MAXLINE + 1];
1105 size_t msglen;
1106 log_message_t *mp;
1107 char cbuf[30];
1108 pthread_t mythreadno;
1110 if (Debug) {
1111 mythreadno = pthread_self();
1114 DPRINT3(3, "formatsys(%u): log_ctl.mid = %d, log_ctl.sid = %d\n",
1115 mythreadno, lp->mid, lp->sid);
1116 DPRINT2(9, "formatsys(%u): Message Content:\n>%s<\n", mythreadno,
1117 msg);
1119 /* msglen includes the null termination */
1120 msglen = strlen(msg) + 1;
1122 for (p = msg; *p != '\0'; ) {
1123 size_t linelen;
1124 size_t len;
1127 * Allocate a log_message_t structure.
1128 * We should do it here since a single message (msg)
1129 * could be composed of many lines.
1131 if ((mp = new_msg()) == NULL) {
1132 MALLOC_FAIL("dropping message");
1134 * Should bail out from the loop.
1136 break;
1139 mp->flags &= ~NETWORK;
1140 mp->hlp = &LocalHostName;
1141 mp->ts = lp->ttime;
1142 if (lp->flags & SL_LOGONLY)
1143 mp->flags |= IGN_CONS;
1144 if (lp->flags & SL_CONSONLY)
1145 mp->flags |= IGN_FILE;
1147 /* extract facility */
1148 if ((lp->pri & LOG_FACMASK) == LOG_KERN) {
1149 (void) sprintf(line, "%.15s ",
1150 ctime_r(&mp->ts, cbuf) + 4);
1151 } else {
1152 (void) sprintf(line, "");
1155 linelen = strlen(line);
1156 q = line + linelen;
1158 DPRINT2(5, "formatsys(%u): msglen = %d\n", mythreadno, msglen);
1159 len = copynl_frwd(q, MAXLINE + 1 - linelen, p, msglen);
1160 DPRINT2(5, "formatsys(%u): len (copynl_frwd) = %d\n",
1161 mythreadno, len);
1163 p += len;
1164 msglen -= len;
1166 if (*p == '\n') {
1167 /* skip newline */
1168 p++;
1171 if (sync && ((lp->pri & LOG_FACMASK) == LOG_KERN))
1172 mp->flags |= SYNC_FILE; /* fsync file after write */
1174 if (len != 0) {
1175 (void) strlcpy(mp->msg, line, MAXLINE+1);
1176 mp->pri = lp->pri;
1178 if (dataq_enqueue(&inputq, (void *)mp) == -1) {
1179 free_msg(mp);
1180 MALLOC_FAIL("dropping message");
1181 break;
1184 DPRINT3(5, "formatsys(%u): sys_thread enqueued msg "
1185 "%p on queue %p\n", mythreadno, (void *)mp,
1186 (void *)&inputq);
1187 } else
1188 free_msg(mp);
1193 * Log a message to the appropriate log files, users, etc. based on
1194 * the priority.
1196 /*ARGSUSED*/
1197 static void *
1198 logmsg(void *ap)
1200 struct filed *f;
1201 int fac, prilev, flags, refcnt;
1202 int fake_shutdown, skip_shutdown;
1203 log_message_t *mp, *save_mp;
1204 pthread_t mythreadno;
1206 if (Debug) {
1207 mythreadno = pthread_self();
1210 DPRINT1(1, "logmsg(%u): msg dispatcher started\n", mythreadno);
1212 fake_shutdown = skip_shutdown = 0;
1213 save_mp = NULL;
1214 for (;;) {
1215 if (save_mp) {
1217 * If we have set aside a message in order to fake a
1218 * SHUTDOWN, use that message before picking from the
1219 * queue again.
1221 mp = save_mp;
1222 save_mp = NULL;
1223 } else {
1224 (void) dataq_dequeue(&inputq, (void **)&mp, 0);
1226 DPRINT3(5, "logmsg(%u): msg dispatcher dequeued %p from "
1227 "queue %p\n", mythreadno, (void *)mp,
1228 (void *)&inputq);
1231 * In most cases, if the message traffic is low, logmsg() wakes
1232 * up when it receives the SHUTDOWN msg, and will sleep until
1233 * HUP process is complete. However, if the inputq is too
1234 * long, logmsg() may not receive SHUTDOWN before reconfigure()
1235 * releases the logger fds, filed and logit threads. That, in
1236 * turn, will cause logmsg to refer to invalid fileds.
1238 * logmsg() needs to respond to the SHUTDOWN message within
1239 * LOOP_INTERVAL seconds when reconfigure() enqueues it. It
1240 * does so in most cases. When it does not respond in time,
1241 * logmsg() needs to be in suspended state immediately, since
1242 * filed may have been invalidated. reconfigure() will set the
1243 * HUP_SUSP_LOGMSG_REQD bit in hup_state and wait another
1244 * LOOP_INTERVAL seconds before proceeding.
1246 * When HUP_SUSP_LOGMSG_REQD is set, we will create a fake
1247 * SHUTDOWN message, and dispatch it to the various logit
1248 * threads, and logmsg() itself will suspend. In order to
1249 * ignore the real SHUTDOWN which will arrive later, we keep a
1250 * counter (skip_shutdown) and decrement it when the SHUTDOWN
1251 * message arrives.
1253 if ((hup_state & HUP_SUSP_LOGMSG_REQD) &&
1254 (mp->flags & SHUTDOWN) == 0) {
1255 DPRINT1(3, "logmsg(%u): suspend request\n",
1256 mythreadno);
1258 save_mp = mp;
1260 /* create a fake SHUTDOWN msg */
1261 if ((mp = new_msg()) == NULL) {
1262 MALLOC_FAIL("dropping message");
1263 if (mp->flags & SHUTDOWN) {
1264 (void) logerror_to_console(1,
1265 "unable to shutdown "
1266 "logger thread");
1268 continue;
1270 mp->flags = SHUTDOWN;
1271 mp->hlp = &LocalHostName;
1272 fake_shutdown = 1;
1273 skip_shutdown++;
1274 DPRINT2(3, "logmsg(%u): pending SHUTDOWN %d\n",
1275 mythreadno, skip_shutdown);
1279 * is it a shutdown or flush message ?
1281 if ((mp->flags & SHUTDOWN) || (mp->flags & FLUSHMSG)) {
1282 (void) pthread_mutex_lock(&mp->msg_mutex);
1284 if ((mp->flags & SHUTDOWN) &&
1285 !fake_shutdown && skip_shutdown > 0) {
1286 skip_shutdown--;
1287 (void) pthread_mutex_unlock(&mp->msg_mutex);
1288 free_msg(mp);
1289 DPRINT2(3, "logmsg(%u): released late "
1290 "arrived SHUTDOWN. pending %d\n",
1291 mythreadno, skip_shutdown);
1292 continue;
1295 for (f = Files; f < &Files[nlogs]; f++) {
1296 (void) pthread_mutex_lock(&f->filed_mutex);
1298 if (f->f_type == F_UNUSED) {
1299 (void) pthread_mutex_unlock(
1300 &f->filed_mutex);
1301 continue;
1304 f->f_queue_count++;
1305 mp->refcnt++;
1307 if (dataq_enqueue(&f->f_queue,
1308 (void *)mp) == -1) {
1309 f->f_queue_count--;
1310 mp->refcnt--;
1311 (void) pthread_mutex_unlock(
1312 &f->filed_mutex);
1313 MALLOC_FAIL("dropping message");
1315 if (mp->flags & SHUTDOWN) {
1316 (void) logerror_to_console(1,
1317 "unable to shutdown "
1318 "logger thread");
1321 continue;
1323 DPRINT3(5, "logmsg(%u): enqueued msg %p "
1324 "on queue %p\n", mythreadno,
1325 (void *)mp, (void *)&f->f_queue);
1326 (void) pthread_mutex_unlock(&f->filed_mutex);
1330 * flags value needs to be saved because mp may
1331 * have been freed before SHUTDOWN test below.
1333 flags = mp->flags;
1334 refcnt = mp->refcnt;
1336 (void) pthread_mutex_unlock(&mp->msg_mutex);
1337 if (refcnt == 0)
1338 free_msg(mp);
1340 if (flags & SHUTDOWN) {
1341 (void) pthread_mutex_lock(&hup_lock);
1342 while (hup_state != HUP_COMPLETED) {
1343 hup_state |= HUP_LOGMSG_SUSPENDED;
1344 (void) pthread_cond_wait(&hup_done,
1345 &hup_lock);
1346 hup_state &= ~HUP_LOGMSG_SUSPENDED;
1348 hup_state = HUP_ACCEPTABLE;
1349 (void) pthread_mutex_unlock(&hup_lock);
1350 fake_shutdown = 0;
1352 continue;
1356 * Check to see if msg looks non-standard.
1358 if ((int)strlen(mp->msg) < 16 || mp->msg[3] != ' ' ||
1359 mp->msg[6] != ' ' || mp->msg[9] != ':' ||
1360 mp->msg[12] != ':' || mp->msg[15] != ' ')
1361 mp->flags |= ADDDATE;
1363 /* extract facility and priority level */
1364 fac = (mp->pri & LOG_FACMASK) >> 3;
1365 if (mp->flags & MARK)
1366 fac = LOG_NFACILITIES;
1367 prilev = mp->pri & LOG_PRIMASK;
1369 DPRINT3(3, "logmsg(%u): fac = %d, pri = %d\n",
1370 mythreadno, fac, prilev);
1373 * Because different devices log at different speeds,
1374 * it's important to hold the mutex for the current
1375 * message until it's been enqueued to all log files,
1376 * so the reference count is accurate before any
1377 * of the log threads can decrement it.
1379 (void) pthread_mutex_lock(&mp->msg_mutex);
1381 for (f = Files; f < &Files[nlogs]; f++) {
1382 /* skip messages that are incorrect priority */
1383 if (f->f_pmask[fac] < (unsigned)prilev ||
1384 f->f_pmask[fac] == NOPRI)
1385 continue;
1386 if (f->f_queue_count > Q_HIGHWATER_MARK) {
1387 DPRINT4(5, "logmsg(%u): Dropping message "
1388 "%p on file %p, count = %d\n",
1389 mythreadno, (void *)mp, (void *)f,
1390 f->f_queue_count);
1391 continue;
1395 * Need to grab filed_mutex before testing the f_type.
1396 * Otherwise logit() may set F_UNUSED after the test
1397 * below, and start pulling out the pending messages.
1400 (void) pthread_mutex_lock(&f->filed_mutex);
1402 if (f->f_type == F_UNUSED ||
1403 (f->f_type == F_FILE && (mp->flags & IGN_FILE)) ||
1404 (f->f_type == F_CONSOLE &&
1405 (mp->flags & IGN_CONS))) {
1406 (void) pthread_mutex_unlock(&f->filed_mutex);
1407 continue;
1410 f->f_queue_count++;
1411 mp->refcnt++;
1413 if (dataq_enqueue(&f->f_queue, (void *)mp) == -1) {
1414 f->f_queue_count--;
1415 mp->refcnt--;
1416 (void) pthread_mutex_unlock(&f->filed_mutex);
1417 MALLOC_FAIL("dropping message");
1418 continue;
1421 DPRINT3(5, "logmsg(%u): enqueued msg %p on queue "
1422 "%p\n", mythreadno, (void *)mp,
1423 (void *)&f->f_queue);
1424 (void) pthread_mutex_unlock(&f->filed_mutex);
1426 refcnt = mp->refcnt;
1427 (void) pthread_mutex_unlock(&mp->msg_mutex);
1428 if (refcnt == 0)
1429 free_msg(mp);
1431 /*NOTREACHED*/
1432 return (NULL);
1436 * function to actually write the log message to the selected file.
1437 * each file has a logger thread that runs this routine. The function
1438 * is called with a pointer to its file structure.
1440 static void *
1441 logit(void *ap)
1443 struct filed *f = ap;
1444 log_message_t *mp;
1445 int forwardingloop = 0;
1446 const char *errmsg = "logit(%u): %s to %s forwarding loop detected\n";
1447 int i, currofst, prevofst, refcnt;
1448 host_list_t *hlp;
1450 assert(f != NULL);
1452 DPRINT4(5, "logit(%u): logger started for \"%s\" (queue %p, filed "
1453 "%p)\n", f->f_thread, f->f_un.f_fname, (void *)&f->f_queue,
1454 (void *)f);
1456 while (f->f_type != F_UNUSED) {
1457 (void) dataq_dequeue(&f->f_queue, (void **)&mp, 0);
1458 DPRINT3(5, "logit(%u): logger dequeued msg %p from queue "
1459 "%p\n", f->f_thread, (void *)mp, (void *)&f->f_queue);
1460 (void) pthread_mutex_lock(&f->filed_mutex);
1461 assert(f->f_queue_count > 0);
1462 f->f_queue_count--;
1463 (void) pthread_mutex_unlock(&f->filed_mutex);
1464 assert(mp->refcnt > 0);
1467 * is it a shutdown message ?
1469 if (mp->flags & SHUTDOWN) {
1470 (void) pthread_mutex_lock(&mp->msg_mutex);
1471 refcnt = --mp->refcnt;
1472 (void) pthread_mutex_unlock(&mp->msg_mutex);
1473 if (refcnt == 0)
1474 free_msg(mp);
1475 break;
1479 * Is it a logsync message?
1481 if ((mp->flags & (FLUSHMSG | LOGSYNC)) ==
1482 (FLUSHMSG | LOGSYNC)) {
1483 if (f->f_type != F_FILE)
1484 goto out; /* nothing to do */
1485 (void) close(f->f_file);
1486 f->f_file = open64(f->f_un.f_fname,
1487 O_WRONLY|O_APPEND|O_NOCTTY);
1488 if (f->f_file < 0) {
1489 f->f_type = F_UNUSED;
1490 logerror(f->f_un.f_fname);
1491 f->f_stat.errs++;
1493 goto out;
1497 * If the message flags include both flush and sync,
1498 * then just sync the file out to disk if appropriate.
1500 if ((mp->flags & (FLUSHMSG | SYNC_FILE)) ==
1501 (FLUSHMSG | SYNC_FILE)) {
1502 if (f->f_type == F_FILE) {
1503 DPRINT2(5, "logit(%u): got FLUSH|SYNC "
1504 "for filed %p\n", f->f_thread,
1505 (void *)f);
1506 (void) fsync(f->f_file);
1508 goto out;
1512 * Otherwise if it's a standard flush message, write
1513 * out any saved messages to the file.
1515 if ((mp->flags & FLUSHMSG) && (f->f_prevcount > 0)) {
1516 set_flush_msg(f);
1517 writemsg(SAVED, f);
1518 goto out;
1521 (void) strlcpy(f->f_current.msg, mp->msg, MAXLINE+1);
1522 (void) strlcpy(f->f_current.host, mp->hlp->hl_hosts[0],
1523 SYS_NMLN);
1524 f->f_current.pri = mp->pri;
1525 f->f_current.flags = mp->flags;
1526 f->f_current.time = mp->ts;
1527 f->f_msgflag &= ~CURRENT_VALID;
1528 hlp = mp->hlp;
1530 prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16;
1531 currofst = (f->f_current.flags & ADDDATE) ? 0 : 16;
1533 if (f->f_type == F_FORW) {
1535 * Should not forward MARK messages, as they are
1536 * not defined outside of the current system.
1539 if (mp->flags & MARK) {
1540 DPRINT1(1, "logit(%u): cannot forward "
1541 "Mark\n", f->f_thread);
1542 goto out;
1546 * can not forward message if we do
1547 * not have a host to forward to
1549 if (hlp == (host_list_t *)NULL)
1550 goto out;
1552 * a forwarding loop is created on machines
1553 * with multiple interfaces because the
1554 * network address of the sender is different
1555 * to the receiver even though it is the
1556 * same machine. Instead, if the
1557 * hostname the source and target are
1558 * the same the message if thrown away
1560 forwardingloop = 0;
1561 for (i = 0; i < hlp->hl_cnt; i++) {
1562 if (strcmp(hlp->hl_hosts[i],
1563 f->f_un.f_forw.f_hname) == 0) {
1564 DPRINT3(1, errmsg, f->f_thread,
1565 f->f_un.f_forw.f_hname,
1566 hlp->hl_hosts[i]);
1567 forwardingloop = 1;
1568 break;
1572 if (forwardingloop == 1) {
1573 f->f_stat.cantfwd++;
1574 goto out;
1578 f->f_msgflag |= CURRENT_VALID;
1580 /* check for dup message */
1581 if (f->f_type != F_FORW &&
1582 (f->f_msgflag & OLD_VALID) &&
1583 prevofst == currofst &&
1584 (strcmp(f->f_prevmsg.msg + prevofst,
1585 f->f_current.msg + currofst) == 0) &&
1586 (strcmp(f->f_prevmsg.host,
1587 f->f_current.host) == 0)) {
1588 /* a dup */
1589 DPRINT2(2, "logit(%u): msg is dup - %p\n",
1590 f->f_thread, (void *)mp);
1591 if (currofst == 16) {
1592 (void) strncpy(f->f_prevmsg.msg,
1593 f->f_current.msg, 15); /* update time */
1595 f->f_prevcount++;
1596 f->f_stat.dups++;
1597 f->f_stat.total++;
1598 f->f_msgflag &= ~CURRENT_VALID;
1599 } else {
1600 /* new: mark or prior dups exist */
1601 if (f->f_current.flags & MARK || f->f_prevcount > 0) {
1602 if (f->f_prevcount > 0 && f->f_type != F_FORW) {
1603 set_flush_msg(f);
1604 if (f->f_msgflag & OLD_VALID) {
1605 writemsg(SAVED, f);
1608 if (f->f_msgflag & CURRENT_VALID)
1609 writemsg(CURRENT, f);
1610 if (!(mp->flags & NOCOPY))
1611 copy_msg(f);
1612 if (f->f_current.flags & MARK) {
1613 DPRINT2(2, "logit(%u): msg is "
1614 "mark - %p)\n", f->f_thread,
1615 (void *)mp);
1616 f->f_msgflag &= ~OLD_VALID;
1617 } else {
1618 DPRINT2(2, "logit(%u): saving "
1619 "message - %p\n", f->f_thread,
1620 (void *)mp);
1622 f->f_stat.total++;
1623 } else { /* new message */
1624 DPRINT2(2, "logit(%u): msg is new "
1625 "- %p\n", f->f_thread, (void *)mp);
1626 writemsg(CURRENT, f);
1627 if (!(mp->flags & NOCOPY))
1628 copy_msg(f);
1629 f->f_stat.total++;
1633 * if message refcnt goes to zero after we decrement
1634 * it here, we are the last consumer of the message,
1635 * and we should free it. We need to hold the lock
1636 * between decrementing the count and checking for
1637 * zero so another thread doesn't beat us to it.
1639 out:
1640 (void) pthread_mutex_lock(&mp->msg_mutex);
1641 refcnt = --mp->refcnt;
1642 (void) pthread_mutex_unlock(&mp->msg_mutex);
1643 if (refcnt == 0)
1644 free_msg(mp);
1646 /* register our exit */
1649 * Pull out all pending messages, if they exist.
1652 (void) pthread_mutex_lock(&f->filed_mutex);
1654 while (f->f_queue_count > 0) {
1655 (void) dataq_dequeue(&f->f_queue, (void **)&mp, 0);
1656 DPRINT3(5, "logit(%u): logger dequeued msg %p from queue "
1657 "%p\n",
1658 f->f_thread, (void *)mp, (void *)&f->f_queue);
1659 (void) pthread_mutex_lock(&mp->msg_mutex);
1660 refcnt = --mp->refcnt;
1661 (void) pthread_mutex_unlock(&mp->msg_mutex);
1662 if (refcnt == 0)
1663 free_msg(mp);
1664 f->f_queue_count--;
1667 (void) pthread_mutex_unlock(&f->filed_mutex);
1669 if (f->f_type != F_USERS && f->f_type != F_WALL &&
1670 f->f_type != F_UNUSED) {
1671 if (f->f_type == F_FORW)
1672 (void) t_close(f->f_file);
1673 else
1674 (void) close(f->f_file);
1678 * Since f_type may have been changed before this point, we need
1679 * to test orig_type.
1681 if (f->f_orig_type == F_FORW) {
1682 free(f->f_un.f_forw.f_addr.buf);
1685 f->f_type = F_UNUSED;
1686 (void) pthread_mutex_lock(&cft);
1687 --conf_threads;
1688 (void) pthread_mutex_unlock(&cft);
1689 DPRINT1(5, "logit(%u): logging thread exited\n", f->f_thread);
1690 return (NULL);
1694 * change the previous message to a flush message, stating how
1695 * many repeats occurred since the last flush
1697 static void
1698 set_flush_msg(struct filed *f)
1700 char tbuf[10];
1701 int prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16;
1703 if (f->f_prevcount == 1)
1704 (void) strncpy(tbuf, "time", sizeof (tbuf));
1705 else
1706 (void) strncpy(tbuf, "times", sizeof (tbuf));
1708 (void) snprintf(f->f_prevmsg.msg+prevofst,
1709 sizeof (f->f_prevmsg.msg) - prevofst,
1710 "last message repeated %d %s", f->f_prevcount, tbuf);
1711 f->f_prevcount = 0;
1712 f->f_msgflag |= OLD_VALID;
1717 * the actual writing of the message is broken into a separate function
1718 * because each file has a current and saved message associated with
1719 * it (for duplicate message detection). It is necessary to be able
1720 * to write either the saved message or the current message.
1722 static void
1723 writemsg(int selection, struct filed *f)
1725 char *cp, *p;
1726 int pri;
1727 int flags;
1728 int l;
1729 time_t ts;
1730 struct t_unitdata ud;
1731 char *eomp, *eomp2, *from, *text, *msg;
1732 char line[MAXLINE*2];
1733 char head[MAXLINE+1];
1734 char tmpbuf[MAXLINE+1];
1735 char cbuf[30];
1736 char *filtered;
1737 char *msgid_start, *msgid_end;
1738 pthread_t mythreadno;
1739 size_t hlen, filter_len;
1741 if (Debug) {
1742 mythreadno = pthread_self();
1745 switch (selection) {
1746 default:
1747 case CURRENT: /* print current message */
1748 msg = f->f_current.msg;
1749 from = f->f_current.host;
1750 pri = f->f_current.pri;
1751 flags = f->f_current.flags;
1752 ts = f->f_current.time;
1753 f->f_msgflag &= ~CURRENT_VALID;
1754 break;
1755 case SAVED: /* print saved message */
1756 msg = f->f_prevmsg.msg;
1757 from = f->f_prevmsg.host;
1758 pri = f->f_prevmsg.pri;
1759 flags = f->f_prevmsg.flags;
1760 ts = f->f_prevmsg.time;
1761 f->f_msgflag &= ~OLD_VALID;
1762 break;
1765 if (msg[0] == '\0')
1766 return;
1768 cp = line;
1770 if (flags & ADDDATE)
1771 (void) strncpy(cp, ctime_r(&ts, cbuf) + 4, 15);
1772 else
1773 (void) strncpy(cp, msg, 15);
1775 line[15] = '\0';
1776 (void) strcat(cp, " ");
1777 (void) strcat(cp, from);
1778 (void) strcat(cp, " ");
1779 text = cp + strlen(cp);
1781 if (flags & ADDDATE)
1782 (void) strcat(cp, msg);
1783 else
1784 (void) strcat(cp, msg+16);
1785 DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text);
1787 errno = 0;
1788 t_errno = 0;
1789 switch (f->f_type) {
1790 case F_UNUSED:
1791 DPRINT1(1, "writemsg(%u): UNUSED\n", mythreadno);
1792 break;
1793 case F_FORW:
1794 DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n",
1795 mythreadno, msg, TypeNames[f->f_type],
1796 f->f_un.f_forw.f_hname);
1798 hlen = snprintf(head, sizeof (head),
1799 "<%d>%.15s ", pri, cp);
1801 DPRINT2(5, "writemsg(%u): head = \"%s\"\n", mythreadno, head);
1802 DPRINT2(5, "writemsg(%u): hlen = %d\n", mythreadno, hlen);
1804 l = strlen(text);
1805 p = text;
1807 DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text);
1808 DPRINT2(5, "writemsg(%u): strlen(text) = %d\n", mythreadno, l);
1810 (void) strncpy(tmpbuf, head, hlen);
1812 while (l > 0) {
1813 size_t len;
1815 len = copy_frwd(tmpbuf + hlen, sizeof (tmpbuf) - hlen,
1816 p, l);
1818 DPRINT2(5, "writemsg(%u): tmpbuf = \"%s\"\n",
1819 mythreadno, tmpbuf);
1820 DPRINT2(5, "writemsg(%u): len = %d\n", mythreadno,
1821 len);
1822 DPRINT2(5, "writemsg(%u): strlen(tmpbuf) = %d\n",
1823 mythreadno, strlen(tmpbuf));
1825 ud.opt.buf = NULL;
1826 ud.opt.len = 0;
1827 ud.udata.buf = tmpbuf;
1828 ud.udata.len = len + hlen;
1829 ud.addr.maxlen = f->f_un.f_forw.f_addr.maxlen;
1830 ud.addr.buf = f->f_un.f_forw.f_addr.buf;
1831 ud.addr.len = f->f_un.f_forw.f_addr.len;
1832 if (t_sndudata(f->f_file, &ud) < 0) {
1833 if ((hup_state & HUP_INPROGRESS) &&
1834 f->f_type == F_UNUSED) {
1835 break;
1837 (void) t_close(f->f_file);
1838 f->f_type = F_UNUSED;
1839 logerror("t_sndudata");
1842 * Since it has already failed, it's not worth
1843 * continuing output from the middle of
1844 * message string.
1846 break;
1848 p += len;
1849 l -= len;
1851 break;
1852 case F_CONSOLE:
1853 case F_TTY:
1854 case F_FILE:
1855 case F_USERS:
1856 case F_WALL:
1857 DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n",
1858 mythreadno, msg, TypeNames[f->f_type],
1859 ((f->f_type == F_USERS) || (f->f_type == F_WALL)) ?
1860 "" : f->f_un.f_fname);
1862 * filter the string in preparation for writing it
1863 * save the original for possible forwarding.
1864 * In case every byte in cp is a control character,
1865 * allocates large enough buffer for filtered.
1868 filter_len = strlen(cp) * 4 + 1;
1869 filtered = (char *)malloc(filter_len);
1870 if (!filtered) {
1871 MALLOC_FAIL("dropping message");
1872 /* seems we can just return */
1873 return;
1875 DPRINT3(5, "writemsg(%u): "
1876 "filtered allocated (%p: %d bytes)\n",
1877 mythreadno, (void *)filtered, filter_len);
1878 /* -3 : we may add "\r\n" to ecomp(filtered) later */
1879 filter_string(cp, filtered, filter_len - 3);
1881 DPRINT2(5, "writemsg(%u): strlen(filtered) = %d\n",
1882 mythreadno, strlen(filtered));
1884 * If we're writing to the console, strip out the message ID
1885 * to reduce visual clutter.
1887 if ((msgid_start = strstr(filtered, "[ID ")) != NULL &&
1888 (msgid_end = strstr(msgid_start, "] ")) != NULL &&
1889 f->f_type == F_CONSOLE)
1890 (void) strcpy(msgid_start, msgid_end + 2);
1892 eomp = filtered + strlen(filtered);
1894 if ((f->f_type == F_USERS) || (f->f_type == F_WALL)) {
1895 /* CSTYLED */
1896 (void) strcat(eomp, "\r\n"); /*lint !e669*/
1898 * Since wallmsg messes with utmpx we need
1899 * to guarantee single threadedness...
1901 (void) pthread_mutex_lock(&wmp);
1902 wallmsg(f, from, filtered);
1903 (void) pthread_mutex_unlock(&wmp);
1906 * The contents of filtered have been copied
1907 * out to the struct walldev. We should free it here.
1910 free(filtered);
1912 /* exiting the switch */
1913 break;
1914 } else if (f->f_type != F_FILE) {
1915 /* CSTYLED */
1916 (void) strncpy(eomp, "\r\n", 3); /*lint !e669*/
1917 } else {
1918 if ((eomp2 = strchr(filtered, '\r')) != NULL) {
1919 (void) strncpy(eomp2, "\n", 2);
1920 } else {
1921 /* CSTYLED */
1922 (void) strncpy(eomp, "\n", 2); /*lint !e669*/
1925 if (write(f->f_file, filtered, strlen(filtered)) < 0) {
1926 int e = errno;
1928 if ((hup_state & HUP_INPROGRESS) &&
1929 f->f_type == F_UNUSED) {
1930 free(filtered);
1931 break;
1933 (void) close(f->f_file);
1935 * Check for EBADF on TTY's due
1936 * to vhangup() XXX
1938 if (e == EBADF && f->f_type != F_FILE) {
1939 f->f_file = open(f->f_un.f_fname,
1940 O_WRONLY|O_APPEND|O_NOCTTY);
1941 if (f->f_file < 0) {
1942 f->f_type = F_UNUSED;
1943 logerror(f->f_un.f_fname);
1944 f->f_stat.errs++;
1946 untty();
1947 } else {
1948 f->f_type = F_UNUSED;
1949 f->f_stat.errs++;
1950 errno = e;
1951 logerror(f->f_un.f_fname);
1953 } else if (flags & SYNC_FILE)
1954 if (((pri & LOG_FACMASK) >> 3) == LOG_KERN)
1955 (void) fsync(f->f_file);
1957 DPRINT2(5, "writemsg(%u): freeing filtered (%p)\n",
1958 mythreadno, (void *)filtered);
1960 free(filtered);
1961 break;
1966 * WALLMSG -- Write a message to the world at large
1968 * Write the specified message to either the entire
1969 * world, or a list of approved users.
1971 static void
1972 wallmsg(struct filed *f, char *from, char *msg)
1974 int i;
1975 size_t len, clen;
1976 char *buf = NULL;
1977 struct utmpx *utxp;
1978 time_t now;
1979 char line[512], dev[100];
1980 char cp[MAXLINE+1];
1981 struct stat statbuf;
1982 walldev_t *w;
1983 char cbuf[30];
1984 pthread_t mythreadno;
1986 if (Debug) {
1987 mythreadno = pthread_self();
1990 if (access(UTMPX_FILE, R_OK) != 0 || stat(UTMPX_FILE, &statbuf) != 0) {
1991 logerror(UTMPX_FILE);
1992 return;
1993 } else if (statbuf.st_uid != 0 || (statbuf.st_mode & 07777) != 0644) {
1994 (void) snprintf(line, sizeof (line), "%s %s", UTMPX_FILE,
1995 "not owned by root or not mode 644.\n"
1996 "This file must be owned by root "
1997 "and not writable by\n"
1998 "anyone other than root. This alert is being "
1999 "dropped because of\n"
2000 "this problem.");
2001 logerror(line);
2002 return;
2005 if (f->f_type == F_WALL) {
2006 (void) time(&now);
2007 len = snprintf(line, sizeof (line),
2008 "\r\n\7Message from syslogd@%s "
2009 "at %.24s ...\r\n", from, ctime_r(&now, cbuf));
2010 len += strlen(msg + 16);
2011 buf = (char *)malloc(len + 1);
2012 if (!buf) {
2013 MALLOC_FAIL("dropping message");
2014 return;
2016 DPRINT3(5, "wallmsg(%u): buf allocated (%p: %d bytes)\n",
2017 mythreadno, (void *)buf, len + 1);
2018 (void) strcpy(buf, line);
2019 (void) strcat(buf, msg + 16);
2020 clen = copy_frwd(cp, sizeof (cp), buf, len);
2021 DPRINT2(5, "wallmsg(%u): clen = %d\n",
2022 mythreadno, clen);
2023 DPRINT2(5, "wallmsg(%u): freeing buf (%p)\n",
2024 mythreadno, (void *)buf);
2025 free(buf);
2026 } else {
2027 clen = copy_frwd(cp, sizeof (cp), msg, strlen(msg));
2028 DPRINT2(5, "wallmsg(%u): clen = %d\n",
2029 mythreadno, clen);
2031 /* scan the user login file */
2032 setutxent();
2033 while ((utxp = getutxent()) != NULL) {
2034 /* is this slot used? */
2035 if (utxp->ut_name[0] == '\0' ||
2036 utxp->ut_line[0] == '\0' ||
2037 utxp->ut_type != USER_PROCESS)
2038 continue;
2039 /* should we send the message to this user? */
2040 if (f->f_type == F_USERS) {
2041 for (i = 0; i < MAXUNAMES; i++) {
2042 if (!f->f_un.f_uname[i][0]) {
2043 i = MAXUNAMES;
2044 break;
2046 if (strncmp(f->f_un.f_uname[i],
2047 utxp->ut_name, UNAMESZ) == 0)
2048 break;
2050 if (i >= MAXUNAMES)
2051 continue;
2054 /* compute the device name */
2055 if (utxp->ut_line[0] == '/') {
2056 (void) strncpy(dev, utxp->ut_line, UDEVSZ);
2057 } else {
2058 (void) strcpy(dev, "/dev/");
2059 (void) strncat(dev, utxp->ut_line, UDEVSZ);
2061 DPRINT2(1, "wallmsg(%u): write to '%s'\n", mythreadno,
2062 dev);
2064 if ((w = malloc(sizeof (walldev_t))) != NULL) {
2065 int rc;
2066 (void) pthread_attr_init(&w->thread_attr);
2067 (void) pthread_attr_setdetachstate(&w->thread_attr,
2068 PTHREAD_CREATE_DETACHED);
2069 (void) strncpy(w->dev, dev, PATH_MAX);
2070 (void) strncpy(w->msg, cp, MAXLINE+1);
2071 (void) strncpy(w->ut_name, utxp->ut_name,
2072 sizeof (w->ut_name));
2074 if ((rc = pthread_create(&w->thread, &w->thread_attr,
2075 writetodev, (void *) w)) != 0) {
2076 DPRINT2(5, "wallmsg(%u): wallmsg thread "
2077 "create failed rc = %d\n",
2078 mythreadno, rc);
2079 free(w);
2080 break;
2082 } else {
2083 MALLOC_FAIL("dropping message to user");
2086 /* close the user login file */
2087 endutxent();
2091 * Each time we need to write to a tty device (a potentially expensive
2092 * or long-running operation) this routine gets called as a new
2093 * detached, unbound thread. This allows writes to many devices
2094 * to proceed nearly in parallel, without having to resort to
2095 * asynchronous I/O or forking.
2097 static void *
2098 writetodev(void *ap)
2100 walldev_t *w = ap;
2101 int ttyf;
2102 int len;
2103 struct stat statb;
2104 struct passwd pw, *pwp;
2105 char pwbuf[MAXLINE];
2106 pthread_t mythreadno;
2108 if (Debug) {
2109 mythreadno = pthread_self();
2112 DPRINT1(1, "writetodev(%u): Device writer thread started\n",
2113 mythreadno);
2115 len = strlen(w->msg);
2117 ttyf = open(w->dev, O_WRONLY|O_NOCTTY|O_NDELAY);
2118 if (ttyf >= 0) {
2119 if (fstat(ttyf, &statb) != 0) {
2120 DPRINT2(1, "writetodev(%u): Can't stat '%s'\n",
2121 mythreadno, w->dev);
2122 errno = 0;
2123 logerror("Can't stat '%s'", w->dev);
2124 } else if (!(statb.st_mode & S_IWRITE)) {
2125 DPRINT2(1, "writetodev(%u): Can't write to "
2126 "'%s'\n", mythreadno, w->dev);
2127 } else if (!isatty(ttyf)) {
2128 DPRINT2(1, "writetodev(%u): '%s' not a tty\n",
2129 mythreadno, w->dev);
2131 * We might hit dtremote here. Don't generate
2132 * error message.
2134 } else if (getpwuid_r(statb.st_uid, &pw, pwbuf,
2135 sizeof (pwbuf), &pwp) != 0) {
2136 DPRINT2(1, "writetodev(%u): Can't determine owner "
2137 "of '%s'\n", mythreadno, w->dev);
2138 errno = 0;
2139 logerror("Can't determine owner of '%s'", w->dev);
2140 } else if (strncmp(pw.pw_name, w->ut_name, UNAMESZ) != 0) {
2141 DPRINT2(1, "writetodev(%u): Bad terminal owner '%s'"
2142 "\n", mythreadno, w->dev);
2143 errno = 0;
2144 logerror("%s %s owns '%s' %s %.*s",
2145 "Bad terminal owner;", pw.pw_name, w->dev,
2146 "but utmpx says", UNAMESZ, w->ut_name);
2147 } else if (write(ttyf, w->msg, len) != len) {
2148 DPRINT2(1, "writetodev(%u): Write failed to "
2149 "'%s'\n", mythreadno, w->dev);
2150 errno = 0;
2151 logerror("Write failed to '%s'", w->dev);
2154 DPRINT2(1, "writetodev(%u): write to '%s' succeeded\n",
2155 mythreadno, w->dev);
2157 (void) close(ttyf);
2158 } else {
2159 DPRINT2(1, "writetodev(%u): Can't open '%s'\n",
2160 mythreadno, w->dev);
2163 (void) pthread_attr_destroy(&w->thread_attr);
2164 free(w);
2166 DPRINT1(1, "writetodev(%u): Device writer thread exiting\n",
2167 mythreadno);
2169 pthread_exit(0);
2170 return (NULL);
2171 /*NOTREACHED*/
2175 * Return a printable representation of a host address. If unable to
2176 * look up hostname, format the numeric address for display instead.
2178 * First calls hnc_lookup to see if there is valid cache entry for
2179 * given network address. If it failed, cvthname looks up hostname,
2180 * and push the results into the hostname cache.
2182 static host_list_t *
2183 cvthname(struct netbuf *nbp, struct netconfig *ncp, char *failsafe_addr)
2185 int i;
2186 host_list_t *h;
2187 struct nd_hostservlist *hsp;
2188 struct nd_hostserv *hspp;
2189 pthread_t mythreadno;
2190 int hindex;
2191 char *uap;
2193 if (Debug) {
2194 mythreadno = pthread_self();
2197 if (Debug)
2198 uap = taddr2uaddr(ncp, nbp);
2200 DPRINT2(2, "cvthname(%u): looking up hostname for %s\n",
2201 mythreadno, uap ? uap : "<unknown>");
2203 if ((h = hnc_lookup(nbp, ncp, &hindex)) != NULL) {
2204 DPRINT4(2, "cvthname(%u): Cache found %p for %s (%s)\n",
2205 mythreadno, (void *)h, uap ? uap : "<unknown>",
2206 h->hl_hosts[0]);
2207 return (h);
2209 DPRINT2(2, "cvthname(%u): No cache found for %s\n",
2210 mythreadno, uap ? uap : "<unknown>");
2212 if (Debug)
2213 free(uap);
2215 if (ncp->nc_semantics != NC_TPI_CLTS) {
2216 return (NULL);
2219 /* memory allocation failure here is fatal */
2220 if ((h = malloc(sizeof (host_list_t))) == NULL) {
2221 MALLOC_FAIL("host name conversion");
2222 return (NULL);
2225 if (netdir_getbyaddr(ncp, &hsp, nbp) == 0) {
2226 if (hsp->h_cnt <= 0) {
2227 out: netdir_free((void *)hsp, ND_HOSTSERVLIST);
2228 free(h);
2229 return (NULL);
2232 hspp = hsp->h_hostservs;
2233 h->hl_cnt = hsp->h_cnt;
2234 h->hl_hosts = (char **)malloc(sizeof (char *) * (h->hl_cnt));
2235 if (h->hl_hosts == NULL) {
2236 MALLOC_FAIL("host name conversion");
2237 goto out;
2240 DPRINT2(2, "cvthname(%u): Found %d hostnames\n",
2241 mythreadno, h->hl_cnt);
2242 for (i = 0; i < h->hl_cnt; i++) {
2243 h->hl_hosts[i] = (char *)
2244 malloc(sizeof (char) * (strlen(hspp->h_host) + 1));
2245 if (h->hl_hosts[i] == NULL) {
2246 int j;
2247 for (j = 0; j < i; j++) {
2248 free(h->hl_hosts[j]);
2250 free(h->hl_hosts);
2251 MALLOC_FAIL("host name conversion");
2252 goto out;
2254 (void) strcpy(h->hl_hosts[i], hspp->h_host);
2255 hspp++;
2257 netdir_free((void *)hsp, ND_HOSTSERVLIST);
2258 } else { /* unknown address */
2259 h->hl_cnt = 1;
2260 h->hl_hosts = (char **)malloc(sizeof (char *));
2261 if (h->hl_hosts == NULL) {
2262 free(h);
2263 MALLOC_FAIL("host name conversion");
2264 return (NULL);
2266 h->hl_hosts[0] = (char *)malloc(strlen(failsafe_addr) + 3);
2267 if (h->hl_hosts[0] == NULL) {
2268 free(h->hl_hosts);
2269 free(h);
2270 MALLOC_FAIL("host name conversion");
2271 return (NULL);
2273 /*LINTED*/
2274 (void) sprintf(h->hl_hosts[0], "[%s]", failsafe_addr);
2275 DPRINT2(1, "cvthname(%u): Hostname lookup failed "
2276 "- using address %s instead\n",
2277 mythreadno, h->hl_hosts[0]);
2280 h->hl_refcnt = 1;
2281 if (pthread_mutex_init(&h->hl_mutex, NULL) != 0) {
2282 logerror("pthread_mutex_init failed");
2283 /* This host_list won't be shared by the cache. */
2284 return (h);
2286 hnc_register(nbp, ncp, h, hindex);
2287 DPRINT3(2, "cvthname(%u): returning %p for %s\n",
2288 mythreadno, (void *)h, h->hl_hosts[0]);
2289 return (h);
2293 * Print syslogd errors some place. Need to be careful here, because
2294 * this routine is called at times when we're not initialized and
2295 * ready to log messages...in this case, fall back to using the console.
2297 void
2298 logerror(const char *type, ...)
2300 char buf[MAXLINE+1];
2301 pthread_t mythreadno;
2302 int flag;
2303 va_list ap;
2305 if (Debug) {
2306 mythreadno = pthread_self();
2309 va_start(ap, type);
2310 logerror_format(type, buf, ap);
2311 va_end(ap);
2312 DPRINT2(1, "logerror(%u): %s\n", mythreadno, buf);
2314 (void) pthread_mutex_lock(&logerror_lock);
2315 if (!interrorlog) {
2316 flag = 0;
2317 if (logerror_to_console(1, buf) == 0) {
2318 /* has written to the console */
2319 flag = IGN_CONS;
2321 (void) logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE|flag, 1);
2322 } else {
2323 if (logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE, 0) == -1) {
2324 (void) logerror_to_console(1, buf);
2327 (void) pthread_mutex_unlock(&logerror_lock);
2329 errno = 0;
2330 t_errno = 0;
2333 static void
2334 logerror_format(const char *type, char *buf, va_list ap)
2336 char tmpbuf[MAXLINE + 1];
2337 pthread_t mythreadno;
2339 if (Debug) {
2340 mythreadno = pthread_self();
2343 (void) vsnprintf(tmpbuf, MAXLINE, type, ap);
2345 if (t_errno == 0 || t_errno == TSYSERR) {
2346 char *errstr;
2348 if (errno == 0) {
2349 (void) snprintf(buf, MAXLINE, "syslogd: %.*s",
2350 MAXLINE, tmpbuf);
2351 } else if ((errstr = strerror(errno)) == NULL) {
2352 (void) snprintf(buf, MAXLINE, "syslogd: %s: error"
2353 " %d", tmpbuf, errno);
2354 } else {
2355 (void) snprintf(buf, MAXLINE, "syslogd: %s: %s",
2356 tmpbuf, errstr);
2358 } else {
2359 if (t_errno > t_nerr) {
2360 (void) snprintf(buf, MAXLINE, "syslogd: %s:"
2361 " t_error %d", tmpbuf, t_errno);
2362 } else {
2363 (void) snprintf(buf, MAXLINE, "syslogd: %s: %s",
2364 tmpbuf, t_errlist[t_errno]);
2368 DPRINT2(5, "logerror_format(%u): out %s\n", mythreadno, buf);
2371 static int
2372 logerror_to_console(int nonblock, const char *buf)
2374 int cfd, modes;
2375 pthread_t mythreadno;
2376 int ret = 0, len;
2377 char tmpbuf[MAXLINE + 1];
2379 if (Debug) {
2380 mythreadno = pthread_self();
2383 DPRINT2(1, "logerror_to_console(%u): %s\n", mythreadno, buf);
2386 * must use open here instead of fopen, because
2387 * we need the O_NOCTTY behavior - otherwise we
2388 * could hang the console at boot time
2391 modes = (nonblock) ?
2392 O_WRONLY|O_APPEND|O_NOCTTY|O_NONBLOCK :
2393 O_WRONLY|O_APPEND|O_NOCTTY;
2395 if (((cfd = open(sysmsg, modes)) >= 0) ||
2396 ((cfd = open(ctty, modes)) >= 0)) {
2397 (void) snprintf(tmpbuf, MAXLINE, "%s\n", buf);
2398 len = strlen(tmpbuf);
2399 if (write(cfd, tmpbuf, len) != len) {
2400 ret = 1;
2402 (void) close(cfd);
2403 } else {
2404 ret = 1;
2406 /* punt */
2407 DPRINT1(1, "logerror_console(%u): can't open console\n",
2408 mythreadno);
2410 return (ret);
2414 * copy current message to saved message in filed structure.
2416 static void
2417 copy_msg(struct filed *f)
2419 (void) strlcpy(f->f_prevmsg.msg, f->f_current.msg, MAXLINE+1);
2420 (void) strlcpy(f->f_prevmsg.host, f->f_current.host, SYS_NMLN);
2421 f->f_prevmsg.pri = f->f_current.pri;
2422 f->f_prevmsg.flags = f->f_current.flags;
2423 f->f_prevmsg.time = f->f_current.time;
2424 f->f_msgflag |= OLD_VALID;
2429 * function to free a host_list_t struct that was allocated
2430 * out of cvthname(). There is a special case where we don't
2431 * free the hostname list in LocalHostName, because that's
2432 * our own addresses, and we just want to have to look it
2433 * up once and save it. Also don't free it if it's
2434 * NullHostName, because that's a special one we use if
2435 * name service lookup fails.
2437 * By having hostname cache, now host_list_t will be shared
2438 * by messages and hostname cache. hl_refcnt is used for
2439 * the purpose.
2441 static void
2442 freehl(host_list_t *h)
2444 int i, refcnt;
2445 pthread_t mythreadno;
2447 if (Debug) {
2448 mythreadno = pthread_self();
2451 DPRINT2(2, "freehl(%u): releasing %p\n", mythreadno, (void *)h);
2453 if (h == NULL || h == &LocalHostName || h == &NullHostName) {
2454 return;
2457 (void) pthread_mutex_lock(&h->hl_mutex);
2458 refcnt = --h->hl_refcnt;
2459 (void) pthread_mutex_unlock(&h->hl_mutex);
2461 if (refcnt != 0) {
2462 DPRINT3(5, "freehl(%u): %p has reference %d\n",
2463 mythreadno, (void *)h, refcnt);
2464 return;
2467 (void) pthread_mutex_destroy(&h->hl_mutex);
2469 DPRINT2(5, "freehl(%u): freeing %p\n", mythreadno, (void *)h);
2471 for (i = 0; i < h->hl_cnt; i++) {
2472 free(h->hl_hosts[i]);
2475 free(h->hl_hosts);
2476 free(h);
2480 * Create the door file and the pid file in /var/run. If the filesystem
2481 * containing /etc is writable, create symlinks /etc/.syslog_door and
2482 * /etc/syslog.pid to them. On systems that do not support /var/run, create
2483 * /etc/.syslog_door and /etc/syslog.pid directly.
2485 * Note: it is not considered fatal to fail to create the pid file or its
2486 * symlink. Attempts to use them in the usual way will fail, of course, but
2487 * syslogd will function nicely without it (not so for the door file).
2490 static void
2491 open_door(void)
2493 struct stat buf;
2494 door_info_t info;
2495 char line[MAXLINE+1];
2496 pthread_t mythreadno;
2497 int err;
2499 if (Debug) {
2500 mythreadno = pthread_self();
2504 * first see if another syslogd is running by trying
2505 * a door call - if it succeeds, there is already
2506 * a syslogd process active
2509 if (!DoorCreated) {
2510 int door;
2512 if ((door = open(DoorFileName, O_RDONLY)) >= 0) {
2513 DPRINT2(5, "open_door(%u): %s opened "
2514 "successfully\n", mythreadno, DoorFileName);
2516 if (door_info(door, &info) >= 0) {
2517 DPRINT2(5, "open_door(%u): "
2518 "door_info:info.di_target = %ld\n",
2519 mythreadno, info.di_target);
2521 if (info.di_target > 0) {
2522 (void) sprintf(line, "syslogd pid %ld"
2523 " already running. Cannot "
2524 "start another syslogd pid %ld",
2525 info.di_target, getpid());
2526 DPRINT2(5, "open_door(%u): error: "
2527 "%s\n", mythreadno, line);
2528 errno = 0;
2529 logerror(line);
2530 exit(1);
2534 (void) close(door);
2535 } else {
2536 if (lstat(DoorFileName, &buf) < 0) {
2537 err = errno;
2539 DPRINT3(5, "open_door(%u): lstat() of %s "
2540 "failed, errno=%d\n",
2541 mythreadno, DoorFileName, err);
2543 if ((door = creat(DoorFileName, 0644)) < 0) {
2544 err = errno;
2545 (void) snprintf(line, sizeof (line),
2546 "creat() of %s failed - fatal",
2547 DoorFileName);
2548 DPRINT3(1, "open_door(%u): error: %s, "
2549 "errno=%d\n", mythreadno, line,
2550 err);
2551 errno = err;
2552 logerror(line);
2553 delete_doorfiles();
2554 exit(1);
2557 (void) fchmod(door,
2558 S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
2560 DPRINT2(5, "open_door(%u): creat() of %s "
2561 "succeeded\n", mythreadno,
2562 DoorFileName);
2564 (void) close(door);
2568 if (strcmp(DoorFileName, DOORFILE) == 0) {
2569 if (lstat(OLD_DOORFILE, &buf) == 0) {
2570 DPRINT2(5, "open_door(%u): lstat() of %s "
2571 "succeeded\n", mythreadno,
2572 OLD_DOORFILE);
2574 if (S_ISDIR(buf.st_mode)) {
2575 (void) snprintf(line, sizeof (line),
2576 "%s is a directory - fatal",
2577 OLD_DOORFILE);
2578 DPRINT2(1, "open_door(%u): error: "
2579 "%s\n", mythreadno, line);
2580 errno = 0;
2581 logerror(line);
2582 delete_doorfiles();
2583 exit(1);
2586 DPRINT2(5, "open_door(%u): %s is not a "
2587 "directory\n",
2588 mythreadno, OLD_DOORFILE);
2590 if (unlink(OLD_DOORFILE) < 0) {
2591 err = errno;
2592 (void) snprintf(line, sizeof (line),
2593 "unlink() of %s failed",
2594 OLD_DOORFILE);
2595 DPRINT2(5, "open_door(%u): %s\n",
2596 mythreadno, line);
2598 if (err != EROFS) {
2599 DPRINT3(1, "open_door(%u): "
2600 "error: %s, "
2601 "errno=%d\n",
2602 mythreadno, line, err);
2603 (void) strcat(line, " - fatal");
2604 errno = err;
2605 logerror(line);
2606 delete_doorfiles();
2607 exit(1);
2610 DPRINT1(5, "open_door(%u): unlink "
2611 "failure OK on RO file "
2612 "system\n", mythreadno);
2614 } else {
2615 DPRINT2(5, "open_door(%u): file %s doesn't "
2616 "exist\n", mythreadno, OLD_DOORFILE);
2619 if (symlink(RELATIVE_DOORFILE, OLD_DOORFILE) < 0) {
2620 err = errno;
2621 (void) snprintf(line, sizeof (line),
2622 "symlink %s -> %s failed", OLD_DOORFILE,
2623 RELATIVE_DOORFILE);
2624 DPRINT2(5, "open_door(%u): %s\n", mythreadno,
2625 line);
2627 if (err != EROFS) {
2628 DPRINT3(1, "open_door(%u): error: %s, "
2629 "errno=%d\n", mythreadno, line,
2630 err);
2631 errno = err;
2632 (void) strcat(line, " - fatal");
2633 logerror(line);
2634 delete_doorfiles();
2635 exit(1);
2638 DPRINT1(5, "open_door(%u): symlink failure OK "
2639 "on RO file system\n", mythreadno);
2640 } else {
2641 DPRINT3(5, "open_door(%u): symlink %s -> %s "
2642 "succeeded\n", mythreadno,
2643 OLD_DOORFILE, RELATIVE_DOORFILE);
2647 if ((DoorFd = door_create(server, 0,
2648 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
2649 err = errno;
2650 (void) sprintf(line, "door_create() failed - fatal");
2651 DPRINT3(1, "open_door(%u): error: %s, errno=%d\n",
2652 mythreadno, line, err);
2653 errno = err;
2654 logerror(line);
2655 delete_doorfiles();
2656 exit(1);
2658 (void) door_setparam(DoorFd, DOOR_PARAM_DATA_MAX, 0);
2659 DPRINT2(5, "open_door(%u): door_create() succeeded, "
2660 "DoorFd=%d\n", mythreadno, DoorFd);
2662 DoorCreated = 1;
2665 (void) fdetach(DoorFileName); /* just in case... */
2667 (void) door_server_create(door_server_pool);
2669 if (fattach(DoorFd, DoorFileName) < 0) {
2670 err = errno;
2671 (void) snprintf(line, sizeof (line), "fattach() of fd"
2672 " %d to %s failed - fatal", DoorFd, DoorFileName);
2673 DPRINT3(1, "open_door(%u): error: %s, errno=%d\n", mythreadno,
2674 line, err);
2675 errno = err;
2676 logerror(line);
2677 delete_doorfiles();
2678 exit(1);
2681 DPRINT2(5, "open_door(%u): attached server() to %s\n", mythreadno,
2682 DoorFileName);
2685 * create pidfile anyway, so those using it to control
2686 * syslogd (with kill `cat /etc/syslog.pid` perhaps)
2687 * don't get broken.
2690 if (!PidfileCreated) {
2691 int pidfd;
2693 PidfileCreated = 1;
2695 if ((pidfd = open(PidFileName, O_RDWR|O_CREAT|O_TRUNC, 0644))
2696 < 0) {
2697 err = errno;
2698 (void) snprintf(line, sizeof (line),
2699 "open() of %s failed", PidFileName);
2700 DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n",
2701 mythreadno, line, err);
2702 errno = err;
2703 logerror(line);
2704 return;
2707 (void) fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
2708 (void) sprintf(line, "%ld\n", getpid());
2710 if (write(pidfd, line, strlen(line)) < 0) {
2711 err = errno;
2712 (void) snprintf(line, sizeof (line),
2713 "write to %s on fd %d failed", PidFileName, pidfd);
2714 DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n",
2715 mythreadno, line, err);
2716 errno = err;
2717 logerror(line);
2718 return;
2721 (void) close(pidfd);
2723 DPRINT2(5, "open_door(%u): %s created\n",
2724 mythreadno, PidFileName);
2726 if (strcmp(PidFileName, PIDFILE) == 0) {
2727 if (lstat(OLD_PIDFILE, &buf) == 0) {
2728 DPRINT2(5, "open_door(%u): lstat() of %s "
2729 "succeded\n", mythreadno, OLD_PIDFILE);
2731 if (S_ISDIR(buf.st_mode)) {
2732 (void) snprintf(line, sizeof (line),
2733 "file %s is a directory",
2734 OLD_PIDFILE);
2735 DPRINT2(1, "open_door(%u): warning: "
2736 "%s\n", mythreadno, line);
2737 errno = 0;
2738 logerror(line);
2739 return;
2742 if (unlink(OLD_PIDFILE) < 0) {
2743 err = errno;
2744 (void) snprintf(line, sizeof (line),
2745 "unlink() of %s failed",
2746 OLD_PIDFILE);
2747 DPRINT2(5, "open_door(%u): %s\n",
2748 mythreadno, line);
2750 if (err != EROFS) {
2751 DPRINT3(1, "open_door (%u): "
2752 "warning: %s, "
2753 "errno=%d\n",
2754 mythreadno, line, err);
2755 errno = err;
2756 logerror(line);
2757 return;
2760 DPRINT1(5, "open_door(%u): unlink "
2761 "failure OK on RO file "
2762 "system\n", mythreadno);
2764 } else {
2765 DPRINT2(5, "open_door(%u): file %s doesn't "
2766 "exist\n", mythreadno, OLD_PIDFILE);
2769 if (symlink(RELATIVE_PIDFILE, OLD_PIDFILE) < 0) {
2770 err = errno;
2771 (void) snprintf(line, sizeof (line),
2772 "symlink %s -> %s failed", OLD_PIDFILE,
2773 RELATIVE_PIDFILE);
2774 DPRINT2(5, "open_door(%u): %s\n", mythreadno,
2775 line);
2777 if (err != EROFS) {
2778 DPRINT3(1, "open_door(%u): warning: "
2779 "%s, errno=%d\n", mythreadno,
2780 line, err);
2781 errno = err;
2782 logerror(line);
2783 return;
2786 DPRINT1(5, "open_door(%u): symlink failure OK "
2787 "on RO file system\n", mythreadno);
2788 return;
2791 DPRINT3(5, "open_door(%u): symlink %s -> %s "
2792 "succeeded\n", mythreadno, OLD_PIDFILE,
2793 RELATIVE_PIDFILE);
2799 * the 'server' function that we export via the door. It does
2800 * nothing but return.
2802 /*ARGSUSED*/
2803 static void
2804 server(void *cookie, char *argp, size_t arg_size,
2805 door_desc_t *dp, uint_t n)
2807 (void) door_return(NULL, 0, NULL, 0);
2808 /* NOTREACHED */
2811 /*ARGSUSED*/
2812 static void *
2813 create_door_thr(void *arg)
2815 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
2816 (void) door_return(NULL, 0, NULL, 0);
2819 * If there is an error in door_return(), it will return here and
2820 * the thread will exit. Hence we need to decrement door_server_cnt.
2822 (void) pthread_mutex_lock(&door_server_cnt_lock);
2823 door_server_cnt--;
2824 (void) pthread_mutex_unlock(&door_server_cnt_lock);
2825 return (NULL);
2829 * Max number of door server threads for syslogd. Since door is used
2830 * to check the health of syslogd, we don't need large number of
2831 * server threads.
2833 #define MAX_DOOR_SERVER_THR 3
2836 * Manage door server thread pool.
2838 /*ARGSUSED*/
2839 static void
2840 door_server_pool(door_info_t *dip)
2842 (void) pthread_mutex_lock(&door_server_cnt_lock);
2843 if (door_server_cnt <= MAX_DOOR_SERVER_THR &&
2844 pthread_create(NULL, &door_thr_attr, create_door_thr, NULL) == 0) {
2845 door_server_cnt++;
2846 (void) pthread_mutex_unlock(&door_server_cnt_lock);
2847 return;
2850 (void) pthread_mutex_unlock(&door_server_cnt_lock);
2854 * checkm4 - used to verify that the external utilities that
2855 * syslogd depends on are where we expect them to be.
2856 * Returns 0 if all utilities are found, > 0 if any are missing.
2857 * Also logs errors so user knows what's missing
2859 static int
2860 checkm4(void)
2862 int notfound = 0;
2863 int saverrno;
2864 pthread_t mythreadno;
2866 if (Debug) {
2867 mythreadno = pthread_self();
2870 if (access("/usr/bin/m4", X_OK) < 0) {
2871 saverrno = errno;
2872 logerror("/usr/bin/m4");
2873 DPRINT2(1, "checkm4(%u): /usr/bin/m4 - access "
2874 "returned %d\n", mythreadno, saverrno);
2875 notfound++;
2878 return (notfound);
2882 * INIT -- Initialize syslogd from configuration table, start up
2883 * input and logger threads. This routine is called only once.
2885 static void
2886 init(void)
2888 struct utsname *up;
2889 pthread_attr_t sys_attr, net_attr, log_attr, hnl_attr;
2890 int nthread;
2891 pthread_t mythreadno;
2893 if (Debug) {
2894 mythreadno = pthread_self();
2897 DPRINT1(2, "init(%u): initializing\n", mythreadno);
2899 /* hand-craft a host_list_t entry for our local host name */
2900 if ((up = malloc(sizeof (struct utsname))) == NULL) {
2901 MALLOC_FAIL_EXIT;
2903 (void) uname(up);
2904 LocalHostName.hl_cnt = 1;
2905 if ((LocalHostName.hl_hosts = malloc(sizeof (char *))) == NULL) {
2906 MALLOC_FAIL_EXIT;
2908 if ((LocalHostName.hl_hosts[0] = strdup(up->nodename)) == NULL) {
2909 free(LocalHostName.hl_hosts);
2910 MALLOC_FAIL_EXIT;
2912 free(up);
2913 /* also hand craft one for use if name resolution fails */
2914 NullHostName.hl_cnt = 1;
2915 if ((NullHostName.hl_hosts = malloc(sizeof (char *))) == NULL) {
2916 MALLOC_FAIL_EXIT;
2918 if ((NullHostName.hl_hosts[0] = strdup("name lookup failed")) == NULL) {
2919 MALLOC_FAIL_EXIT;
2922 hnc_init(0);
2925 * Note that getnets will allocate network resources, but won't be
2926 * binding UDP port. This is because, there could be a race
2927 * condition between door. If we bind here, one syslogd could grab
2928 * UDP port first, but later another syslogd could take over without
2929 * getting UDP port but grab the door file. The 2nd syslogd could
2930 * continue to run without listening network.
2931 * bindnet() will be called after door was successfully opened.
2933 getnets();
2936 * Start up configured theads
2938 conf_init();
2941 * allocate thread stacks for the persistant threads
2943 nthread = (turnoff == 0) ? 4 : 2;
2945 if ((stack_ptr = alloc_stacks(nthread)) == NULL) {
2946 logerror("alloc_stacks failed - fatal");
2947 exit(1);
2950 if (Debug) {
2951 dumpstats(STDOUT_FILENO);
2954 (void) dataq_init(&inputq); /* init the input queue */
2956 if (pthread_attr_init(&sys_attr) != 0 ||
2957 pthread_attr_init(&log_attr) != 0 ||
2958 pthread_attr_init(&net_attr) != 0 ||
2959 pthread_attr_init(&hnl_attr) != 0 ||
2960 pthread_attr_init(&door_thr_attr) != 0) {
2961 logerror("pthread_attr_init failed - fatal");
2962 exit(1);
2965 (void) pthread_attr_setscope(&sys_attr, PTHREAD_SCOPE_PROCESS);
2966 (void) pthread_attr_setscope(&log_attr, PTHREAD_SCOPE_PROCESS);
2967 (void) pthread_attr_setscope(&net_attr, PTHREAD_SCOPE_PROCESS);
2968 (void) pthread_attr_setscope(&hnl_attr, PTHREAD_SCOPE_PROCESS);
2969 (void) pthread_attr_setscope(&door_thr_attr, PTHREAD_SCOPE_SYSTEM);
2970 (void) pthread_attr_setdetachstate(&door_thr_attr,
2971 PTHREAD_CREATE_DETACHED);
2973 /* 1: logmsg thread */
2974 (void) pthread_attr_setstacksize(&log_attr, stacksize);
2975 (void) pthread_attr_setstackaddr(&log_attr, stack_ptr);
2976 stack_ptr += stacksize + redzonesize;
2977 if (pthread_create(&log_thread, &log_attr, logmsg, NULL) != 0) {
2978 logerror("pthread_create failed - fatal");
2979 exit(1);
2983 * open the log device, and pull up all pending message
2984 * from the log driver.
2986 prepare_sys_poll();
2989 * Now we can deliver the pending internal error messages.
2991 enable_errorlog();
2993 /* 2: sys_poll thread */
2994 (void) pthread_attr_setstacksize(&sys_attr, stacksize);
2995 (void) pthread_attr_setstackaddr(&sys_attr, stack_ptr);
2996 stack_ptr += stacksize + redzonesize;
2997 if (pthread_create(&sys_thread, &sys_attr, sys_poll, NULL) != 0) {
2998 logerror("pthread_create failed - fatal");
2999 exit(1);
3003 * We've started the sys_poll() and logmsg() threads. Now we are ready
3004 * to open the door. This cannot happen before spawning sys_poll(),
3005 * because after opening the door, syslog() will no longer take care of
3006 * LOG_CONS. Therefor, we should pull up all pending log messages and
3007 * activate sys_poll() before opening the door, so that log driver
3008 * won't drop messages.
3010 open_door();
3012 DPRINT1(1, "init(%u): accepting messages from local system\n",
3013 mythreadno);
3015 if (turnoff == 0) {
3016 /* init the hostname lookup queue */
3017 (void) dataq_init(&hnlq);
3019 /* 3: hostname lookup thread */
3020 (void) pthread_attr_setstacksize(&hnl_attr, stacksize);
3021 (void) pthread_attr_setstackaddr(&hnl_attr, stack_ptr);
3022 stack_ptr += stacksize + redzonesize;
3023 if (pthread_create(&hnl_thread, &hnl_attr,
3024 hostname_lookup, NULL) != 0) {
3025 logerror("pthread_create failed - fatal");
3026 exit(1);
3029 /* 4: net_poll thread */
3030 (void) pthread_attr_setstacksize(&net_attr, stacksize);
3031 (void) pthread_attr_setstackaddr(&net_attr, stack_ptr);
3032 stack_ptr += stacksize + redzonesize;
3034 /* grab UDP port */
3035 bindnet();
3037 if (pthread_create(&net_thread, &net_attr, net_poll,
3038 NULL) != 0) {
3039 logerror("pthread_create failed - fatal");
3040 exit(1);
3042 DPRINT1(1, "init(%u): accepting messages from remote\n",
3043 mythreadno);
3046 (void) pthread_attr_destroy(&sys_attr);
3047 (void) pthread_attr_destroy(&net_attr);
3048 (void) pthread_attr_destroy(&log_attr);
3049 (void) pthread_attr_destroy(&hnl_attr);
3051 curalarm = MarkInterval * 60 / MARKCOUNT;
3052 (void) alarm((unsigned)curalarm);
3053 DPRINT2(2, "init(%u): Next alarm in %d seconds\n",
3054 mythreadno, curalarm);
3055 DPRINT1(1, "init(%u): syslogd: started\n", mythreadno);
3059 * will print a bunch of debugging stats on 'fd'
3061 static void
3062 dumpstats(int fd)
3064 FILE *out;
3065 struct filed *f;
3066 int i;
3067 char users[1024];
3068 char cbuf[30];
3069 char *dashes = "------------------------";
3070 static int conversion_printed;
3072 if ((out = fdopen(fd, "w+")) == NULL)
3073 return;
3075 (void) fprintf(out, "\nSyslogd started: %s",
3076 ctime_r(&start_time, cbuf));
3077 (void) fprintf(out, "Input message count: system %d, network %d\n",
3078 sys_msg_count, net_msg_count);
3079 (void) fprintf(out, "# Outputs: %d\n\n", nlogs);
3081 (void) fprintf(out, "%s priority = [file, facility] %s\n\n",
3082 dashes, dashes);
3084 for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3085 (void) fprintf(out, "%d ", i / 10);
3087 (void) fprintf(out, "\n");
3088 for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3089 (void) fprintf(out, "%d ", i % 10);
3091 (void) fprintf(out, "\n");
3092 for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3093 (void) fprintf(out, "--");
3095 (void) fprintf(out, "\n");
3097 for (f = Files; f < &Files[nlogs]; f++) {
3098 for (i = 0; i < LOG_NFACILITIES + 1; i++) {
3099 if (f->f_pmask[i] == NOPRI)
3100 (void) fprintf(out, "X ");
3101 else
3102 (void) fprintf(out, "%d ",
3103 f->f_pmask[i]);
3105 (void) fprintf(out, "%s: ", TypeNames[f->f_type]);
3106 switch (f->f_type) {
3107 case F_FILE:
3108 case F_TTY:
3109 case F_CONSOLE:
3110 (void) fprintf(out, "%s", f->f_un.f_fname);
3111 break;
3112 case F_FORW:
3113 (void) fprintf(out, "%s", f->f_un.f_forw.f_hname);
3114 break;
3115 case F_USERS:
3116 for (i = 0; i < MAXUNAMES &&
3117 *f->f_un.f_uname[i]; i++) {
3118 if (!i)
3119 (void) fprintf(out, "%s",
3120 f->f_un.f_uname[i]);
3121 else
3122 (void) fprintf(out, ", %s",
3123 f->f_un.f_uname[i]);
3125 break;
3127 (void) fprintf(out, "\n");
3130 if (!conversion_printed) {
3131 (void) fprintf(out, "\nFacilities:\n");
3133 for (i = 0; FacNames[i].c_val != -1; i++) {
3134 (void) fprintf(out, " [%02d] %s: %3d\n", i,
3135 FacNames[i].c_name, FacNames[i].c_val);
3138 (void) fprintf(out, "\nPriorities:\n");
3140 for (i = 0; PriNames[i].c_val != -1; i++) {
3141 (void) fprintf(out, " [%02d] %s: %3d\n", i,
3142 PriNames[i].c_name, PriNames[i].c_val);
3145 conversion_printed = 1;
3148 (void) fprintf(out, "\n\n\n\t\tPer File Statistics\n");
3149 (void) fprintf(out, "%-24s\tTot\tDups\tNofwd\tErrs\n", "File");
3150 (void) fprintf(out, "%-24s\t---\t----\t-----\t----\n", "----");
3151 for (f = Files; f < &Files[nlogs]; f++) {
3152 switch (f->f_type) {
3153 case F_FILE:
3154 case F_TTY:
3155 case F_CONSOLE:
3156 (void) fprintf(out, "%-24s", f->f_un.f_fname);
3157 break;
3158 case F_WALL:
3159 (void) fprintf(out, "%-24s", TypeNames[f->f_type]);
3160 break;
3161 case F_FORW:
3162 (void) fprintf(out, "%-24s", f->f_un.f_forw.f_hname);
3163 break;
3164 case F_USERS:
3165 for (i = 0; i < MAXUNAMES &&
3166 *f->f_un.f_uname[i]; i++) {
3167 if (!i)
3168 (void) strcpy(users,
3169 f->f_un.f_uname[i]);
3170 else {
3171 (void) strcat(users, ",");
3172 (void) strcat(users,
3173 f->f_un.f_uname[i]);
3176 (void) fprintf(out, "%-24s", users);
3177 break;
3179 (void) fprintf(out, "\t%d\t%d\t%d\t%d\n",
3180 f->f_stat.total, f->f_stat.dups,
3181 f->f_stat.cantfwd, f->f_stat.errs);
3183 (void) fprintf(out, "\n\n");
3184 if (Debug && fd == 1)
3185 return;
3186 (void) fclose(out);
3190 * conf_init - This routine is code seperated from the
3191 * init routine in order to be re-callable when we get
3192 * a SIGHUP signal.
3194 static void
3195 conf_init(void)
3197 char *p;
3198 int i;
3199 struct filed *f;
3200 char *m4argv[4];
3201 int m4argc = 0;
3202 conf_t cf;
3203 pthread_t mythreadno;
3205 if (Debug) {
3206 mythreadno = pthread_self();
3209 DPRINT1(2, "conf_init(%u): starting logger threads\n",
3210 mythreadno);
3212 m4argv[m4argc++] = "m4";
3214 if (amiloghost() == 1) {
3215 DPRINT1(1, "conf_init(%u): I am loghost\n", mythreadno);
3216 m4argv[m4argc++] = "-DLOGHOST=1";
3219 m4argv[m4argc++] = ConfFile;
3220 m4argv[m4argc] = NULL;
3223 * Make sure the configuration file and m4 exist, and then parse
3224 * the configuration file with m4. If any of these fail, resort
3225 * to our hardcoded fallback configuration.
3228 if (access(ConfFile, R_OK) == -1) {
3229 DPRINT2(1, "conf_init(%u): %s does not exist\n", mythreadno,
3230 ConfFile);
3231 logerror("can't open configuration file");
3232 /* CSTYLED */
3233 Files = (struct filed *) &fallback; /*lint !e545 */
3234 cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3235 cfline("*.PANIC\t*", 0, &Files[1]);
3236 nlogs = 2;
3237 goto nofile;
3240 if (checkm4() != 0 || conf_open(&cf, "/usr/bin/m4", m4argv) == -1) {
3241 DPRINT2(1, "conf_init(%u): cannot open %s\n", mythreadno,
3242 ConfFile);
3243 /* CSTYLED */
3244 Files = (struct filed *) &fallback; /*lint !e545 */
3245 cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3246 cfline("*.PANIC\t*", 0, &Files[1]);
3247 nlogs = 2;
3248 goto nofile;
3251 /* Count the number of lines which are not blanks or comments */
3252 nlogs = 0;
3253 while ((p = conf_read(&cf)) != NULL) {
3254 if (p[0] != '\0' && p[0] != '#')
3255 nlogs++;
3258 Files = (struct filed *)malloc(sizeof (struct filed) * nlogs);
3260 if (!Files) {
3261 DPRINT1(1, "conf_init(%u): malloc failed - can't "
3262 "allocate 'Files' array\n", mythreadno);
3263 MALLOC_FAIL("loading minimum configuration");
3264 /* CSTYLED */
3265 Files = (struct filed *) &fallback; /*lint !e545 */
3266 cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]);
3267 cfline("*.PANIC\t*", 0, &Files[1]);
3268 nlogs = 2;
3269 conf_close(&cf);
3270 goto nofile;
3274 * Foreach line in the conf table, open that file.
3276 conf_rewind(&cf);
3277 f = Files;
3278 i = 0;
3279 while (((p = conf_read(&cf)) != NULL) && (f < &Files[nlogs])) {
3280 i++;
3281 /* check for end-of-section */
3282 if (p[0] == '\0' || p[0] == '#')
3283 continue;
3285 cfline(p, i, f);
3286 if (f->f_type == F_UNUSED)
3287 nlogs--;
3288 else
3289 f++;
3292 conf_close(&cf);
3295 * See if marks are to be written to any files. If so, set up a
3296 * timeout for marks.
3298 nofile:
3299 Marking = 0;
3302 * allocate thread stacks - one for each logger thread.
3304 if ((cstack_ptr = alloc_stacks(nlogs)) == NULL) {
3305 logerror("alloc_stacks failed - fatal");
3306 exit(1);
3309 /* And now one thread for each configured file */
3310 for (f = Files; f < &Files[nlogs]; f++) {
3311 if (filed_init(f) != 0) {
3312 logerror("pthread_create failed - fatal");
3313 exit(1);
3316 (void) pthread_mutex_lock(&cft);
3317 ++conf_threads;
3318 (void) pthread_mutex_unlock(&cft);
3320 if (f->f_type != F_UNUSED &&
3321 f->f_pmask[LOG_NFACILITIES] != NOPRI)
3322 Marking = 1;
3327 * filed init - initialize fields in a file descriptor struct
3328 * this is called before multiple threads are running, so no mutex
3329 * needs to be held at this time.
3331 static int
3332 filed_init(struct filed *f)
3334 pthread_attr_t stack_attr;
3335 pthread_t mythreadno;
3337 if (Debug) {
3338 mythreadno = pthread_self();
3341 if (pthread_mutex_init(&f->filed_mutex, NULL) != 0) {
3342 logerror("pthread_mutex_init failed");
3343 return (-1);
3346 DPRINT2(5, "filed_init(%u): dataq_init for queue %p\n",
3347 mythreadno, (void *)&f->f_queue);
3348 (void) dataq_init(&f->f_queue);
3350 if (pthread_attr_init(&stack_attr) != 0) {
3351 logerror("pthread_attr_init failed");
3352 return (-1);
3355 (void) pthread_attr_setstacksize(&stack_attr, stacksize);
3356 (void) pthread_attr_setstackaddr(&stack_attr, cstack_ptr);
3357 cstack_ptr += stacksize + redzonesize;
3359 f->f_msgflag = 0;
3360 f->f_prevmsg.msg[0] = '\0';
3361 f->f_prevmsg.flags = 0;
3362 f->f_prevmsg.pri = 0;
3363 f->f_prevmsg.host[0] = '\0';
3365 f->f_current.msg[0] = '\0';
3366 f->f_current.flags = 0;
3367 f->f_current.pri = 0;
3368 f->f_current.host[0] = '\0';
3370 f->f_prevcount = 0;
3372 f->f_stat.flag = 0;
3373 f->f_stat.total = 0;
3374 f->f_stat.dups = 0;
3375 f->f_stat.cantfwd = 0;
3376 f->f_stat.errs = 0;
3378 if (pthread_create(&f->f_thread, NULL, logit, (void *)f) != 0) {
3379 logerror("pthread_create failed");
3380 (void) pthread_attr_destroy(&stack_attr);
3381 return (-1);
3384 (void) pthread_attr_destroy(&stack_attr);
3385 return (0);
3390 * Crack a configuration file line
3392 static void
3393 cfline(char *line, int lineno, struct filed *f)
3395 char *p;
3396 char *q;
3397 int i;
3398 char *bp;
3399 int pri;
3400 char buf[MAXLINE];
3401 char ebuf[SYS_NMLN+1+40];
3402 mode_t fmode, omode = O_WRONLY|O_APPEND|O_NOCTTY;
3403 struct stat64 sbuf;
3404 pthread_t mythreadno;
3406 if (Debug) {
3407 mythreadno = pthread_self();
3410 DPRINT2(1, "cfline(%u): (%s)\n", mythreadno, line);
3412 errno = 0; /* keep errno related stuff out of logerror messages */
3414 /* clear out file entry */
3415 bzero((char *)f, sizeof (*f));
3416 for (i = 0; i <= LOG_NFACILITIES; i++)
3417 f->f_pmask[i] = NOPRI;
3419 /* scan through the list of selectors */
3420 for (p = line; *p && *p != '\t'; ) {
3422 /* find the end of this facility name list */
3423 for (q = p; *q && *q != '\t' && *q++ != '.'; )
3424 continue;
3426 /* collect priority name */
3427 for (bp = buf; *q && !strchr("\t,;", *q); )
3428 *bp++ = *q++;
3429 *bp = '\0';
3431 /* skip cruft */
3432 while (strchr(", ;", *q))
3433 q++;
3435 /* decode priority name */
3436 pri = decode(buf, PriNames);
3437 if (pri < 0) {
3438 logerror("line %d: unknown priority name \"%s\"",
3439 lineno, buf);
3440 return;
3443 /* scan facilities */
3444 while (*p && !strchr("\t.;", *p)) {
3445 for (bp = buf; *p && !strchr("\t,;.", *p); )
3446 *bp++ = *p++;
3447 *bp = '\0';
3448 if (*buf == '*')
3449 for (i = 0; i < LOG_NFACILITIES; i++)
3450 f->f_pmask[i] = (uchar_t)pri;
3451 else {
3452 i = decode(buf, FacNames);
3453 if (i < 0) {
3454 logerror("line %d: unknown facility"
3455 " name \"%s\"", lineno, buf);
3456 return;
3458 f->f_pmask[i >> 3] = (uchar_t)pri;
3460 while (*p == ',' || *p == ' ')
3461 p++;
3464 p = q;
3467 /* skip to action part */
3468 while (*p == '\t' || *p == ' ')
3469 p++;
3471 switch (*p) {
3472 case '\0':
3473 errno = 0;
3474 logerror("line %d: no action part", lineno);
3475 break;
3477 case '@':
3478 (void) strlcpy(f->f_un.f_forw.f_hname, ++p, SYS_NMLN);
3479 if (logforward(f, ebuf, sizeof (ebuf)) != 0) {
3480 logerror("line %d: %s", lineno, ebuf);
3481 break;
3483 f->f_type = F_FORW;
3484 break;
3486 case '/':
3487 (void) strlcpy(f->f_un.f_fname, p, MAXPATHLEN);
3488 if (stat64(p, &sbuf) < 0) {
3489 logerror(p);
3490 break;
3493 * don't block trying to open a pipe
3494 * with no reader on the other end
3496 fmode = 0; /* reset each pass */
3497 if (S_ISFIFO(sbuf.st_mode))
3498 fmode = O_NONBLOCK;
3500 f->f_file = open64(p, omode|fmode);
3501 if (f->f_file < 0) {
3502 if (fmode && errno == ENXIO) {
3503 errno = 0;
3504 logerror("%s - no reader", p);
3505 } else
3506 logerror(p);
3507 break;
3511 * Fifos are initially opened NONBLOCK
3512 * to insure we don't hang, but once
3513 * we are open, we need to change the
3514 * behavior back to blocking, otherwise
3515 * we may get write errors, and the log
3516 * will get closed down the line.
3518 if (S_ISFIFO(sbuf.st_mode))
3519 (void) fcntl(f->f_file, F_SETFL, omode);
3521 if (isatty(f->f_file)) {
3522 f->f_type = F_TTY;
3523 untty();
3524 } else
3525 f->f_type = F_FILE;
3527 if ((strcmp(p, ctty) == 0) || (strcmp(p, sysmsg) == 0))
3528 f->f_type = F_CONSOLE;
3529 break;
3531 case '*':
3532 f->f_type = F_WALL;
3533 break;
3535 default:
3536 for (i = 0; i < MAXUNAMES && *p; i++) {
3537 for (q = p; *q && *q != ','; )
3538 q++;
3539 (void) strlcpy(f->f_un.f_uname[i], p, UNAMESZ);
3540 if ((q - p) > UNAMESZ)
3541 f->f_un.f_uname[i][UNAMESZ] = '\0';
3542 else
3543 f->f_un.f_uname[i][q - p] = '\0';
3544 while (*q == ',' || *q == ' ')
3545 q++;
3546 p = q;
3548 f->f_type = F_USERS;
3549 break;
3551 f->f_orig_type = f->f_type;
3556 * Decode a symbolic name to a numeric value
3558 static int
3559 decode(char *name, struct code *codetab)
3561 struct code *c;
3562 char *p;
3563 char buf[40];
3565 if (isdigit(*name))
3566 return (atoi(name));
3568 (void) strncpy(buf, name, sizeof (buf) - 1);
3569 for (p = buf; *p; p++)
3570 if (isupper(*p))
3571 *p = tolower(*p);
3572 for (c = codetab; c->c_name; c++)
3573 if (!(strcmp(buf, c->c_name)))
3574 return (c->c_val);
3576 return (-1);
3579 static int
3580 ismyaddr(struct netbuf *nbp)
3582 int i;
3584 if (nbp == NULL)
3585 return (0);
3587 for (i = 1; i < Ninputs; i++) {
3588 if (same_addr(nbp, Myaddrs[i]))
3589 return (1);
3591 return (0);
3594 static void
3595 getnets(void)
3597 struct nd_hostserv hs;
3598 struct netconfig *ncp;
3599 struct nd_addrlist *nap;
3600 struct netbuf *nbp;
3601 int i, inputs;
3602 void *handle;
3603 char *uap;
3604 pthread_t mythreadno;
3606 if (Debug) {
3607 mythreadno = pthread_self();
3610 if (turnoff) {
3611 DPRINT1(1, "getnets(%u): network is being turned off\n",
3612 mythreadno);
3613 return;
3616 hs.h_host = HOST_SELF;
3617 hs.h_serv = "syslog";
3619 if ((handle = setnetconfig()) == NULL) {
3620 return;
3623 while ((ncp = getnetconfig(handle)) != NULL) {
3624 if (ncp->nc_semantics != NC_TPI_CLTS) {
3625 continue;
3628 if (netdir_getbyname(ncp, &hs, &nap) != 0) {
3629 continue;
3632 if (nap == NULL || nap->n_cnt <= 0) {
3633 DPRINT1(1, "getnets(%u): found no address\n",
3634 mythreadno);
3635 netdir_free((void *)nap, ND_ADDRLIST);
3636 continue;
3639 if (Debug) {
3640 DPRINT2(1, "getnets(%u): found %d addresses",
3641 mythreadno, nap->n_cnt);
3642 DPRINT0(1, ", they are: ");
3643 nbp = nap->n_addrs;
3645 for (i = 0; i < nap->n_cnt; i++) {
3646 if ((uap = taddr2uaddr(ncp, nbp)) != NULL) {
3647 DPRINT1(1, "%s ", uap);
3648 free(uap);
3650 nbp++;
3653 DPRINT0(1, "\n");
3656 inputs = Ninputs + nap->n_cnt;
3658 Nfd = reallocarray(Nfd, inputs, sizeof (struct pollfd));
3659 Ncf = reallocarray(Ncf, inputs, sizeof (struct netconfig));
3660 Myaddrs = reallocarray(Myaddrs, inputs,
3661 sizeof (struct netbuf *));
3662 Udp = reallocarray(Udp, inputs, sizeof (struct t_unitdata *));
3663 Errp = reallocarray(Errp, inputs, sizeof (struct t_uderr *));
3666 * all malloc failures here are fatal
3668 if (Nfd == NULL || Ncf == NULL || Myaddrs == NULL ||
3669 Udp == NULL || Errp == NULL) {
3670 MALLOC_FAIL_EXIT;
3673 nbp = nap->n_addrs;
3675 for (i = 0; i < nap->n_cnt; i++, nbp++) {
3676 char ebuf[128];
3678 if (addnet(ncp, nbp) == 0) {
3679 /* no error */
3680 continue;
3683 (void) strcpy(ebuf, "Unable to configure syslog port");
3685 if ((uap = taddr2uaddr(ncp, nbp)) != NULL) {
3686 size_t l = strlen(ebuf);
3687 (void) snprintf(ebuf + l, sizeof (ebuf) - l,
3688 " for %s", uap);
3691 DPRINT2(1, "getnets(%u): %s",
3692 mythreadno, ebuf);
3694 if (uap) {
3695 free(uap);
3698 logerror(ebuf);
3700 * Here maybe syslogd can quit. However, syslogd
3701 * has been ignoring this error and keep running.
3702 * So we won't break it.
3706 netdir_free((void *)nap, ND_ADDRLIST);
3709 (void) endnetconfig(handle);
3713 * Open the network device, and allocate necessary resources.
3714 * Myaddrs will also be filled, so that we can call ismyaddr() before
3715 * being bound to the network.
3717 static int
3718 addnet(struct netconfig *ncp, struct netbuf *nbp)
3720 int fd;
3721 struct netbuf *bp;
3723 fd = t_open(ncp->nc_device, O_RDWR, NULL);
3725 if (fd < 0) {
3726 return (1);
3729 (void) memcpy(&Ncf[Ninputs], ncp, sizeof (struct netconfig));
3731 /*LINTED*/
3732 Udp[Ninputs] = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ADDR);
3734 if (Udp[Ninputs] == NULL) {
3735 (void) t_close(fd);
3736 return (1);
3739 /*LINTED*/
3740 Errp[Ninputs] = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ADDR);
3742 if (Errp[Ninputs] == NULL) {
3743 (void) t_close(fd);
3744 (void) t_free((char *)Udp[Ninputs], T_UNITDATA);
3745 return (1);
3748 if ((bp = malloc(sizeof (struct netbuf))) == NULL ||
3749 (bp->buf = malloc(nbp->len)) == NULL) {
3750 MALLOC_FAIL("allocating address buffer");
3751 (void) t_close(fd);
3752 (void) t_free((char *)Udp[Ninputs], T_UNITDATA);
3753 (void) t_free((char *)Errp[Ninputs], T_UDERROR);
3755 if (bp) {
3756 free(bp);
3759 return (1);
3762 bp->len = nbp->len;
3763 (void) memcpy(bp->buf, nbp->buf, nbp->len);
3764 Myaddrs[Ninputs] = bp;
3766 Nfd[Ninputs].fd = fd;
3767 Nfd[Ninputs].events = POLLIN;
3768 Ninputs++;
3769 return (0);
3773 * Allocate UDP buffer to minimize packet loss.
3775 static void
3776 set_udp_buffer(int fd)
3778 struct t_optmgmt req, resp;
3779 struct opthdr *opt;
3780 size_t optsize, bsize = 256 * 1024;
3781 pthread_t mythreadno;
3783 if (Debug) {
3784 mythreadno = pthread_self();
3787 optsize = sizeof (struct opthdr) + sizeof (int);
3788 if ((opt = malloc(optsize)) == NULL) {
3789 MALLOC_FAIL("will have no udp buffer");
3790 return;
3792 opt->level = SOL_SOCKET;
3793 opt->name = SO_RCVBUF;
3794 opt->len = sizeof (int);
3795 *(int *)(opt + 1) = bsize;
3797 req.flags = T_NEGOTIATE;
3798 req.opt.len = optsize;
3799 req.opt.buf = (char *)opt;
3801 resp.flags = 0;
3802 resp.opt.maxlen = optsize;
3803 resp.opt.buf = (char *)opt;
3805 while (t_optmgmt(fd, &req, &resp) == -1 || resp.flags != T_SUCCESS) {
3806 if (t_errno != TSYSERR || errno != ENOBUFS) {
3807 bsize = 0;
3808 break;
3810 bsize >>= 1;
3811 if (bsize < 8192) {
3812 break;
3814 *(int *)(opt + 1) = bsize;
3816 if (bsize == 0) {
3817 logerror("failed to allocate UDP buffer");
3819 DPRINT3(1, "set_udp_buffer(%u): allocate %d for fd %d\n",
3820 mythreadno, bsize, fd);
3821 free(opt);
3825 * Attach the network, and allocate UDP buffer for the interface.
3827 static void
3828 bindnet(void)
3830 struct t_bind bind, *bound;
3831 int cnt, i;
3832 char *uap;
3833 pthread_t mythreadno;
3835 if (Debug) {
3836 mythreadno = pthread_self();
3839 cnt = 0;
3841 while (cnt < Ninputs) {
3842 char ebuf[128];
3844 /*LINTED*/
3845 bound = (struct t_bind *)t_alloc(Nfd[cnt].fd, T_BIND, T_ADDR);
3846 bind.addr = *Myaddrs[cnt];
3847 bind.qlen = 0;
3849 if (t_bind(Nfd[cnt].fd, &bind, bound) == 0) {
3850 if (same_addr(&bind.addr, &bound->addr)) {
3851 (void) t_free((char *)bound, T_BIND);
3852 set_udp_buffer(Nfd[cnt].fd);
3853 cnt++;
3854 continue;
3858 /* failed to bind port */
3859 (void) t_free((char *)bound, T_BIND);
3861 (void) strcpy(ebuf, "Unable to bind syslog port");
3863 uap = taddr2uaddr(&Ncf[cnt], Myaddrs[cnt]);
3864 if (uap) {
3865 i = strlen(ebuf);
3866 (void) snprintf(ebuf + i, sizeof (ebuf) - i,
3867 " for %s", uap);
3870 DPRINT2(1, "bindnet(%u): failed to bind port (%s)\n",
3871 mythreadno, uap ? uap : "<unknown>");
3873 if (uap) {
3874 free(uap);
3877 errno = 0;
3878 logerror(ebuf);
3880 (void) t_close(Nfd[cnt].fd);
3881 free(Myaddrs[cnt]->buf);
3882 free(Myaddrs[cnt]);
3883 (void) t_free((char *)Udp[cnt], T_UNITDATA);
3884 (void) t_free((char *)Errp[cnt], T_UDERROR);
3886 for (i = cnt; i < (Ninputs-1); i++) {
3887 Nfd[i] = Nfd[i + 1];
3888 Ncf[i] = Ncf[i + 1];
3889 Myaddrs[i] = Myaddrs[i + 1];
3890 Udp[i] = Udp[i + 1];
3891 Errp[i] = Errp[i + 1];
3894 Ninputs--;
3898 static int
3899 logforward(struct filed *f, char *ebuf, size_t elen)
3901 struct nd_hostserv hs;
3902 struct netbuf *nbp;
3903 struct netconfig *ncp;
3904 struct nd_addrlist *nap;
3905 void *handle;
3906 char *hp;
3908 hp = f->f_un.f_forw.f_hname;
3909 hs.h_host = hp;
3910 hs.h_serv = "syslog";
3912 if ((handle = setnetconfig()) == NULL) {
3913 (void) strlcpy(ebuf,
3914 "unable to rewind the netconfig database", elen);
3915 errno = 0;
3916 return (-1);
3918 nap = NULL;
3919 while ((ncp = getnetconfig(handle)) != NULL) {
3920 if (ncp->nc_semantics == NC_TPI_CLTS) {
3921 if (netdir_getbyname(ncp, &hs, &nap) == 0) {
3922 if (!nap)
3923 continue;
3924 nbp = nap->n_addrs;
3925 break;
3929 if (ncp == NULL) {
3930 (void) endnetconfig(handle);
3931 (void) snprintf(ebuf, elen,
3932 "WARNING: %s could not be resolved", hp);
3933 errno = 0;
3934 return (-1);
3936 if (nap == NULL) {
3937 (void) endnetconfig(handle);
3938 (void) snprintf(ebuf, elen, "unknown host %s", hp);
3939 errno = 0;
3940 return (-1);
3942 /* CSTYLED */
3943 if (ismyaddr(nbp)) { /*lint !e644 */
3944 netdir_free((void *)nap, ND_ADDRLIST);
3945 (void) endnetconfig(handle);
3946 (void) snprintf(ebuf, elen,
3947 "host %s is this host - logging loop", hp);
3948 errno = 0;
3949 return (-1);
3951 f->f_un.f_forw.f_addr.buf = malloc(nbp->len);
3952 if (f->f_un.f_forw.f_addr.buf == NULL) {
3953 netdir_free((void *)nap, ND_ADDRLIST);
3954 (void) endnetconfig(handle);
3955 (void) strlcpy(ebuf, "malloc failed", elen);
3956 return (-1);
3958 bcopy(nbp->buf, f->f_un.f_forw.f_addr.buf, nbp->len);
3959 f->f_un.f_forw.f_addr.len = nbp->len;
3960 f->f_file = t_open(ncp->nc_device, O_RDWR, NULL);
3961 if (f->f_file < 0) {
3962 netdir_free((void *)nap, ND_ADDRLIST);
3963 (void) endnetconfig(handle);
3964 free(f->f_un.f_forw.f_addr.buf);
3965 (void) strlcpy(ebuf, "t_open", elen);
3966 return (-1);
3968 netdir_free((void *)nap, ND_ADDRLIST);
3969 (void) endnetconfig(handle);
3970 if (t_bind(f->f_file, NULL, NULL) < 0) {
3971 (void) strlcpy(ebuf, "t_bind", elen);
3972 free(f->f_un.f_forw.f_addr.buf);
3973 (void) t_close(f->f_file);
3974 return (-1);
3976 return (0);
3979 static int
3980 amiloghost(void)
3982 struct nd_hostserv hs;
3983 struct netconfig *ncp;
3984 struct nd_addrlist *nap;
3985 struct netbuf *nbp;
3986 int i, fd;
3987 void *handle;
3988 char *uap;
3989 struct t_bind bind, *bound;
3990 pthread_t mythreadno;
3992 if (Debug) {
3993 mythreadno = pthread_self();
3997 * we need to know if we are running on the loghost. This is
3998 * checked by binding to the address associated with "loghost"
3999 * and "syslogd" service over the connectionless transport
4001 hs.h_host = "loghost";
4002 hs.h_serv = "syslog";
4004 if ((handle = setnetconfig()) == NULL) {
4005 return (0);
4008 while ((ncp = getnetconfig(handle)) != NULL) {
4009 if (ncp->nc_semantics != NC_TPI_CLTS) {
4010 continue;
4013 if (netdir_getbyname(ncp, &hs, &nap) != 0) {
4014 continue;
4017 if (nap == NULL) {
4018 continue;
4021 nbp = nap->n_addrs;
4023 for (i = 0; i < nap->n_cnt; i++) {
4024 if ((uap = taddr2uaddr(ncp, nbp)) != NULL) {
4025 DPRINT2(1, "amiloghost(%u): testing %s\n",
4026 mythreadno, uap);
4029 free(uap);
4031 fd = t_open(ncp->nc_device, O_RDWR, NULL);
4033 if (fd < 0) {
4034 netdir_free((void *)nap, ND_ADDRLIST);
4035 (void) endnetconfig(handle);
4036 return (0);
4039 /*LINTED*/
4040 bound = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR);
4041 bind.addr = *nbp;
4042 bind.qlen = 0;
4044 if (t_bind(fd, &bind, bound) == 0) {
4045 (void) t_close(fd);
4046 (void) t_free((char *)bound, T_BIND);
4047 netdir_free((void *)nap, ND_ADDRLIST);
4048 (void) endnetconfig(handle);
4049 return (1);
4050 } else {
4051 (void) t_close(fd);
4052 (void) t_free((char *)bound, T_BIND);
4055 nbp++;
4058 netdir_free((void *)nap, ND_ADDRLIST);
4061 (void) endnetconfig(handle);
4062 return (0);
4066 same_addr(struct netbuf *na, struct netbuf *nb)
4068 char *a, *b;
4069 size_t n;
4071 assert(na->buf != NULL && nb->buf != NULL);
4073 if (na->len != nb->len) {
4074 return (0);
4077 a = na->buf;
4078 b = nb->buf;
4079 n = nb->len;
4081 while (n-- > 0) {
4082 if (*a++ != *b++) {
4083 return (0);
4087 return (1);
4091 * allocates a new message structure, initializes it
4092 * and returns a pointer to it
4094 static log_message_t *
4095 new_msg(void)
4097 log_message_t *lm;
4098 pthread_t mythreadno;
4100 if (Debug) {
4101 mythreadno = pthread_self();
4104 if ((lm = malloc(sizeof (log_message_t))) == NULL)
4105 return ((log_message_t *)NULL);
4107 if (pthread_mutex_init(&lm->msg_mutex, NULL) != 0)
4108 return ((log_message_t *)NULL);
4109 lm->refcnt = 0;
4110 lm->pri = 0;
4111 lm->flags = 0;
4112 lm->hlp = NULL;
4113 lm->msg[0] = '\0';
4114 lm->ptr = NULL;
4116 DPRINT2(3, "new_msg(%u): creating msg %p\n", mythreadno, (void *)lm);
4117 return (lm);
4121 * frees a message structure - should only be called if
4122 * the refcount is 0
4124 static void
4125 free_msg(log_message_t *lm)
4127 pthread_t mythreadno;
4129 if (Debug) {
4130 mythreadno = pthread_self();
4133 assert(lm != NULL && lm->refcnt == 0);
4134 if (lm->hlp != NULL)
4135 freehl(lm->hlp);
4136 DPRINT2(3, "free_msg(%u): freeing msg %p\n", mythreadno, (void *)lm);
4137 free(lm);
4141 * Make sure that the message makes sense in the current locale, and
4142 * does not contain stray control characters.
4144 static void
4145 filter_string(char *mbstr, char *filtered, size_t max)
4147 size_t cs = 0;
4148 size_t mb_cur_max;
4149 unsigned char *p = (unsigned char *)mbstr;
4150 pthread_t mythreadno = 0;
4152 if (Debug) {
4153 mythreadno = pthread_self();
4156 assert(mbstr != NULL && filtered != NULL);
4159 * Since the access to MB_CUR_MAX is expensive (because
4160 * MB_CUR_MAX lives in a global area), it should be
4161 * restrained for the better performance.
4163 mb_cur_max = (size_t)MB_CUR_MAX;
4164 if (mb_cur_max > 1) {
4165 /* multibyte locale */
4166 int mlen;
4167 wchar_t wc;
4169 while (*p != '\0') {
4170 if ((mlen = mbtowc(&wc, (char *)p,
4171 mb_cur_max)) == -1) {
4173 * Invalid byte sequence found.
4175 * try to print one byte
4176 * in ASCII format.
4178 DPRINT2(9, "filter_string(%u): Invalid "
4179 "MB sequence: %ld\n", mythreadno,
4180 wc);
4182 if (!putctrlc(*p++, &filtered, &cs, max)) {
4183 /* not enough buffer */
4184 goto end;
4185 } else {
4186 continue;
4188 } else {
4190 * Since *p is not a null byte here,
4191 * mbtowc should have never returned 0.
4193 * A valid wide character found.
4196 if (wc != L'\t' && iswcntrl(wc)) {
4198 * non-tab, non-newline, and
4199 * control character found.
4201 * try to print this wide character
4202 * in ASCII-format.
4204 char *q = filtered;
4206 DPRINT2(9, "filter_string(%u): MB"
4207 " control character: %ld\n",
4208 mythreadno, wc);
4210 while (mlen--) {
4211 if (!putctrlc(*p++, &filtered,
4212 &cs, max)) {
4214 * not enough buffer in
4215 * filtered
4217 * cancel already
4218 * stored bytes in
4219 * filtered for this
4220 * wide character.
4222 filtered = q;
4223 goto end;
4226 continue;
4227 } else {
4229 * tab, newline, or non-control
4230 * character found.
4232 if (cs + mlen < max) {
4233 /* enough buffer */
4234 cs += mlen;
4235 while (mlen--) {
4236 *filtered++ = *p++;
4238 continue;
4239 } else {
4240 /* not enough buffer */
4241 goto end;
4246 } else {
4247 /* singlebyte locale */
4249 while (*p != '\0') {
4250 if (*p != '\t' && iscntrl(*p)) {
4252 * non-tab, non-newline,
4253 * and control character found.
4255 * try to print this singlebyte character
4256 * in ASCII format.
4258 DPRINT2(9, "filter_string(%u): control "
4259 "character: %d\n", mythreadno, *p);
4261 if (!putctrlc(*p++, &filtered, &cs, max)) {
4262 /* not enough buffer */
4263 goto end;
4264 } else {
4265 continue;
4267 } else if (*p != '\t' && !isprint(*p)) {
4269 * non-tab and non printable character found
4270 * this check is required for the C locale
4272 DPRINT2(9, "filter_string(%u): non-printable "
4273 "character: %d\n", mythreadno, *p);
4274 if (!putctrlc(*p++, &filtered, &cs, max)) {
4275 /* not enough buffer */
4276 goto end;
4277 } else {
4278 continue;
4280 } else {
4282 * tab, newline, non-control character, or
4283 * printable found.
4285 if (cs + 1 < max) {
4286 *filtered++ = *p++;
4287 cs++;
4288 continue;
4289 } else {
4290 /* not enough buffer */
4291 goto end;
4297 end:
4298 *filtered = '\0';
4300 if (cs >= 2 &&
4301 filtered[-2] == '\\' && filtered[-1] == 'n') {
4302 filtered[-2] = '\0';
4306 static char *
4307 alloc_stacks(int numstacks)
4309 size_t pagesize, mapsize;
4310 char *stack_top;
4311 char *addr;
4312 int i;
4314 pagesize = (size_t)sysconf(_SC_PAGESIZE);
4316 * stacksize and redzonesize are global so threads
4317 * can be created elsewhere and refer to the sizes
4319 stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) +
4320 DEFAULT_STACKSIZE, pagesize);
4321 redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize);
4324 * allocate an additional "redzonesize" chunk in addition
4325 * to what we require, so we can create a redzone at the
4326 * bottom of the last stack as well.
4328 mapsize = redzonesize + numstacks * (stacksize + redzonesize);
4329 stack_top = mmap(NULL, mapsize, PROT_READ|PROT_WRITE,
4330 MAP_PRIVATE|MAP_ANON, -1, 0);
4331 if (stack_top == MAP_FAILED)
4332 return (NULL);
4334 addr = stack_top;
4336 * this loop is intentionally <= instead of <, so we can
4337 * protect the redzone at the bottom of the last stack
4339 for (i = 0; i <= numstacks; i++) {
4340 (void) mprotect(addr, redzonesize, PROT_NONE);
4341 addr += stacksize + redzonesize;
4343 return ((char *)(stack_top + redzonesize));
4346 static void
4347 dealloc_stacks(int numstacks)
4349 size_t pagesize, mapsize;
4351 pagesize = (size_t)sysconf(_SC_PAGESIZE);
4353 stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) +
4354 DEFAULT_STACKSIZE, pagesize);
4356 redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize);
4358 mapsize = redzonesize + numstacks * (stacksize + redzonesize);
4359 (void) munmap(cstack_ptr - mapsize, mapsize);
4362 static void
4363 filed_destroy(struct filed *f)
4365 (void) dataq_destroy(&f->f_queue);
4366 (void) pthread_mutex_destroy(&f->filed_mutex);
4369 static void
4370 close_door(void)
4372 pthread_t mythreadno;
4374 if (Debug) {
4375 mythreadno = pthread_self();
4378 (void) fdetach(DoorFileName);
4380 DPRINT2(5, "close_door(%u): detached server() from %s\n",
4381 mythreadno, DoorFileName);
4384 static void
4385 delete_doorfiles(void)
4387 pthread_t mythreadno;
4388 struct stat sb;
4389 int err;
4390 char line[MAXLINE+1];
4392 if (Debug) {
4393 mythreadno = pthread_self();
4397 if (lstat(DoorFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4398 if (unlink(DoorFileName) < 0) {
4399 err = errno;
4400 (void) snprintf(line, sizeof (line),
4401 "unlink() of %s failed - fatal", DoorFileName);
4402 errno = err;
4403 logerror(line);
4404 DPRINT3(1, "delete_doorfiles(%u): error: %s, "
4405 "errno=%d\n", mythreadno, line, err);
4406 exit(1);
4409 DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4410 mythreadno, DoorFileName);
4413 if (strcmp(DoorFileName, DOORFILE) == 0) {
4414 if (lstat(OLD_DOORFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4415 if (unlink(OLD_DOORFILE) < 0) {
4416 err = errno;
4417 (void) snprintf(line, sizeof (line),
4418 "unlink() of %s failed", OLD_DOORFILE);
4419 DPRINT2(5, "delete_doorfiles(%u): %s\n",
4420 mythreadno, line);
4422 if (err != EROFS) {
4423 errno = err;
4424 (void) strlcat(line, " - fatal",
4425 sizeof (line));
4426 logerror(line);
4427 DPRINT3(1, "delete_doorfiles(%u): "
4428 "error: %s, errno=%d\n",
4429 mythreadno, line, err);
4430 exit(1);
4433 DPRINT1(5, "delete_doorfiles(%u): unlink() "
4434 "failure OK on RO file system\n",
4435 mythreadno);
4438 DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4439 mythreadno, OLD_DOORFILE);
4443 if (lstat(PidFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4444 if (unlink(PidFileName) < 0) {
4445 err = errno;
4446 (void) snprintf(line, sizeof (line),
4447 "unlink() of %s failed - fatal", PidFileName);
4448 errno = err;
4449 logerror(line);
4450 DPRINT3(1, "delete_doorfiles(%u): error: %s, "
4451 "errno=%d\n", mythreadno, line, err);
4452 exit(1);
4455 DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno,
4456 PidFileName);
4459 if (strcmp(PidFileName, PIDFILE) == 0) {
4460 if (lstat(OLD_PIDFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
4461 if (unlink(OLD_PIDFILE) < 0) {
4462 err = errno;
4463 (void) snprintf(line, sizeof (line),
4464 "unlink() of %s failed", OLD_PIDFILE);
4465 DPRINT2(5, "delete_doorfiles(%u): %s, \n",
4466 mythreadno, line);
4468 if (err != EROFS) {
4469 errno = err;
4470 (void) strlcat(line, " - fatal",
4471 sizeof (line));
4472 logerror(line);
4473 DPRINT3(1, "delete_doorfiles(%u): "
4474 "error: %s, errno=%d\n",
4475 mythreadno, line, err);
4476 exit(1);
4479 DPRINT1(5, "delete_doorfiles(%u): unlink "
4480 "failure OK on RO file system\n",
4481 mythreadno);
4484 DPRINT2(5, "delete_doorfiles(%u): deleted %s\n",
4485 mythreadno, OLD_PIDFILE);
4489 if (DoorFd != -1) {
4490 (void) door_revoke(DoorFd);
4493 DPRINT2(1, "delete_doorfiles(%u): revoked door: DoorFd=%d\n",
4494 mythreadno, DoorFd);
4498 /*ARGSUSED*/
4499 static void
4500 signull(int sig, siginfo_t *sip, void *utp)
4502 DPRINT1(1, "signull(%u): THIS CALL SHOULD NEVER HAPPEN\n",
4503 pthread_self());
4505 * Do nothing, as this is a place-holder used in conjunction with
4506 * sigaction()/sigwait() to ensure that the proper disposition is
4507 * given to the signals we handle in main().
4512 * putctrlc returns zero, if failed due to not enough buffer.
4513 * Otherwise, putctrlc returns non-zero.
4515 * c: a byte to print in ASCII format
4516 * **buf: a pointer to the pointer to the output buffer.
4517 * *cl: current length of characters in the output buffer
4518 * max: maximum length of the buffer
4521 static int
4522 putctrlc(int c, char **buf, size_t *cl, size_t max)
4524 char *p = *buf;
4526 if (c == '\n') {
4527 if (*cl + 2 < max) {
4528 *p++ = '\\';
4529 *p++ = 'n';
4530 *cl += 2;
4531 *buf = p;
4532 return (2);
4533 } else {
4534 return (0);
4536 } else if (c < 0200) {
4537 /* ascii control character */
4538 if (*cl + 2 < max) {
4539 *p++ = '^';
4540 *p++ = c ^ 0100;
4541 *cl += 2;
4542 *buf = p;
4543 return (2);
4544 } else {
4545 return (0);
4547 } else {
4548 if (*cl + 4 < max) {
4549 *p++ = '\\';
4550 *p++ = ((c >> 6) & 07) + '0';
4551 *p++ = ((c >> 3) & 07) + '0';
4552 *p++ = (c & 07) + '0';
4553 *cl += 4;
4554 *buf = p;
4555 return (4);
4556 } else {
4557 return (0);
4563 * findnl_bkwd:
4564 * Scans each character in buf until it finds the last newline in buf,
4565 * or the scanned character becomes the last COMPLETE character in buf.
4566 * Returns the number of scanned bytes.
4568 * buf - pointer to a buffer containing the message string
4569 * len - the length of the buffer
4571 size_t
4572 findnl_bkwd(const char *buf, const size_t len)
4574 const char *p;
4575 size_t mb_cur_max;
4576 pthread_t mythreadno;
4578 if (Debug) {
4579 mythreadno = pthread_self();
4582 if (len == 0) {
4583 return (0);
4586 mb_cur_max = MB_CUR_MAX;
4588 if (mb_cur_max == 1) {
4589 /* single-byte locale */
4590 for (p = buf + len - 1; p != buf; p--) {
4591 if (*p == '\n') {
4592 return ((size_t)(p - buf));
4595 return ((size_t)len);
4596 } else {
4597 /* multi-byte locale */
4598 int mlen;
4599 const char *nl;
4600 size_t rem;
4602 p = buf;
4603 nl = NULL;
4604 for (rem = len; rem >= mb_cur_max; ) {
4605 mlen = mblen(p, mb_cur_max);
4606 if (mlen == -1) {
4608 * Invalid character found.
4610 DPRINT1(9, "findnl_bkwd(%u): Invalid MB "
4611 "sequence\n", mythreadno);
4613 * handle as a single byte character.
4615 p++;
4616 rem--;
4617 } else {
4619 * It's guaranteed that *p points to
4620 * the 1st byte of a multibyte character.
4622 if (*p == '\n') {
4623 nl = p;
4625 p += mlen;
4626 rem -= mlen;
4629 if (nl) {
4630 return ((size_t)(nl - buf));
4633 * no newline nor null byte found.
4634 * Also it's guaranteed that *p points to
4635 * the 1st byte of a (multibyte) character
4636 * at this point.
4638 return (len - rem);
4643 * copynl_frwd:
4644 * Scans each character in buf and copies the scanned character to obuf
4645 * until it finds a null byte or a newline, or
4646 * the number of the remaining bytes in obuf gets to exceed obuflen
4647 * if copying the scanned character to obuf.
4648 * Returns the number of scanned bytes.
4650 * obuf - buffer to be copied the scanned character
4651 * obuflen - the size of obuf
4652 * buf - pointer to a buffer containing the message string
4653 * len - the length of the buffer
4655 size_t
4656 copynl_frwd(char *obuf, const size_t obuflen,
4657 const char *buf, const size_t len)
4659 const char *p;
4660 char *q = obuf;
4661 size_t olen = 0;
4662 size_t mb_cur_max;
4663 pthread_t mythreadno;
4665 if (Debug) {
4666 mythreadno = pthread_self();
4669 if (len == 0) {
4670 return (0);
4673 mb_cur_max = MB_CUR_MAX;
4675 if (mb_cur_max == 1) {
4676 /* single-byte locale */
4677 for (p = buf; *p; ) {
4678 if (obuflen > olen + 1) {
4679 if (*p != '\n') {
4680 *q++ = *p++;
4681 olen++;
4682 } else {
4683 *q = '\0';
4684 return ((size_t)(p - buf));
4686 } else {
4687 *q = '\0';
4688 return ((size_t)(p - buf));
4691 *q = '\0';
4692 return ((size_t)(p - buf));
4693 } else {
4694 /* multi-byte locale */
4695 int mlen;
4697 for (p = buf; *p; ) {
4698 mlen = mblen(p, mb_cur_max);
4699 if (mlen == -1) {
4701 * Invalid character found.
4703 DPRINT1(9, "copynl_frwd(%u): Invalid MB "
4704 "sequence\n", mythreadno);
4706 * handle as a single byte character.
4708 if (obuflen > olen + 1) {
4709 *q++ = *p++;
4710 olen++;
4711 } else {
4712 *q = '\0';
4713 return ((size_t)(p - buf));
4715 } else {
4717 * It's guaranteed that *p points to
4718 * the 1st byte of a multibyte character.
4720 if (*p == '\n') {
4721 *q = '\0';
4722 return ((size_t)(p - buf));
4724 if (obuflen > olen + mlen) {
4725 int n;
4726 for (n = 0; n < mlen; n++) {
4727 *q++ = *p++;
4729 olen += mlen;
4730 } else {
4731 *q = '\0';
4732 return ((size_t)(p - buf));
4737 * no newline nor null byte found.
4738 * Also it's guaranteed that *p points to
4739 * the 1st byte of a (multibyte) character
4740 * at this point.
4742 *q = '\0';
4743 return ((size_t)(p - buf));
4748 * copy_frwd:
4749 * Scans each character in buf and copies the scanned character to obuf
4750 * until the number of the remaining bytes in obuf gets to exceed obuflen
4751 * if copying the scanned character to obuf.
4752 * Returns the number of scanned (copied) bytes.
4754 * obuf - buffer to be copied the scanned character
4755 * obuflen - the size of obuf
4756 * buf - pointer to a buffer containing the message string
4757 * len - the length of the buffer
4759 size_t
4760 copy_frwd(char *obuf, const size_t obuflen,
4761 const char *buf, const size_t len)
4763 const char *p;
4764 char *q = obuf;
4765 size_t olen = 0;
4766 size_t mb_cur_max;
4767 pthread_t mythreadno;
4769 if (Debug) {
4770 mythreadno = pthread_self();
4773 if (len == 0) {
4774 return (0);
4777 mb_cur_max = MB_CUR_MAX;
4779 if (mb_cur_max == 1) {
4780 /* single-byte locale */
4781 if (obuflen > len) {
4782 (void) memcpy(obuf, buf, len);
4783 obuf[len] = '\0';
4784 return ((size_t)len);
4785 } else {
4786 (void) memcpy(obuf, buf, obuflen - 1);
4787 obuf[obuflen - 1] = '\0';
4788 return (obuflen - 1);
4790 } else {
4791 /* multi-byte locale */
4792 int mlen;
4794 for (p = buf; *p; ) {
4795 mlen = mblen(p, mb_cur_max);
4796 if (mlen == -1) {
4798 * Invalid character found.
4800 DPRINT1(9, "copy_frwd(%u): Invalid MB "
4801 "sequence\n", mythreadno);
4803 * handle as a single byte character.
4805 if (obuflen > olen + 1) {
4806 *q++ = *p++;
4807 olen++;
4808 } else {
4809 *q = '\0';
4810 return ((size_t)(p - buf));
4812 } else {
4813 if (obuflen > olen + mlen) {
4814 int n;
4815 for (n = 0; n < mlen; n++) {
4816 *q++ = *p++;
4818 olen += mlen;
4819 } else {
4820 *q = '\0';
4821 return ((size_t)(p - buf));
4825 *q = '\0';
4826 return ((size_t)(p - buf));
4831 * properties:
4832 * Get properties from SMF framework.
4834 static void
4835 properties(void)
4837 scf_simple_prop_t *prop;
4838 uint8_t *bool;
4840 if ((prop = scf_simple_prop_get(NULL, NULL, "config",
4841 "log_from_remote")) != NULL) {
4842 if ((bool = scf_simple_prop_next_boolean(prop)) != NULL) {
4843 if (*bool == 0)
4844 turnoff = 1; /* log_from_remote = false */
4845 else
4846 turnoff = 0; /* log_from_remote = true */
4848 scf_simple_prop_free(prop);
4849 DPRINT1(1, "properties: setting turnoff to %s\n",
4850 turnoff ? "true" : "false");
4855 * close all the input devices.
4857 static void
4858 shutdown_input(void)
4860 int cnt;
4862 shutting_down = 1;
4864 for (cnt = 0; cnt < Ninputs; cnt++) {
4865 (void) t_close(Nfd[cnt].fd);
4868 (void) close(Pfd.fd);
4872 * This is for the one thread that dedicates to resolve the
4873 * hostname. This will get the messages from net_poll() through
4874 * hnlq, and resolve the hostname, and push the messages back
4875 * into the inputq.
4877 /*ARGSUSED*/
4878 static void *
4879 hostname_lookup(void *ap)
4881 char *uap;
4882 log_message_t *mp;
4883 host_info_t *hip;
4884 char failsafe_addr[SYS_NMLN + 1];
4885 pthread_t mythreadno;
4887 if (Debug) {
4888 mythreadno = pthread_self();
4891 DPRINT1(1, "hostname_lookup(%u): hostname_lookup started\n",
4892 mythreadno);
4894 for (;;) {
4895 (void) dataq_dequeue(&hnlq, (void **)&mp, 0);
4897 DPRINT3(5, "hostname_lookup(%u): dequeued msg %p"
4898 " from queue %p\n", mythreadno, (void *)mp,
4899 (void *)&hnlq);
4901 hip = (host_info_t *)mp->ptr;
4902 if ((uap = taddr2uaddr(hip->ncp, &hip->addr)) != NULL) {
4903 (void) strlcpy(failsafe_addr, uap, SYS_NMLN);
4904 free(uap);
4905 } else {
4906 (void) strlcpy(failsafe_addr, "<unknown>", SYS_NMLN);
4909 mp->hlp = cvthname(&hip->addr, hip->ncp, failsafe_addr);
4911 if (mp->hlp == NULL) {
4912 mp->hlp = &NullHostName;
4915 free(hip->addr.buf);
4916 free(hip);
4917 mp->ptr = NULL;
4919 if (dataq_enqueue(&inputq, (void *)mp) == -1) {
4920 MALLOC_FAIL("dropping message from remote");
4921 free_msg(mp);
4922 continue;
4925 DPRINT3(5, "hostname_lookup(%u): enqueued msg %p on queue "
4926 "%p\n", mythreadno, (void *)mp, (void *)&inputq);
4929 /*NOTREACHED*/
4930 return (NULL);
4934 * Does all HUP(re-configuration) process.
4936 static void
4937 reconfigure()
4939 int cnt, loop, drops;
4940 int really_stuck;
4941 int console_stuck = 0;
4942 struct filed *f;
4943 char buf[LINE_MAX];
4944 struct utsname up;
4945 char cbuf[30];
4946 time_t tim;
4947 pthread_t mythreadno;
4949 if (Debug) {
4950 mythreadno = pthread_self();
4953 /* If we get here then we must need to regen */
4954 flushmsg(0);
4956 if (logmymsg(LOG_SYSLOG|LOG_INFO, "syslogd: configuration restart",
4957 ADDDATE, 0) == -1) {
4958 MALLOC_FAIL("dropping message");
4962 * make sure the logmsg thread is not in the waiting state.
4963 * Otherwise, changing hup_state will prevent the logmsg thread
4964 * getting out from the waiting loop.
4967 if (Debug) {
4968 tim = time(NULL);
4969 DPRINT2(3, "reconfigure(%u): %.15s: awaiting logmsg()"
4970 " moving to the safe place\n",
4971 mythreadno, ctime_r(&tim, cbuf)+4);
4974 for (loop = 0; loop < LOOP_MAX; loop++) {
4975 /* we don't need the mutex to read */
4976 if (hup_state == HUP_ACCEPTABLE)
4977 break;
4978 (void) sleep(1);
4980 if (hup_state != HUP_ACCEPTABLE) {
4981 goto thread_stuck;
4984 if (Debug) {
4985 tim = time(NULL);
4986 DPRINT2(3, "reconfigure(%u): %.15s: logmsg() will accept HUP\n",
4987 mythreadno, ctime_r(&tim, cbuf)+4);
4991 * Prevent logging until we are truly done processing the HUP
4993 (void) pthread_mutex_lock(&hup_lock);
4994 hup_state = HUP_INPROGRESS;
4995 (void) pthread_mutex_unlock(&hup_lock);
4998 * We will be going into a critical state. Any error message
4999 * from syslogd needs to be dumped to the console by default
5000 * immediately. Also, those error messages are quened in a temporary
5001 * queue to be able to post into the regular stream later.
5003 disable_errorlog();
5005 if (Debug) {
5006 tim = time(NULL);
5007 DPRINT2(3, "reconfigure(%u): %.15s: sending SHUTDOWN\n",
5008 mythreadno, ctime_r(&tim, cbuf)+4);
5011 /* stop configured threads */
5012 if (shutdown_msg() == -1) {
5014 * No memory, message will be dumped to the console.
5016 MALLOC_FAIL("unable to restart syslogd");
5017 goto out;
5020 /* make sure logmsg() is in suspended state */
5021 for (loop = 0; loop < LOOP_INTERVAL; loop++) {
5022 if (hup_state & HUP_LOGMSG_SUSPENDED)
5023 break;
5024 (void) sleep(1);
5027 if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
5028 if (Debug) {
5029 tim = time(NULL);
5030 DPRINT2(3, "reconfigure(%u): %.15s: logmsg() does not "
5031 "stop. enforcing\n",
5032 mythreadno, ctime_r(&tim, cbuf)+4);
5035 /* probably we have too long input queue, or really stuck */
5036 (void) pthread_mutex_lock(&hup_lock);
5037 hup_state |= HUP_SUSP_LOGMSG_REQD;
5038 (void) pthread_mutex_unlock(&hup_lock);
5040 for (loop = 0; loop < LOOP_MAX; loop++) {
5041 if (hup_state & HUP_LOGMSG_SUSPENDED)
5042 break;
5043 (void) sleep(1);
5045 if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
5046 if (Debug) {
5047 tim = time(NULL);
5048 DPRINT2(3, "reconfigure(%u): %.15s: logmsg()"
5049 " does not stop. give up\n",
5050 mythreadno, ctime_r(&tim, cbuf)+4);
5052 logerror("could not suspend logmsg - fatal");
5053 goto thread_stuck;
5057 if (Debug) {
5058 tim = time(NULL);
5059 DPRINT2(3, "reconfigure(%u): %.15s: logmsg() suspended\n",
5060 mythreadno, ctime_r(&tim, cbuf)+4);
5064 * Will wait for LOOP_MAX secs with watching queue lengths for the
5065 * each logger threads. If they have backlogs, and no change in the
5066 * length of queue found in 30 seconds, those will be counted as
5067 * "really stuck".
5068 * If all running logger threads become "really stuck" state, there
5069 * should be no worth waiting for them to quit.
5070 * In that case, we will go ahead and close out file descriptors to
5071 * have them pull out from hanging system call, and give them a last
5072 * chance(LOOP_INTERVAL sec) to quit.
5075 if (Debug) {
5076 tim = time(NULL);
5077 DPRINT2(3, "reconfigure(%u): %.15s: awaiting logit() to be"
5078 " shutdown\n", mythreadno, ctime_r(&tim, cbuf)+4);
5081 cnt = 0;
5082 really_stuck = 0;
5083 while (cnt < (LOOP_MAX/LOOP_INTERVAL) &&
5084 conf_threads > really_stuck) {
5086 /* save initial queue count */
5087 for (f = Files; f < &Files[nlogs]; f++) {
5088 f->f_prev_queue_count = (f->f_type == F_UNUSED) ?
5089 -1 : f->f_queue_count;
5092 for (loop = 0; loop < LOOP_INTERVAL; loop++) {
5093 if (conf_threads == 0)
5094 break;
5095 (void) sleep(1);
5098 if (conf_threads == 0)
5099 break;
5101 if (Debug) {
5102 tim = time(NULL);
5103 DPRINT3(3, "reconfigure(%u): %.15s: "
5104 "%d threads are still alive.\n",
5105 mythreadno, ctime_r(&tim, cbuf)+4,
5106 conf_threads);
5109 really_stuck = 0;
5110 for (f = Files; f < &Files[nlogs]; f++) {
5111 if (f->f_type == F_UNUSED) {
5112 f->f_prev_queue_count = -1;
5113 continue;
5115 if (f->f_prev_queue_count == f->f_queue_count) {
5116 really_stuck++;
5117 f->f_prev_queue_count = 1;
5118 DPRINT2(3, "reconfigure(%u): "
5119 "tid=%d is really stuck.\n",
5120 mythreadno, f->f_thread);
5121 } else {
5122 f->f_prev_queue_count = 0;
5123 DPRINT2(3, "reconfigure(%u): "
5124 "tid=%d is still active.\n",
5125 mythreadno, f->f_thread);
5129 * Here we have one of following values in the
5130 * f_prev_queue_count:
5131 * 0: logger thread is still actively working.
5132 * 1: logger thread is really stuck.
5133 * -1: logger thread has already died.
5136 cnt++;
5139 if (Debug) {
5140 tim = time(NULL);
5141 DPRINT2(3, "reconfigure(%u): %.15s:"
5142 " complete awaiting logit()\n",
5143 mythreadno, ctime_r(&tim, cbuf)+4);
5144 DPRINT3(3, "reconfigure(%u): %d threads alive."
5145 " %d threads stuck\n",
5146 mythreadno, conf_threads, really_stuck);
5150 * Still running? If so, mark it as UNUSED, and close
5151 * the fd so that logger threads can bail out from the loop.
5153 drops = 0;
5154 if (conf_threads) {
5155 for (f = Files; f < &Files[nlogs]; f++) {
5156 if (f->f_type == F_CONSOLE &&
5157 f->f_prev_queue_count == 1) {
5158 /* console is really stuck */
5159 console_stuck = 1;
5161 if (f->f_type == F_USERS || f->f_type == F_WALL ||
5162 f->f_type == F_UNUSED)
5163 continue;
5164 cnt = f->f_queue_count;
5165 drops += (cnt > 0) ? cnt - 1: 0;
5166 f->f_type = F_UNUSED;
5168 if (f->f_orig_type == F_FORW)
5169 (void) t_close(f->f_file);
5170 else
5171 (void) close(f->f_file);
5174 if (Debug) {
5175 tim = time(NULL);
5176 DPRINT1(3, "reconfigure(%u): terminating logit()\n",
5177 mythreadno);
5180 /* last chance to exit */
5181 for (loop = 0; loop < LOOP_MAX; loop++) {
5182 if (conf_threads == 0)
5183 break;
5184 (void) sleep(1);
5187 if (Debug) {
5188 tim = time(NULL);
5189 DPRINT3(3, "reconfigure(%u): %.15s: %d alive\n",
5190 mythreadno, ctime_r(&tim, cbuf)+4,
5191 conf_threads);
5195 if (conf_threads == 0 && drops) {
5196 errno = 0;
5197 logerror("Could not completely output pending messages"
5198 " while preparing re-configuration");
5199 logerror("discarded %d messages and restart configuration.",
5200 drops);
5201 if (Debug) {
5202 tim = time(NULL);
5203 DPRINT3(3, "reconfigure(%u): %.15s: "
5204 "discarded %d messages\n",
5205 mythreadno, ctime_r(&tim, cbuf)+4, drops);
5210 * If all threads still haven't exited
5211 * something is stuck or hosed. We just
5212 * have no option but to exit.
5214 if (conf_threads) {
5215 thread_stuck:
5216 if (Debug) {
5217 tim = time(NULL);
5218 DPRINT2(3, "reconfigure(%u): %.15s: really stuck\n",
5219 mythreadno, ctime_r(&tim, cbuf)+4);
5222 shutdown_input();
5223 delete_doorfiles();
5224 (void) uname(&up);
5226 (void) snprintf(buf, sizeof (buf),
5227 "syslogd(%s): some logger thread(s) "
5228 "are stuck%s; syslogd is shutting down.",
5229 up.nodename,
5230 console_stuck ? " (including the console)" : "");
5232 if (console_stuck) {
5233 FILE *m = popen(MAILCMD, "w");
5235 if (m != NULL) {
5236 (void) fprintf(m, "%s\n", buf);
5237 (void) pclose(m);
5241 disable_errorlog();
5242 logerror(buf);
5243 exit(1);
5246 /* Free up some resources */
5247 if (Files != (struct filed *)&fallback) {
5248 for (f = Files; f < &Files[nlogs]; f++) {
5249 (void) pthread_join(f->f_thread, NULL);
5250 filed_destroy(f);
5252 free(Files);
5255 dealloc_stacks(nlogs);
5257 if (Debug) {
5258 tim = time(NULL);
5259 DPRINT2(3, "reconfigure(%u): %.15s: cleanup complete\n",
5260 mythreadno, ctime_r(&tim, cbuf)+4);
5263 hnc_init(1); /* purge hostname cache */
5264 conf_init(); /* start reconfigure */
5266 out:;
5267 /* Now should be ready to dispatch error messages from syslogd. */
5268 enable_errorlog();
5270 /* Wake up the log thread */
5272 if (Debug) {
5273 tim = time(NULL);
5274 DPRINT2(3, "reconfigure(%u): %.15s: resuming logmsg()\n",
5275 mythreadno, ctime_r(&tim, cbuf)+4);
5278 (void) pthread_mutex_lock(&hup_lock);
5279 hup_state = HUP_COMPLETED;
5280 (void) pthread_cond_signal(&hup_done);
5281 (void) pthread_mutex_unlock(&hup_lock);
5285 * The following function implements simple hostname cache mechanism.
5286 * Host name cache is implemented through hash table bucket chaining method.
5287 * Collisions are handled by bucket chaining.
5289 * hnc_init():
5290 * allocate and initialize the cache. If reinit is set,
5291 * invalidate all cache entries.
5292 * hnc_look():
5293 * It hashes the ipaddress gets the index and walks thru the
5294 * single linked list. if cached entry was found, it will
5295 * put in the head of the list, and return.While going through
5296 * the entries, an entry which has already expired will be invalidated.
5297 * hnc_register():
5298 * Hashes the ipaddress finds the index and puts current entry to the list.
5299 * hnc_unreg():
5300 * invalidate the cachep.
5303 static void
5304 hnc_init(int reinit)
5306 struct hostname_cache **hpp;
5307 pthread_t mythreadno;
5308 int i;
5310 if (Debug) {
5311 mythreadno = pthread_self();
5314 if (reinit) {
5315 (void) pthread_mutex_lock(&hnc_mutex);
5317 for (i = 0; i < hnc_size; i++) {
5318 for (hpp = &hnc_cache[i]; *hpp != NULL; ) {
5319 hnc_unreg(hpp);
5323 (void) pthread_mutex_unlock(&hnc_mutex);
5324 DPRINT1(2, "hnc_init(%u): hostname cache re-configured\n",
5325 mythreadno);
5326 } else {
5328 hnc_cache = calloc(hnc_size, sizeof (struct hostname_cache *));
5330 if (hnc_cache == NULL) {
5331 MALLOC_FAIL("hostname cache");
5332 logerror("hostname cache disabled");
5333 return;
5336 DPRINT3(1, "hnc_init(%u): hostname cache configured %d entry"
5337 " ttl:%d\n", mythreadno, hnc_size, hnc_ttl);
5341 static host_list_t *
5342 hnc_lookup(struct netbuf *nbp, struct netconfig *ncp, int *hindex)
5344 struct hostname_cache **hpp, *hp;
5345 time_t now;
5346 pthread_t mythreadno;
5347 int index;
5349 if (Debug) {
5350 mythreadno = pthread_self();
5353 if (hnc_cache == NULL) {
5354 return (NULL);
5357 (void) pthread_mutex_lock(&hnc_mutex);
5358 now = time(0);
5360 *hindex = index = addr_hash(nbp);
5362 for (hpp = &hnc_cache[index]; (hp = *hpp) != NULL; ) {
5363 DPRINT4(10, "hnc_lookup(%u): check %p on %p for %s\n",
5364 mythreadno, (void *)hp->h, (void *)hp,
5365 hp->h->hl_hosts[0]);
5367 if (hp->expire < now) {
5368 DPRINT2(9, "hnc_lookup(%u): purge %p\n",
5369 mythreadno, (void *)hp);
5370 hnc_unreg(hpp);
5371 continue;
5374 if (ncp == hp->ncp && same_addr(&hp->addr, nbp)) {
5376 * found!
5377 * Put the entry at the top.
5380 if (hp != hnc_cache[index]) {
5381 /* unlink from active list */
5382 *hpp = (*hpp)->next;
5383 /* push it onto the top */
5384 hp->next = hnc_cache[index];
5385 hnc_cache[index] = hp;
5388 (void) pthread_mutex_lock(&hp->h->hl_mutex);
5389 hp->h->hl_refcnt++;
5390 (void) pthread_mutex_unlock(&hp->h->hl_mutex);
5392 DPRINT4(9, "hnc_lookup(%u): found %p on %p for %s\n",
5393 mythreadno, (void *)hp->h, (void *)hp,
5394 hp->h->hl_hosts[0]);
5396 (void) pthread_mutex_unlock(&hnc_mutex);
5397 return (hp->h);
5400 hpp = &hp->next;
5403 (void) pthread_mutex_unlock(&hnc_mutex);
5404 return (NULL);
5407 static void
5408 hnc_register(struct netbuf *nbp, struct netconfig *ncp,
5409 host_list_t *h, int hindex)
5411 struct hostname_cache **hpp, **tailp, *hp, *entry;
5412 void *addrbuf;
5413 time_t now;
5414 pthread_t mythreadno;
5415 int i;
5417 if (Debug) {
5418 mythreadno = pthread_self();
5421 if (hnc_cache == NULL) {
5422 return;
5425 if ((addrbuf = malloc(nbp->len)) == NULL) {
5426 MALLOC_FAIL("pushing hostname cache");
5427 return;
5430 if ((entry = malloc(sizeof (struct hostname_cache))) == NULL) {
5431 MALLOC_FAIL("pushing hostname entry");
5432 free(addrbuf);
5433 return;
5436 (void) pthread_mutex_lock(&hnc_mutex);
5438 i = 0;
5440 now = time(0);
5442 * first go through active list, and discard the
5443 * caches which has been invalid. Count number of
5444 * non-expired buckets.
5447 for (hpp = &hnc_cache[hindex]; (hp = *hpp) != NULL; ) {
5448 tailp = hpp;
5450 if (hp->expire < now) {
5451 DPRINT2(9, "hnc_register(%u): discard %p\n",
5452 mythreadno, (void *)hp);
5453 hnc_unreg(hpp);
5454 } else {
5455 i++;
5456 hpp = &hp->next;
5461 * If max limit of chained hash buckets has been used up
5462 * delete the least active element in the chain.
5464 if (i == MAX_BUCKETS) {
5465 hnc_unreg(tailp);
5468 (void) memcpy(addrbuf, nbp->buf, nbp->len);
5469 entry->addr.len = nbp->len;
5470 entry->addr.buf = addrbuf;
5471 entry->ncp = ncp;
5472 entry->h = h;
5473 entry->expire = time(NULL) + hnc_ttl;
5475 /* insert it at the top */
5476 entry->next = hnc_cache[hindex];
5477 hnc_cache[hindex] = entry;
5480 * As far as cache is valid, corresponding host_list must
5481 * also be valid. Increments the refcnt to avoid freeing
5482 * host_list.
5484 h->hl_refcnt++;
5485 DPRINT4(9, "hnc_register(%u): reg %p onto %p for %s\n",
5486 mythreadno, (void *)entry->h, (void *)entry, entry->h->hl_hosts[0]);
5487 (void) pthread_mutex_unlock(&hnc_mutex);
5490 static void
5491 hnc_unreg(struct hostname_cache **hpp)
5493 struct hostname_cache *hp = *hpp;
5494 pthread_t mythreadno;
5496 if (Debug) {
5497 mythreadno = pthread_self();
5500 DPRINT4(9, "hnc_unreg(%u): unreg %p on %p for %s\n",
5501 mythreadno, (void *)hp->h, (void *)hp, hp->h->hl_hosts[0]);
5502 free(hp->addr.buf);
5503 freehl(hp->h);
5505 /* unlink from active list */
5506 *hpp = (*hpp)->next;
5508 free(hp);
5512 * Once this is called, error messages through logerror() will go to
5513 * the console immediately. Also, messages are queued into the tmpq
5514 * to be able to later put them into inputq.
5516 static void
5517 disable_errorlog()
5519 (void) dataq_init(&tmpq);
5521 (void) pthread_mutex_lock(&logerror_lock);
5522 interrorlog = 0;
5523 (void) pthread_mutex_unlock(&logerror_lock);
5527 * Turn internal error messages to regular input stream.
5528 * All pending messages are pulled and pushed into the regular
5529 * input queue.
5531 static void
5532 enable_errorlog()
5534 log_message_t *mp;
5536 (void) pthread_mutex_lock(&logerror_lock);
5537 interrorlog = 1;
5538 (void) pthread_mutex_unlock(&logerror_lock);
5541 * push all the pending messages into inputq.
5543 while (dataq_dequeue(&tmpq, (void **)&mp, 1) == 0) {
5544 (void) dataq_enqueue(&inputq, mp);
5546 (void) dataq_destroy(&tmpq);
5550 * Generate a hash value of the given address and derive
5551 * an index into the hnc_cache hashtable.
5552 * The hashing method is similar to what Java does for strings.
5554 static int
5555 addr_hash(struct netbuf *nbp)
5557 char *uap;
5558 int i;
5559 unsigned long hcode = 0;
5561 uap = nbp->buf;
5563 if (uap == NULL) {
5564 return (0);
5568 * Compute a hashcode of the address string
5570 for (i = 0; i < nbp->len; i++)
5571 hcode = (31 * hcode) + uap[i];
5574 * Scramble the hashcode for better distribution
5576 hcode += ~(hcode << 9);
5577 hcode ^= (hcode >> 14);
5578 hcode += (hcode << 4);
5579 hcode ^= (hcode >> 10);
5581 return ((int)(hcode % hnc_size));