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