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