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