/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T * All Rights Reserved */ /* * University Copyright- Copyright (c) 1982, 1986, 1988 * The Regents of the University of California * All Rights Reserved * * University Acknowledgment- Portions of this document are derived from * software developed by the University of California, Berkeley, and its * contributors. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * syslogd -- log system messages * * This program implements a system log. It takes a series of lines. * Each line may have a priority, signified as "" as * the first characters of the line. If this is * not present, a default priority is used. * * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will * cause it to reconfigure. * * Defined Constants: * * MAXLINE -- the maximimum line length that can be handled. * DEFUPRI -- the default priority for user messages. * DEFSPRI -- the default priority for kernel messages. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dataq.h" #include "conf.h" #include "syslogd.h" #define DOORFILE "/var/run/syslog_door" #define RELATIVE_DOORFILE "../var/run/syslog_door" #define OLD_DOORFILE "/etc/.syslog_door" #define PIDFILE "/var/run/syslog.pid" #define RELATIVE_PIDFILE "../var/run/syslog.pid" #define OLD_PIDFILE "/etc/syslog.pid" static char *Version = "%I%"; static char *LogName = "/dev/log"; static char *ConfFile = "/etc/syslog.conf"; static char *DflFile = "/etc/default/syslogd"; static char ctty[] = "/dev/console"; static char sysmsg[] = "/dev/sysmsg"; static int DoorFd = -1; static int DoorCreated = 0; static int PidfileCreated = 0; static char *DoorFileName = DOORFILE; static char *PidFileName = PIDFILE; /* * configuration file directives */ static struct code PriNames[] = { "panic", LOG_EMERG, "emerg", LOG_EMERG, "alert", LOG_ALERT, "crit", LOG_CRIT, "err", LOG_ERR, "error", LOG_ERR, "warn", LOG_WARNING, "warning", LOG_WARNING, "notice", LOG_NOTICE, "info", LOG_INFO, "debug", LOG_DEBUG, "none", NOPRI, NULL, -1 }; static struct code FacNames[] = { "kern", LOG_KERN, "user", LOG_USER, "mail", LOG_MAIL, "daemon", LOG_DAEMON, "auth", LOG_AUTH, "security", LOG_AUTH, "mark", LOG_MARK, "syslog", LOG_SYSLOG, "lpr", LOG_LPR, "news", LOG_NEWS, "uucp", LOG_UUCP, "audit", LOG_AUDIT, "cron", LOG_CRON, "local0", LOG_LOCAL0, "local1", LOG_LOCAL1, "local2", LOG_LOCAL2, "local3", LOG_LOCAL3, "local4", LOG_LOCAL4, "local5", LOG_LOCAL5, "local6", LOG_LOCAL6, "local7", LOG_LOCAL7, NULL, -1 }; static char *TypeNames[7] = { "UNUSED", "FILE", "TTY", "CONSOLE", "FORW", "USERS", "WALL" }; /* * we allocate our own thread stacks so we can create them * without the MAP_NORESERVE option. We need to be sure * we have stack space even if the machine runs out of swap */ #define DEFAULT_STACKSIZE (100 * 1024) /* 100 k stack */ #define DEFAULT_REDZONESIZE (8 * 1024) /* 8k redzone */ static pthread_mutex_t wmp = PTHREAD_MUTEX_INITIALIZER; /* wallmsg lock */ static pthread_mutex_t cft = PTHREAD_MUTEX_INITIALIZER; static int conf_threads = 0; static pthread_mutex_t hup_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t hup_done = PTHREAD_COND_INITIALIZER; static pthread_mutex_t logerror_lock = PTHREAD_MUTEX_INITIALIZER; #define HUP_ACCEPTABLE 0x0000 /* can start SIGHUP process */ #define HUP_INPROGRESS 0x0001 /* SIGHUP process in progress */ #define HUP_COMPLETED 0x0002 /* SIGHUP process completed */ #define HUP_SUSP_LOGMSG_REQD 0x1000 /* request to suspend */ #define HUP_LOGMSG_SUSPENDED 0x2000 /* logmsg is suspended */ static int hup_state = HUP_ACCEPTABLE; static size_t stacksize; /* thread stack size */ static size_t redzonesize; /* thread stack redzone size */ static char *stack_ptr; /* ptr to allocated stacks */ static char *cstack_ptr; /* ptr to conf_thr stacks */ static time_t start_time; static pthread_t sys_thread; /* queues messages from us */ static pthread_t net_thread; /* queues messages from the net */ static pthread_t log_thread; /* message processing thread */ static pthread_t hnl_thread; /* hostname lookup thread */ static dataq_t inputq; /* the input queue */ static dataq_t tmpq; /* temporary queue for err msg */ static dataq_t hnlq; /* hostname lookup queue */ static struct filed fallback[2]; static struct filed *Files; static int nlogs; static int Debug; /* debug flag */ static host_list_t LocalHostName; /* our hostname */ static host_list_t NullHostName; /* in case of lookup failure */ static int debuglev = 1; /* debug print level */ static int interrorlog; /* internal error logging */ static int MarkInterval = 20; /* interval between marks (mins) */ static int Marking = 0; /* non-zero if marking some file */ static int Ninputs = 0; /* number of network inputs */ static int curalarm = 0; /* current timeout value (secs) */ static int sys_msg_count = 0; /* total msgs rcvd from local log */ static int sys_init_msg_count = 0; /* initially received */ static int net_msg_count = 0; /* total msgs rcvd from net */ static struct pollfd Pfd; /* Pollfd for local the log device */ static struct pollfd *Nfd; /* Array of pollfds for udp ports */ static struct netconfig *Ncf; static struct netbuf **Myaddrs; static struct t_unitdata **Udp; static struct t_uderr **Errp; static int turnoff = 0; static int shutting_down; static struct hostname_cache *hnc_cache, *hnc_active, *hnc_freeq; static pthread_mutex_t hnc_mutex = PTHREAD_MUTEX_INITIALIZER; static size_t hnc_size = DEF_HNC_SIZE; static unsigned int hnc_ttl = DEF_HNC_TTL; #define DPRINT0(d, m) if ((Debug) && debuglev >= (d)) \ (void) fprintf(stderr, m) #define DPRINT1(d, m, a) if ((Debug) && debuglev >= (d)) \ (void) fprintf(stderr, m, a) #define DPRINT2(d, m, a, b) if ((Debug) && debuglev >= (d)) \ (void) fprintf(stderr, m, a, b) #define DPRINT3(d, m, a, b, c) if ((Debug) && debuglev >= (d)) \ (void) fprintf(stderr, m, a, b, c) #define DPRINT4(d, m, a, b, c, e) if ((Debug) && debuglev >= (d)) \ (void) fprintf(stderr, m, a, b, c, e) #define MALLOC_FAIL(x) \ logerror("malloc failed: " x) #define MALLOC_FAIL_EXIT \ logerror("malloc failed - fatal"); \ exit(1) #define MAILCMD "mailx -s \"syslogd shut down\" root" /* * Number of seconds to wait before giving up on threads that won't * shutdown: (that's right, 10 minutes!) */ #define LOOP_MAX (10 * 60) /* * Interval(sec) to check the status of output queue while processing * HUP signal. */ #define LOOP_INTERVAL (15) int main(int argc, char **argv) { int i; char *pstr; int sig, fd; int tflag = 0, Tflag = 0; sigset_t sigs, allsigs; struct rlimit rlim; char *debugstr; int mcount = 0; struct sigaction act; pthread_t mythreadno = 0; char cbuf [30]; struct stat sb; #ifdef DEBUG #define DEBUGDIR "/var/tmp" if (chdir(DEBUGDIR)) DPRINT2(1, "main(%u): Unable to cd to %s\n", mythreadno, DEBUGDIR); #endif /* DEBUG */ (void) setlocale(LC_ALL, ""); if ((debugstr = getenv("SYSLOGD_DEBUG")) != NULL) if ((debuglev = atoi(debugstr)) == 0) debuglev = 1; #if ! defined(TEXT_DOMAIN) /* should be defined by cc -D */ #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); (void) time(&start_time); if (lstat("/var/run", &sb) != 0 || !(S_ISDIR(sb.st_mode))) { DoorFileName = OLD_DOORFILE; PidFileName = OLD_PIDFILE; } defaults(); while ((i = getopt(argc, argv, "df:p:m:tT")) != EOF) { switch (i) { case 'f': /* configuration file */ ConfFile = optarg; break; case 'd': /* debug */ Debug++; break; case 'p': /* path */ LogName = optarg; break; case 'm': /* mark interval */ for (pstr = optarg; *pstr; pstr++) { if (! (isdigit(*pstr))) { (void) fprintf(stderr, "Illegal interval\n"); usage(); } } MarkInterval = atoi(optarg); if (MarkInterval < 1 || MarkInterval > INT_MAX) { (void) fprintf(stderr, "Interval must be between 1 and %d\n", INT_MAX); usage(); } break; case 't': /* turn off remote reception */ tflag++; turnoff++; break; case 'T': /* turn on remote reception */ Tflag++; turnoff = 0; break; default: usage(); } } if (optind < argc) usage(); if (tflag && Tflag) { (void) fprintf(stderr, "specify only one of -t and -T\n"); usage(); } /* * close all fd's except 0-2 */ closefrom(3); if (!Debug) { if (fork()) return (0); (void) close(0); (void) open("/", 0); (void) dup2(0, 1); (void) dup2(0, 2); untty(); } if (Debug) { mythreadno = pthread_self(); } /* * DO NOT call logerror() until tmpq is initialized. */ disable_errorlog(); /* * ensure that file descriptor limit is "high enough" */ (void) getrlimit(RLIMIT_NOFILE, &rlim); if (rlim.rlim_cur < rlim.rlim_max) rlim.rlim_cur = rlim.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) logerror("Unable to increase file descriptor limit."); /* block all signals from all threads initially */ (void) sigfillset(&allsigs); (void) pthread_sigmask(SIG_BLOCK, &allsigs, NULL); DPRINT2(1, "main(%u): Started at time %s", mythreadno, ctime_r(&start_time, cbuf)); init(); /* read configuration, start threads */ DPRINT1(1, "main(%u): off & running....\n", mythreadno); /* now set up to catch signals we care about */ (void) sigemptyset(&sigs); (void) sigaddset(&sigs, SIGHUP); /* reconfigure */ (void) sigaddset(&sigs, SIGALRM); /* mark & flush timer */ (void) sigaddset(&sigs, SIGTERM); /* exit */ (void) sigaddset(&sigs, SIGINT); /* exit if debugging */ (void) sigaddset(&sigs, SIGQUIT); /* exit if debugging */ (void) sigaddset(&sigs, SIGPIPE); /* catch & discard */ (void) sigaddset(&sigs, SIGUSR1); /* dump debug stats */ /* * We must set up to catch these signals, even though sigwait * will get them before the isr does. Setting SA_SIGINFO ensures * that signals will be enqueued. */ act.sa_flags = SA_SIGINFO; act.sa_sigaction = signull; (void) sigaction(SIGHUP, &act, NULL); (void) sigaction(SIGALRM, &act, NULL); (void) sigaction(SIGTERM, &act, NULL); (void) sigaction(SIGINT, &act, NULL); (void) sigaction(SIGQUIT, &act, NULL); (void) sigaction(SIGPIPE, &act, NULL); (void) sigaction(SIGUSR1, &act, NULL); /* we now turn into the signal handling thread */ DPRINT1(2, "main(%u): now handling signals\n", mythreadno); for (;;) { (void) sigwait(&sigs, &sig); DPRINT2(2, "main(%u): received signal %d\n", mythreadno, sig); switch (sig) { case SIGALRM: DPRINT1(1, "main(%u): Got SIGALRM\n", mythreadno); flushmsg(NOCOPY); if (Marking && (++mcount % MARKCOUNT == 0)) { if (logmymsg(LOG_INFO, "-- MARK --", ADDDATE|MARK|NOCOPY, 0) == -1) { MALLOC_FAIL( "dropping MARK message"); } mcount = 0; } curalarm = MarkInterval * 60 / MARKCOUNT; (void) alarm((unsigned)curalarm); DPRINT2(2, "main(%u): Next alarm in %d " "seconds\n", mythreadno, curalarm); break; case SIGHUP: DPRINT1(1, "main(%u): got SIGHUP - " "reconfiguring\n", mythreadno); reconfigure(); DPRINT1(1, "main(%u): done processing SIGHUP\n", mythreadno); break; case SIGQUIT: case SIGINT: if (!Debug) { /* allow these signals if debugging */ break; } /* FALLTHROUGH */ case SIGTERM: DPRINT2(1, "main(%u): going down on signal %d\n", mythreadno, sig); (void) alarm(0); flushmsg(0); errno = 0; t_errno = 0; logerror("going down on signal %d", sig); disable_errorlog(); /* force msg to console */ (void) shutdown_msg(); /* stop threads */ shutdown_input(); close_door(); delete_doorfiles(); return (0); break; case SIGUSR1: /* secret debug dump mode */ /* if in debug mode, use stdout */ if (Debug) { dumpstats(STDOUT_FILENO); break; } /* otherwise dump to a debug file */ if ((fd = open(DEBUGFILE, (O_WRONLY|O_CREAT|O_TRUNC|O_EXCL), 0644)) < 0) break; dumpstats(fd); (void) close(fd); break; default: DPRINT2(2, "main(%u): unexpected signal %d\n", mythreadno, sig); break; } } } /* * Attempts to open the local log device * and return a file descriptor. */ static int openklog(char *name, int mode) { int fd; struct strioctl str; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if ((fd = open(name, mode)) < 0) { logerror("cannot open %s", name); DPRINT3(1, "openklog(%u): cannot create %s (%d)\n", mythreadno, name, errno); return (-1); } str.ic_cmd = I_CONSLOG; str.ic_timout = 0; str.ic_len = 0; str.ic_dp = NULL; if (ioctl(fd, I_STR, &str) < 0) { logerror("cannot register to log console messages"); DPRINT2(1, "openklog(%u): cannot register to log " "console messages (%d)\n", mythreadno, errno); return (-1); } return (fd); } /* * Open the log device, and pull up all pending messages. */ static void prepare_sys_poll() { int nfds, funix; if ((funix = openklog(LogName, O_RDONLY)) < 0) { logerror("can't open kernel log device - fatal"); exit(1); } Pfd.fd = funix; Pfd.events = POLLIN; for (;;) { nfds = poll(&Pfd, 1, 0); if (nfds <= 0) { if (sys_init_msg_count > 0) flushmsg(SYNC_FILE); break; } if (Pfd.revents & POLLIN) { getkmsg(0); } else if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { logerror("kernel log driver poll error"); break; } } } /* * this thread listens to the local stream log driver for log messages * generated by this host, formats them, and queues them to the logger * thread. */ /*ARGSUSED*/ static void * sys_poll(void *ap) { int nfds; static int klogerrs = 0; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(1, "sys_poll(%u): sys_thread started\n", mythreadno); /* * Try to process as many messages as we can without blocking on poll. * We count such "initial" messages with sys_init_msg_count and * enqueue them without the SYNC_FILE flag. When no more data is * waiting on the local log device, we set timeout to INFTIM, * clear sys_init_msg_count, and generate a flush message to sync * the previously counted initial messages out to disk. */ sys_init_msg_count = 0; for (;;) { errno = 0; t_errno = 0; nfds = poll(&Pfd, 1, INFTIM); if (nfds == 0) continue; if (nfds < 0) { if (errno != EINTR) logerror("poll"); continue; } if (Pfd.revents & POLLIN) { getkmsg(INFTIM); } else { if (shutting_down) { pthread_exit(0); } if (Pfd.revents & (POLLNVAL|POLLHUP|POLLERR)) { logerror("kernel log driver poll error"); (void) close(Pfd.fd); Pfd.fd = -1; } } while (Pfd.fd == -1 && klogerrs++ < 10) { Pfd.fd = openklog(LogName, O_RDONLY); } if (klogerrs >= 10) { logerror("can't reopen kernel log device - fatal"); exit(1); } } /*NOTREACHED*/ return (NULL); } /* * Pull up one message from log driver. */ static void getkmsg(int timeout) { int flags = 0, i; char *lastline; struct strbuf ctl, dat; struct log_ctl hdr; char buf[MAXLINE+1]; size_t buflen; size_t len; char tmpbuf[MAXLINE+1]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } dat.maxlen = MAXLINE; dat.buf = buf; ctl.maxlen = sizeof (struct log_ctl); ctl.buf = (caddr_t)&hdr; while ((i = getmsg(Pfd.fd, &ctl, &dat, &flags)) == MOREDATA) { lastline = &dat.buf[dat.len]; *lastline = '\0'; DPRINT2(5, "sys_poll:(%u): getmsg: dat.len = %d\n", mythreadno, dat.len); buflen = strlen(buf); len = findnl_bkwd(buf, buflen); (void) memcpy(tmpbuf, buf, len); tmpbuf[len] = '\0'; /* * Format sys will enqueue the log message. * Set the sync flag if timeout != 0, which * means that we're done handling all the * initial messages ready during startup. */ if (timeout == 0) { formatsys(&hdr, tmpbuf, 0); sys_init_msg_count++; } else { formatsys(&hdr, tmpbuf, 1); } sys_msg_count++; if (len != buflen) { /* If anything remains in buf */ size_t remlen; if (buf[len] == '\n') { /* skip newline */ len++; } /* * Move the remaining bytes to * the beginnning of buf. */ remlen = buflen - len; (void) memcpy(buf, &buf[len], remlen); dat.maxlen = MAXLINE - remlen; dat.buf = &buf[remlen]; } else { dat.maxlen = MAXLINE; dat.buf = buf; } } if (i == 0 && dat.len > 0) { dat.buf[dat.len] = '\0'; /* * Format sys will enqueue the log message. * Set the sync flag if timeout != 0, which * means that we're done handling all the * initial messages ready during startup. */ DPRINT2(5, "getkmsg(%u): getmsg: dat.maxlen = %d\n", mythreadno, dat.maxlen); DPRINT2(5, "getkmsg(%u): getmsg: dat.len = %d\n", mythreadno, dat.len); DPRINT2(5, "getkmsg(%u): getmsg: strlen(dat.buf) = %d\n", mythreadno, strlen(dat.buf)); DPRINT2(5, "getkmsg(%u): getmsg: dat.buf = \"%s\"\n", mythreadno, dat.buf); DPRINT2(5, "getkmsg(%u): buf len = %d\n", mythreadno, strlen(buf)); if (timeout == 0) { formatsys(&hdr, buf, 0); sys_init_msg_count++; } else { formatsys(&hdr, buf, 1); } sys_msg_count++; } else if (i < 0 && errno != EINTR) { if (!shutting_down) { logerror("kernel log driver read error"); } (void) close(Pfd.fd); Pfd.fd = -1; } } /* * this thread polls all the network interfaces for syslog messages * forwarded to us, tags them with the hostname they are received * from, and queues them to the logger thread. */ /*ARGSUSED*/ static void * net_poll(void *ap) { int nfds, i; int flags = 0; struct t_unitdata *udp; struct t_uderr *errp; char buf[MAXLINE+1]; char *uap; log_message_t *mp; host_info_t *hinfo; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(1, "net_poll(%u): net_thread started\n", mythreadno); _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)) for (;;) { errno = 0; t_errno = 0; nfds = poll(Nfd, Ninputs, -1); if (nfds == 0) continue; if (nfds < 0) { if (errno != EINTR) logerror("poll"); continue; } for (i = 0; nfds > 0 && i < Ninputs; i++) { if ((Nfd[i].revents & POLLIN) == 0) { if (shutting_down) { pthread_exit(0); } if (Nfd[i].revents & (POLLNVAL|POLLHUP|POLLERR)) { logerror("POLLNVAL|POLLHUP|POLLERR"); (void) t_close(Nfd[i].fd); Nfd[i].fd = -1; nfds--; } continue; } udp = Udp[i]; udp->udata.buf = buf; udp->udata.maxlen = MAXLINE; udp->udata.len = 0; flags = 0; if (t_rcvudata(Nfd[i].fd, udp, &flags) < 0) { errp = Errp[i]; if (t_errno == TLOOK) { if (t_rcvuderr(Nfd[i].fd, errp) < 0) { if (!shutting_down) { logerror("t_rcvuderr"); } t_close(Nfd[i].fd); Nfd[i].fd = -1; } } else { if (!shutting_down) { logerror("t_rcvudata"); } t_close(Nfd[i].fd); Nfd[i].fd = -1; } nfds--; if (shutting_down) { pthread_exit(0); } continue; } nfds--; if (udp->udata.len == 0) { if (Debug) { uap = NULL; if (udp->addr.len > 0) { uap = taddr2uaddr(&Ncf[i], &udp->addr); } DPRINT2(1, "net_poll(%u):" " received empty packet" " from %s\n", mythreadno, uap ? uap : ""); if (uap) free(uap); } continue; /* No data */ } if (udp->addr.len == 0) { /* * The previous message was larger than * MAXLINE, and T_MORE should have been set. * Further data needs to be discarded as * we've already received MAXLINE. */ DPRINT1(1, "net_poll(%u): discarding packet " "exceeds max line size\n", mythreadno); continue; } net_msg_count++; if ((mp = new_msg()) == NULL) { MALLOC_FAIL("dropping message from " "remote"); continue; } buf[udp->udata.len] = '\0'; formatnet(&udp->udata, mp); if (Debug) { uap = taddr2uaddr(&Ncf[i], &udp->addr); DPRINT2(1, "net_poll(%u): received message" " from %s\n", mythreadno, uap ? uap : ""); free(uap); } if ((hinfo = malloc(sizeof (*hinfo))) == NULL || (hinfo->addr.buf = malloc(udp->addr.len)) == NULL) { MALLOC_FAIL("dropping message from " "remote"); if (hinfo) { free(hinfo); } free_msg(mp); continue; } hinfo->ncp = &Ncf[i]; hinfo->addr.len = udp->addr.len; (void) memcpy(hinfo->addr.buf, udp->addr.buf, udp->addr.len); mp->ptr = hinfo; if (dataq_enqueue(&hnlq, (void *)mp) == -1) { MALLOC_FAIL("dropping message from " "remote"); free_msg(mp); free(hinfo->addr.buf); free(hinfo); continue; } DPRINT3(5, "net_poll(%u): enqueued msg %p " "on queue %p\n", mythreadno, mp, &hnlq); } } /*NOTREACHED*/ return (NULL); } static void usage(void) { (void) fprintf(stderr, "usage: syslogd [-d] [-t|-T] [-mmarkinterval] [-ppath]" " [-fconffile]\n"); exit(1); } static void untty(void) { if (!Debug) (void) setsid(); } /* * generate a log message internally. The original version of syslogd * simply called logmsg directly, but because everything is now based * on message passing, we need an internal way to generate and queue * log messages from within syslogd itself. */ static int logmymsg(int pri, char *msg, int flags, int pending) { log_message_t *mp; pthread_t mythreadno; dataq_t *qptr; if (Debug) { mythreadno = pthread_self(); } if ((mp = new_msg()) == NULL) { return (-1); } _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)) mp->pri = pri; mp->hlp = &LocalHostName; (void) strlcpy(mp->msg, msg, MAXLINE+1); mp->flags = flags; (void) time(&mp->ts); qptr = pending ? &tmpq : &inputq; if (dataq_enqueue(qptr, (void *)mp) == -1) { free_msg(mp); return (-1); } DPRINT3(5, "logmymsg(%u): enqueued msg %p on queue %p\n", mythreadno, mp, qptr); DPRINT2(5, "logmymsg(%u): Message content: %s\n", mythreadno, msg); return (0); } /* * Generate an internal shutdown message */ static int shutdown_msg(void) { pthread_t mythreadno; log_message_t *mp; if (Debug) { mythreadno = pthread_self(); } if ((mp = new_msg()) == NULL) { return (-1); } _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)); mp->flags = SHUTDOWN; mp->hlp = &LocalHostName; if (dataq_enqueue(&inputq, (void *)mp) == -1) { free_msg(mp); return (-1); } DPRINT3(5, "shutdown_msg(%u): enqueued msg %p on queue %p\n", mythreadno, mp, &inputq); return (0); } /* * Generate an internal flush message */ static void flushmsg(int flags) { log_message_t *mp; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if ((mp = new_msg()) == NULL) { MALLOC_FAIL("dropping flush msg"); return; } _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)); mp->flags = FLUSHMSG | flags; mp->hlp = &LocalHostName; if (dataq_enqueue(&inputq, (void *)mp) == -1) { free_msg(mp); MALLOC_FAIL("dropping flush msg"); return; } DPRINT4(5, "flush_msg(%u): enqueued msg %p on queue %p, flags " "0x%x\n", mythreadno, mp, &inputq, flags); } /* * Do some processing on messages received from the net */ static void formatnet(struct netbuf *nbp, log_message_t *mp) { char *p; int pri; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT2(5, "formatnet(%u): called for msg %p\n", mythreadno, mp); mp->flags = NETWORK; (void) time(&mp->ts); /* test for special codes */ pri = DEFUPRI; p = nbp->buf; DPRINT2(9, "formatnet(%u): Message content:\n>%s<\n", mythreadno, p); if (*p == '<' && isdigit(*(p+1))) { pri = 0; while (isdigit(*++p)) pri = 10 * pri + (*p - '0'); if (*p == '>') ++p; if (pri <= 0 || pri >= (LOG_NFACILITIES << 3)) pri = DEFUPRI; } mp->pri = pri; (void) strlcpy(mp->msg, p, MAXLINE+1); } /* * Do some processing on messages generated by this host * and then enqueue the log message. */ static void formatsys(struct log_ctl *lp, char *msg, int sync) { char *p, *q; char line[MAXLINE + 1]; size_t msglen; log_message_t *mp; char cbuf[30]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT3(3, "formatsys(%u): log_ctl.mid = %d, log_ctl.sid = %d\n", mythreadno, lp->mid, lp->sid); DPRINT2(9, "formatsys(%u): Message Content:\n>%s<\n", mythreadno, msg); /* msglen includes the null termination */ msglen = strlen(msg) + 1; for (p = msg; *p != '\0'; ) { size_t linelen; size_t len; /* * Allocate a log_message_t structure. * We should do it here since a single message (msg) * could be composed of many lines. */ _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)); if ((mp = new_msg()) == NULL) { MALLOC_FAIL("dropping message"); /* * Should bail out from the loop. */ break; } mp->flags &= ~NETWORK; mp->hlp = &LocalHostName; mp->ts = lp->ttime; if (lp->flags & SL_LOGONLY) mp->flags |= IGN_CONS; if (lp->flags & SL_CONSONLY) mp->flags |= IGN_FILE; /* extract facility */ if ((lp->pri & LOG_FACMASK) == LOG_KERN) { (void) sprintf(line, "%.15s ", ctime_r(&mp->ts, cbuf) + 4); } else { (void) sprintf(line, ""); } linelen = strlen(line); q = line + linelen; DPRINT2(5, "formatsys(%u): msglen = %d\n", mythreadno, msglen); len = copynl_frwd(q, MAXLINE + 1 - linelen, p, msglen); DPRINT2(5, "formatsys(%u): len (copynl_frwd) = %d\n", mythreadno, len); p += len; msglen -= len; if (*p == '\n') { /* skip newline */ p++; } if (sync && ((lp->pri & LOG_FACMASK) == LOG_KERN)) mp->flags |= SYNC_FILE; /* fsync file after write */ if (len != 0) { (void) strlcpy(mp->msg, line, MAXLINE+1); mp->pri = lp->pri; if (dataq_enqueue(&inputq, (void *)mp) == -1) { free_msg(mp); MALLOC_FAIL("dropping message"); break; } DPRINT3(5, "formatsys(%u): sys_thread enqueued msg " "%p on queue %p\n", mythreadno, mp, &inputq); } else free_msg(mp); } } /* * Log a message to the appropriate log files, users, etc. based on * the priority. */ /*ARGSUSED*/ static void * logmsg(void *ap) { struct filed *f; int fac, prilev, flags, refcnt; int fake_shutdown, skip_shutdown; log_message_t *mp, *save_mp; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(1, "logmsg(%u): msg dispatcher started\n", mythreadno); fake_shutdown = skip_shutdown = 0; save_mp = NULL; for (;;) { if (save_mp) { /* * If we have set aside a message in order to fake a * SHUTDOWN, use that message before picking from the * queue again. */ mp = save_mp; save_mp = NULL; } else { (void) dataq_dequeue(&inputq, (void **)&mp, 0); } _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*mp)) DPRINT3(5, "logmsg(%u): msg dispatcher dequeued %p from " "queue %p\n", mythreadno, mp, &inputq); /* * In most cases, if the message traffic is low, logmsg() wakes * up when it receives the SHUTDOWN msg, and will sleep until * HUP process is complete. However, if the inputq is too * long, logmsg() may not receive SHUTDOWN before reconfigure() * releases the logger fds, filed and logit threads. That, in * turn, will cause logmsg to refer to invalid fileds. * * logmsg() needs to respond to the SHUTDOWN message within * LOOP_INTERVAL seconds when reconfigure() enqueues it. It * does so in most cases. When it does not respond in time, * logmsg() needs to be in suspended state immediately, since * filed may have been invalidated. reconfigure() will set the * HUP_SUSP_LOGMSG_REQD bit in hup_state and wait another * LOOP_INTERVAL seconds before proceeding. * * When HUP_SUSP_LOGMSG_REQD is set, we will create a fake * SHUTDOWN message, and dispatch it to the various logit * threads, and logmsg() itself will suspend. In order to * ignore the real SHUTDOWN which will arrive later, we keep a * counter (skip_shutdown) and decrement it when the SHUTDOWN * message arrives. */ if ((hup_state & HUP_SUSP_LOGMSG_REQD) && (mp->flags & SHUTDOWN) == 0) { DPRINT1(3, "logmsg(%u): suspend request\n", mythreadno); save_mp = mp; /* create a fake SHUTDOWN msg */ if ((mp = new_msg()) == NULL) { MALLOC_FAIL("dropping message"); if (mp->flags & SHUTDOWN) { (void) logerror_to_console(1, "unable to shutdown " "logger thread"); } continue; } mp->flags = SHUTDOWN; mp->hlp = &LocalHostName; fake_shutdown = 1; skip_shutdown++; DPRINT2(3, "logmsg(%u): pending SHUTDOWN %d\n", mythreadno, skip_shutdown); } /* * is it a shutdown or flush message ? */ if ((mp->flags & SHUTDOWN) || (mp->flags & FLUSHMSG)) { pthread_mutex_lock(&mp->msg_mutex); if ((mp->flags & SHUTDOWN) && !fake_shutdown && skip_shutdown > 0) { skip_shutdown--; pthread_mutex_unlock(&mp->msg_mutex); free_msg(mp); DPRINT2(3, "logmsg(%u): released late " "arrived SHUTDOWN. pending %d\n", mythreadno, skip_shutdown); continue; } for (f = Files; f < &Files[nlogs]; f++) { pthread_mutex_lock(&f->filed_mutex); if (f->f_type == F_UNUSED) { pthread_mutex_unlock(&f->filed_mutex); continue; } f->f_queue_count++; mp->refcnt++; if (dataq_enqueue(&f->f_queue, (void *)mp) == -1) { f->f_queue_count--; mp->refcnt--; pthread_mutex_unlock(&f->filed_mutex); MALLOC_FAIL("dropping message"); if (mp->flags & SHUTDOWN) { (void) logerror_to_console(1, "unable to shutdown " "logger thread"); } continue; } DPRINT3(5, "logmsg(%u): enqueued msg %p " "on queue %p\n", mythreadno, mp, &f->f_queue); pthread_mutex_unlock(&f->filed_mutex); } /* * flags value needs to be saved because mp may * have been freed before SHUTDOWN test below. */ flags = mp->flags; refcnt = mp->refcnt; pthread_mutex_unlock(&mp->msg_mutex); if (refcnt == 0) free_msg(mp); if (flags & SHUTDOWN) { pthread_mutex_lock(&hup_lock); while (hup_state != HUP_COMPLETED) { hup_state |= HUP_LOGMSG_SUSPENDED; (void) pthread_cond_wait(&hup_done, &hup_lock); hup_state &= ~HUP_LOGMSG_SUSPENDED; } hup_state = HUP_ACCEPTABLE; pthread_mutex_unlock(&hup_lock); fake_shutdown = 0; } continue; } /* * Check to see if msg looks non-standard. */ if ((int)strlen(mp->msg) < 16 || mp->msg[3] != ' ' || mp->msg[6] != ' ' || mp->msg[9] != ':' || mp->msg[12] != ':' || mp->msg[15] != ' ') mp->flags |= ADDDATE; /* extract facility and priority level */ fac = (mp->pri & LOG_FACMASK) >> 3; if (mp->flags & MARK) fac = LOG_NFACILITIES; prilev = mp->pri & LOG_PRIMASK; DPRINT3(3, "logmsg(%u): fac = %d, pri = %d\n", mythreadno, fac, prilev); /* * Because different devices log at different speeds, * it's important to hold the mutex for the current * message until it's been enqueued to all log files, * so the reference count is accurate before any * of the log threads can decrement it. */ _NOTE(NOW_VISIBLE_TO_OTHER_THREADS(*mp)) _NOTE(COMPETING_THREADS_NOW) pthread_mutex_lock(&mp->msg_mutex); for (f = Files; f < &Files[nlogs]; f++) { /* skip messages that are incorrect priority */ if (f->f_pmask[fac] < (unsigned)prilev || f->f_pmask[fac] == NOPRI) continue; if (f->f_queue_count > Q_HIGHWATER_MARK) { DPRINT4(5, "logmsg(%u): Dropping message " "%p on file %p, count = %d\n", mythreadno, mp, f, f->f_queue_count); continue; } /* * Need to grab filed_mutex before testing the f_type. * Otherwise logit() may set F_UNUSED after the test * below, and start pulling out the pending messages. */ pthread_mutex_lock(&f->filed_mutex); if (f->f_type == F_UNUSED || (f->f_type == F_FILE && (mp->flags & IGN_FILE)) || (f->f_type == F_CONSOLE && (mp->flags & IGN_CONS))) { pthread_mutex_unlock(&f->filed_mutex); continue; } f->f_queue_count++; mp->refcnt++; if (dataq_enqueue(&f->f_queue, (void *)mp) == -1) { f->f_queue_count--; mp->refcnt--; pthread_mutex_unlock(&f->filed_mutex); MALLOC_FAIL("dropping message"); continue; } DPRINT3(5, "logmsg(%u): enqueued msg %p on queue " "%p\n", mythreadno, mp, &f->f_queue); pthread_mutex_unlock(&f->filed_mutex); } refcnt = mp->refcnt; pthread_mutex_unlock(&mp->msg_mutex); if (refcnt == 0) free_msg(mp); } /*NOTREACHED*/ return (NULL); } /* * function to actually write the log message to the selected file. * each file has a logger thread that runs this routine. The function * is called with a pointer to its file structure. */ static void * logit(void *ap) { struct filed *f = ap; log_message_t *mp; int forwardingloop = 0; char *errmsg = "logit(%u): %s to %s forwarding loop detected\n"; int i, currofst, prevofst, refcnt; host_list_t *hlp; assert(f != NULL); DPRINT4(5, "logit(%u): logger started for \"%s\" (queue %p, filed " "%p)\n", f->f_thread, f->f_un.f_fname, &f->f_queue, f); _NOTE(COMPETING_THREADS_NOW); while (f->f_type != F_UNUSED) { (void) dataq_dequeue(&f->f_queue, (void **)&mp, 0); DPRINT3(5, "logit(%u): logger dequeued msg %p from queue " "%p\n", f->f_thread, mp, &f->f_queue); pthread_mutex_lock(&f->filed_mutex); assert(f->f_queue_count > 0); f->f_queue_count--; pthread_mutex_unlock(&f->filed_mutex); assert(mp->refcnt > 0); /* * is it a shutdown message ? */ if (mp->flags & SHUTDOWN) { pthread_mutex_lock(&mp->msg_mutex); refcnt = --mp->refcnt; pthread_mutex_unlock(&mp->msg_mutex); if (refcnt == 0) free_msg(mp); break; } /* * Is it a logsync message? */ if ((mp->flags & (FLUSHMSG | LOGSYNC)) == (FLUSHMSG | LOGSYNC)) { if (f->f_type != F_FILE) goto out; /* nothing to do */ (void) close(f->f_file); f->f_file = open64(f->f_un.f_fname, O_WRONLY|O_APPEND|O_NOCTTY); if (f->f_file < 0) { f->f_type = F_UNUSED; logerror(f->f_un.f_fname); f->f_stat.errs++; } goto out; } /* * If the message flags include both flush and sync, * then just sync the file out to disk if appropriate. */ if ((mp->flags & (FLUSHMSG | SYNC_FILE)) == (FLUSHMSG | SYNC_FILE)) { if (f->f_type == F_FILE) { DPRINT2(5, "logit(%u): got FLUSH|SYNC " "for filed %p\n", f->f_thread, f); (void) fsync(f->f_file); } goto out; } /* * Otherwise if it's a standard flush message, write * out any saved messages to the file. */ if ((mp->flags & FLUSHMSG) && (f->f_prevcount > 0)) { set_flush_msg(f); writemsg(SAVED, f); goto out; } (void) strlcpy(f->f_current.msg, mp->msg, MAXLINE+1); (void) strlcpy(f->f_current.host, mp->hlp->hl_hosts[0], SYS_NMLN); f->f_current.pri = mp->pri; f->f_current.flags = mp->flags; f->f_current.time = mp->ts; f->f_msgflag &= ~CURRENT_VALID; hlp = mp->hlp; prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16; currofst = (f->f_current.flags & ADDDATE) ? 0 : 16; if (f->f_type == F_FORW) { /* * Should not forward MARK messages, as they are * not defined outside of the current system. */ if (mp->flags & MARK) { DPRINT1(1, "logit(%u): cannot forward " "Mark\n", f->f_thread); goto out; } /* * can not forward message if we do * not have a host to forward to */ if (hlp == (host_list_t *)NULL) goto out; /* * a forwarding loop is created on machines * with multiple interfaces because the * network address of the sender is different * to the receiver even though it is the * same machine. Instead, if the * hostname the source and target are * the same the message if thrown away */ forwardingloop = 0; for (i = 0; i < hlp->hl_cnt; i++) { if (strcmp(hlp->hl_hosts[i], f->f_un.f_forw.f_hname) == 0) { DPRINT3(1, errmsg, f->f_thread, f->f_un.f_forw.f_hname, hlp->hl_hosts[i]); forwardingloop = 1; break; } } if (forwardingloop == 1) { f->f_stat.cantfwd++; goto out; } } f->f_msgflag |= CURRENT_VALID; /* check for dup message */ if (f->f_type != F_FORW && (f->f_msgflag & OLD_VALID) && prevofst == currofst && (strcmp(f->f_prevmsg.msg + prevofst, f->f_current.msg + currofst) == 0) && (strcmp(f->f_prevmsg.host, f->f_current.host) == 0)) { /* a dup */ DPRINT2(2, "logit(%u): msg is dup - %p\n", f->f_thread, mp); if (currofst == 16) { (void) strncpy(f->f_prevmsg.msg, f->f_current.msg, 15); /* update time */ } f->f_prevcount++; f->f_stat.dups++; f->f_stat.total++; f->f_msgflag &= ~CURRENT_VALID; } else { /* new: mark or prior dups exist */ if (f->f_current.flags & MARK || f->f_prevcount > 0) { if (f->f_prevcount > 0 && f->f_type != F_FORW) { set_flush_msg(f); if (f->f_msgflag & OLD_VALID) { writemsg(SAVED, f); } } if (f->f_msgflag & CURRENT_VALID) writemsg(CURRENT, f); if (!(mp->flags & NOCOPY)) copy_msg(f); if (f->f_current.flags & MARK) { DPRINT2(2, "logit(%u): msg is " "mark - %p)\n", f->f_thread, mp); f->f_msgflag &= ~OLD_VALID; } else { DPRINT2(2, "logit(%u): saving " "message - %p\n", f->f_thread, mp); } f->f_stat.total++; } else { /* new message */ DPRINT2(2, "logit(%u): msg is new " "- %p\n", f->f_thread, mp); writemsg(CURRENT, f); if (!(mp->flags & NOCOPY)) copy_msg(f); f->f_stat.total++; } } /* * if message refcnt goes to zero after we decrement * it here, we are the last consumer of the message, * and we should free it. We need to hold the lock * between decrementing the count and checking for * zero so another thread doesn't beat us to it. */ out: pthread_mutex_lock(&mp->msg_mutex); refcnt = --mp->refcnt; pthread_mutex_unlock(&mp->msg_mutex); if (refcnt == 0) free_msg(mp); } /* register our exit */ /* * Pull out all pending messages, if they exist. */ pthread_mutex_lock(&f->filed_mutex); while (f->f_queue_count > 0) { (void) dataq_dequeue(&f->f_queue, (void **)&mp, 0); DPRINT3(5, "logit(%u): logger dequeued msg %p from queue " "%p\n", f->f_thread, mp, &f->f_queue); pthread_mutex_lock(&mp->msg_mutex); refcnt = --mp->refcnt; pthread_mutex_unlock(&mp->msg_mutex); if (refcnt == 0) free_msg(mp); f->f_queue_count--; } pthread_mutex_unlock(&f->filed_mutex); if (f->f_type != F_USERS && f->f_type != F_WALL && f->f_type != F_UNUSED) { if (f->f_type == F_FORW) (void) t_close(f->f_file); else (void) close(f->f_file); } /* * Since f_type may have been changed before this point, we need * to test orig_type. */ if (f->f_orig_type == F_FORW) { free(f->f_un.f_forw.f_addr.buf); } f->f_type = F_UNUSED; pthread_mutex_lock(&cft); --conf_threads; pthread_mutex_unlock(&cft); DPRINT1(5, "logit(%u): logging thread exited\n", f->f_thread); return (NULL); } /* * change the previous message to a flush message, stating how * many repeats occurred since the last flush */ static void set_flush_msg(struct filed *f) { char tbuf[10]; int prevofst = (f->f_prevmsg.flags & ADDDATE) ? 0 : 16; if (f->f_prevcount == 1) (void) strncpy(tbuf, "time", sizeof (tbuf)); else (void) strncpy(tbuf, "times", sizeof (tbuf)); (void) sprintf(f->f_prevmsg.msg+prevofst, "last message repeated %d %s", f->f_prevcount, tbuf); f->f_prevcount = 0; f->f_msgflag |= OLD_VALID; } /* * the actual writing of the message is broken into a separate function * because each file has a current and saved message associated with * it (for duplicate message detection). It is necessary to be able * to write either the saved message or the current message. */ static void writemsg(int selection, struct filed *f) { char *cp, *p; int pri; int flags; int l; time_t ts; struct t_unitdata ud; char *eomp, *eomp2, *from, *text, *msg; char line[MAXLINE*2]; char head[MAXLINE+1]; char tmpbuf[MAXLINE+1]; char cbuf[30]; char *filtered; char *msgid_start, *msgid_end; pthread_t mythreadno; size_t hlen, filter_len; if (Debug) { mythreadno = pthread_self(); } switch (selection) { default: case CURRENT: /* print current message */ msg = f->f_current.msg; from = f->f_current.host; pri = f->f_current.pri; flags = f->f_current.flags; ts = f->f_current.time; f->f_msgflag &= ~CURRENT_VALID; break; case SAVED: /* print saved message */ msg = f->f_prevmsg.msg; from = f->f_prevmsg.host; pri = f->f_prevmsg.pri; flags = f->f_prevmsg.flags; ts = f->f_prevmsg.time; f->f_msgflag &= ~OLD_VALID; break; } if (msg[0] == '\0') return; cp = line; if (flags & ADDDATE) (void) strncpy(cp, ctime_r(&ts, cbuf) + 4, 15); else (void) strncpy(cp, msg, 15); line[15] = '\0'; (void) strcat(cp, " "); (void) strcat(cp, from); (void) strcat(cp, " "); text = cp + strlen(cp); if (flags & ADDDATE) (void) strcat(cp, msg); else (void) strcat(cp, msg+16); DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text); errno = 0; t_errno = 0; switch (f->f_type) { case F_UNUSED: DPRINT1(1, "writemsg(%u): UNUSED\n", mythreadno); break; case F_FORW: DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n", mythreadno, msg, TypeNames[f->f_type], f->f_un.f_forw.f_hname); hlen = snprintf(head, sizeof (head), "<%d>%.15s ", pri, cp); DPRINT2(5, "writemsg(%u): head = \"%s\"\n", mythreadno, head); DPRINT2(5, "writemsg(%u): hlen = %d\n", mythreadno, hlen); l = strlen(text); p = text; DPRINT2(5, "writemsg(%u): text = \"%s\"\n", mythreadno, text); DPRINT2(5, "writemsg(%u): strlen(text) = %d\n", mythreadno, l); (void) strncpy(tmpbuf, head, hlen); while (l > 0) { size_t len; len = copy_frwd(tmpbuf + hlen, sizeof (tmpbuf) - hlen, p, l); DPRINT2(5, "writemsg(%u): tmpbuf = \"%s\"\n", mythreadno, tmpbuf); DPRINT2(5, "writemsg(%u): len = %d\n", mythreadno, len); DPRINT2(5, "writemsg(%u): strlen(tmpbuf) = %d\n", mythreadno, strlen(tmpbuf)); ud.opt.buf = NULL; ud.opt.len = 0; ud.udata.buf = tmpbuf; ud.udata.len = len + hlen; ud.addr.maxlen = f->f_un.f_forw.f_addr.maxlen; ud.addr.buf = f->f_un.f_forw.f_addr.buf; ud.addr.len = f->f_un.f_forw.f_addr.len; if (t_sndudata(f->f_file, &ud) < 0) { if ((hup_state & HUP_INPROGRESS) && f->f_type == F_UNUSED) { break; } (void) t_close(f->f_file); f->f_type = F_UNUSED; logerror("t_sndudata"); /* * Since it has already failed, it's not worth * continuing output from the middle of * message string. */ break; } p += len; l -= len; } break; case F_CONSOLE: case F_TTY: case F_FILE: case F_USERS: case F_WALL: DPRINT4(1, "writemsg(%u): Logging msg '%s' to %s %s\n", mythreadno, msg, TypeNames[f->f_type], ((f->f_type == F_USERS) || (f->f_type == F_WALL)) ? "" : f->f_un.f_fname); /* * filter the string in preparation for writing it * save the original for possible forwarding. * In case every byte in cp is a control character, * allocates large enough buffer for filtered. */ filter_len = strlen(cp) * 4 + 1; filtered = (char *)malloc(filter_len); if (!filtered) { MALLOC_FAIL("dropping message"); /* seems we can just return */ return; } DPRINT3(5, "writemsg(%u): " "filtered allocated (%p: %d bytes)\n", mythreadno, filtered, filter_len); /* -3 : we may add "\r\n" to ecomp(filtered) later */ filter_string(cp, filtered, filter_len - 3); DPRINT2(5, "writemsg(%u): strlen(filtered) = %d\n", mythreadno, strlen(filtered)); /* * If we're writing to the console, strip out the message ID * to reduce visual clutter. */ if ((msgid_start = strstr(filtered, "[ID ")) != NULL && (msgid_end = strstr(msgid_start, "] ")) != NULL && f->f_type == F_CONSOLE) (void) strcpy(msgid_start, msgid_end + 2); eomp = filtered + strlen(filtered); if ((f->f_type == F_USERS) || (f->f_type == F_WALL)) { /* CSTYLED */ (void) strcat(eomp, "\r\n"); /*lint !e669*/ /* * Since wallmsg messes with utmpx we need * to guarantee single threadedness... */ (void) pthread_mutex_lock(&wmp); wallmsg(f, from, filtered); (void) pthread_mutex_unlock(&wmp); /* * The contents of filtered have been copied * out to the struct walldev. We should free it here. */ free(filtered); /* exiting the switch */ break; } else if (f->f_type != F_FILE) { /* CSTYLED */ (void) strncpy(eomp, "\r\n", 3); /*lint !e669*/ } else { if ((eomp2 = strchr(filtered, '\r')) != NULL) { (void) strncpy(eomp2, "\n", 2); } else { /* CSTYLED */ (void) strncpy(eomp, "\n", 2); /*lint !e669*/ } } if (write(f->f_file, filtered, strlen(filtered)) < 0) { int e = errno; if ((hup_state & HUP_INPROGRESS) && f->f_type == F_UNUSED) { free(filtered); break; } (void) close(f->f_file); /* * Check for EBADF on TTY's due * to vhangup() XXX */ if (e == EBADF && f->f_type != F_FILE) { f->f_file = open(f->f_un.f_fname, O_WRONLY|O_APPEND|O_NOCTTY); if (f->f_file < 0) { f->f_type = F_UNUSED; logerror(f->f_un.f_fname); f->f_stat.errs++; } untty(); } else { f->f_type = F_UNUSED; f->f_stat.errs++; errno = e; logerror(f->f_un.f_fname); } } else if (flags & SYNC_FILE) if (((pri & LOG_FACMASK) >> 3) == LOG_KERN) (void) fsync(f->f_file); DPRINT2(5, "writemsg(%u): freeing filtered (%p)\n", mythreadno, filtered); free(filtered); break; } } /* * WALLMSG -- Write a message to the world at large * * Write the specified message to either the entire * world, or a list of approved users. */ static void wallmsg(struct filed *f, char *from, char *msg) { int i; size_t len, clen; char *buf = NULL; struct utmpx *utxp; time_t now; char line[512], dev[100]; char cp[MAXLINE+1]; struct stat statbuf; walldev_t *w; char cbuf[30]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (access(UTMPX_FILE, R_OK) != 0 || stat(UTMPX_FILE, &statbuf) != 0) { logerror(UTMPX_FILE); return; } else if (statbuf.st_uid != 0 || (statbuf.st_mode & 07777) != 0644) { (void) sprintf(line, "%s %s", UTMPX_FILE, "not owned by root or not mode 644.\n" "This file must be owned by root " "and not writable by\n" "anyone other than root. This alert is being " "dropped because of\n" "this problem."); logerror(line); return; } if (f->f_type == F_WALL) { (void) time(&now); len = snprintf(line, sizeof (line), "\r\n\7Message from syslogd@%s " "at %.24s ...\r\n", from, ctime_r(&now, cbuf)); len += strlen(msg + 16); buf = (char *)malloc(len + 1); if (!buf) { MALLOC_FAIL("dropping message"); return; } DPRINT3(5, "wallmsg(%u): buf allocated (%p: %d bytes)\n", mythreadno, buf, len + 1); (void) strcpy(buf, line); (void) strcat(buf, msg + 16); clen = copy_frwd(cp, sizeof (cp), buf, len); DPRINT2(5, "wallmsg(%u): clen = %d\n", mythreadno, clen); DPRINT2(5, "wallmsg(%u): freeing buf (%p)\n", mythreadno, buf); free(buf); } else { clen = copy_frwd(cp, sizeof (cp), msg, strlen(msg)); DPRINT2(5, "wallmsg(%u): clen = %d\n", mythreadno, clen); } /* scan the user login file */ setutxent(); while ((utxp = getutxent()) != NULL) { /* is this slot used? */ if (utxp->ut_name[0] == '\0' || utxp->ut_line[0] == '\0' || utxp->ut_type != USER_PROCESS) continue; /* should we send the message to this user? */ if (f->f_type == F_USERS) { for (i = 0; i < MAXUNAMES; i++) { if (!f->f_un.f_uname[i][0]) { i = MAXUNAMES; break; } if (strncmp(f->f_un.f_uname[i], utxp->ut_name, UNAMESZ) == 0) break; } if (i >= MAXUNAMES) continue; } /* compute the device name */ if (utxp->ut_line[0] == '/') { (void) strncpy(dev, utxp->ut_line, UDEVSZ); } else { (void) strcpy(dev, "/dev/"); (void) strncat(dev, utxp->ut_line, UDEVSZ); } DPRINT2(1, "wallmsg(%u): write to '%s'\n", mythreadno, dev); if ((w = malloc(sizeof (walldev_t))) != NULL) { int rc; (void) pthread_attr_init(&w->thread_attr); (void) pthread_attr_setdetachstate(&w->thread_attr, PTHREAD_CREATE_DETACHED); (void) strncpy(w->dev, dev, PATH_MAX); (void) strncpy(w->msg, cp, MAXLINE+1); (void) strncpy(w->ut_name, utxp->ut_name, sizeof (w->ut_name)); if ((rc = pthread_create(&w->thread, &w->thread_attr, writetodev, (void *) w)) != 0) { DPRINT2(5, "wallmsg(%u): wallmsg thread " "create failed rc = %d\n", mythreadno, rc); free(w); break; } } else { MALLOC_FAIL("dropping message to user"); } } /* close the user login file */ endutxent(); } /* * Each time we need to write to a tty device (a potentially expensive * or long-running operation) this routine gets called as a new * detached, unbound thread. This allows writes to many devices * to proceed nearly in parallel, without having to resort to * asynchronous I/O or forking. */ static void * writetodev(void *ap) { walldev_t *w = ap; int ttyf; int len; struct stat statb; struct passwd pw, *pwp; char pwbuf[MAXLINE]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(1, "writetodev(%u): Device writer thread started\n", mythreadno); len = strlen(w->msg); ttyf = open(w->dev, O_WRONLY|O_NOCTTY|O_NDELAY); if (ttyf >= 0) { if (fstat(ttyf, &statb) != 0) { DPRINT2(1, "writetodev(%u): Can't stat '%s'\n", mythreadno, w->dev); errno = 0; logerror("Can't stat '%s'", w->dev); } else if (!(statb.st_mode & S_IWRITE)) { DPRINT2(1, "writetodev(%u): Can't write to " "'%s'\n", mythreadno, w->dev); } else if (!isatty(ttyf)) { DPRINT2(1, "writetodev(%u): '%s' not a tty\n", mythreadno, w->dev); /* * We might hit dtremote here. Don't generate * error message. */ } else if (getpwuid_r(statb.st_uid, &pw, pwbuf, sizeof (pwbuf), &pwp) != 0) { DPRINT2(1, "writetodev(%u): Can't determine owner " "of '%s'\n", mythreadno, w->dev); errno = 0; logerror("Can't determine owner of '%s'", w->dev); } else if (strncmp(pw.pw_name, w->ut_name, UNAMESZ) != 0) { DPRINT2(1, "writetodev(%u): Bad terminal owner '%s'" "\n", mythreadno, w->dev); errno = 0; logerror("%s %s owns '%s' %s %.*s", "Bad terminal owner;", pw.pw_name, w->dev, "but utmpx says", UNAMESZ, w->ut_name); } else if (write(ttyf, w->msg, len) != len) { DPRINT2(1, "writetodev(%u): Write failed to " "'%s'\n", mythreadno, w->dev); errno = 0; logerror("Write failed to '%s'", w->dev); } DPRINT2(1, "writetodev(%u): write to '%s' succeeded\n", mythreadno, w->dev); (void) close(ttyf); } else { DPRINT2(1, "writetodev(%u): Can't open '%s'\n", mythreadno, w->dev); } pthread_attr_destroy(&w->thread_attr); free(w); DPRINT1(1, "writetodev(%u): Device writer thread exiting\n", mythreadno); pthread_exit(0); return (NULL); /*NOTREACHED*/ } /* * Return a printable representation of a host address. If unable to * look up hostname, format the numeric address for display instead. * * First calls hnc_lookup to see if there is valid cache entry for * given network address. If it failed, cvthname looks up hostname, * and push the results into the hostname cache. */ static host_list_t * cvthname(struct netbuf *nbp, struct netconfig *ncp, char *failsafe_addr) { int i; host_list_t *h; struct nd_hostservlist *hsp; struct nd_hostserv *hspp; pthread_t mythreadno; char *uap; if (Debug) { mythreadno = pthread_self(); } if (Debug) uap = taddr2uaddr(ncp, nbp); DPRINT2(2, "cvthname(%u): looking up hostname for %s\n", mythreadno, uap ? uap : ""); if ((h = hnc_lookup(nbp, ncp)) != NULL) { DPRINT4(2, "cvthname(%u): Cache found %p for %s (%s)\n", mythreadno, h, uap ? uap : "", h->hl_hosts[0]); return (h); } DPRINT2(2, "cvthname(%u): No cache found for %s\n", mythreadno, uap ? uap : ""); if (Debug) free(uap); if (ncp->nc_semantics != NC_TPI_CLTS) { return (NULL); } /* memory allocation failure here is fatal */ if ((h = malloc(sizeof (host_list_t))) == NULL) { MALLOC_FAIL("host name conversion"); return (NULL); } if (netdir_getbyaddr(ncp, &hsp, nbp) == 0) { if (hsp->h_cnt <= 0) { out: netdir_free((void *)hsp, ND_HOSTSERVLIST); free(h); return (NULL); } hspp = hsp->h_hostservs; h->hl_cnt = hsp->h_cnt; h->hl_hosts = (char **)malloc(sizeof (char *) * (h->hl_cnt)); if (h->hl_hosts == NULL) { MALLOC_FAIL("host name conversion"); goto out; } DPRINT2(2, "cvthname(%u): Found %d hostnames\n", mythreadno, h->hl_cnt); for (i = 0; i < h->hl_cnt; i++) { h->hl_hosts[i] = (char *) malloc(sizeof (char) * (strlen(hspp->h_host) + 1)); if (h->hl_hosts[i] == NULL) { int j; for (j = 0; j < i; j++) { free(h->hl_hosts[j]); } free(h->hl_hosts); MALLOC_FAIL("host name conversion"); goto out; } (void) strcpy(h->hl_hosts[i], hspp->h_host); hspp++; } netdir_free((void *)hsp, ND_HOSTSERVLIST); } else { /* unknown address */ h->hl_cnt = 1; h->hl_hosts = (char **)malloc(sizeof (char *)); if (h->hl_hosts == NULL) { free(h); MALLOC_FAIL("host name conversion"); return (NULL); } h->hl_hosts[0] = (char *)malloc(strlen(failsafe_addr) + 3); if (h->hl_hosts[0] == NULL) { free(h->hl_hosts); free(h); MALLOC_FAIL("host name conversion"); return (NULL); } (void) sprintf(h->hl_hosts[0], "[%s]", failsafe_addr); DPRINT2(1, "cvthname(%u): Hostname lookup failed " "- using address %s instead\n", mythreadno, h->hl_hosts[0]); } h->hl_refcnt = 1; if (pthread_mutex_init(&h->hl_mutex, NULL) != 0) { logerror("pthread_mutex_init failed"); /* This host_list won't be shared by the cache. */ return (h); } hnc_register(nbp, ncp, h); DPRINT3(2, "cvthname(%u): returning %p for %s\n", mythreadno, h, h->hl_hosts[0]); return (h); } /* * Print syslogd errors some place. Need to be careful here, because * this routine is called at times when we're not initialized and * ready to log messages...in this case, fall back to using the console. */ void logerror(const char *type, ...) { char buf[MAXLINE+1]; pthread_t mythreadno; int flag; va_list ap; if (Debug) { mythreadno = pthread_self(); } va_start(ap, type); logerror_format(type, buf, ap); va_end(ap); DPRINT2(1, "logerror(%u): %s\n", mythreadno, buf); pthread_mutex_lock(&logerror_lock); if (!interrorlog) { flag = 0; if (logerror_to_console(1, buf) == 0) { /* has written to the console */ flag = IGN_CONS; } (void) logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE|flag, 1); } else { if (logmymsg(LOG_SYSLOG|LOG_ERR, buf, ADDDATE, 0) == -1) { (void) logerror_to_console(1, buf); } } pthread_mutex_unlock(&logerror_lock); errno = 0; t_errno = 0; } static void logerror_format(const char *type, char *buf, va_list ap) { char tmpbuf[MAXLINE + 1]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } (void) vsnprintf(tmpbuf, MAXLINE, type, ap); if (t_errno == 0 || t_errno == TSYSERR) { char *errstr; if (errno == 0) { (void) snprintf(buf, MAXLINE, "syslogd: %.*s", MAXLINE, tmpbuf); } else if ((errstr = strerror(errno)) == (char *)NULL) { (void) snprintf(buf, MAXLINE, "syslogd: %s: error" " %d", tmpbuf, errno); } else { (void) snprintf(buf, MAXLINE, "syslogd: %s: %s", tmpbuf, errstr); } } else { if (t_errno > t_nerr) { (void) snprintf(buf, MAXLINE, "syslogd: %s:" " t_error %d", tmpbuf, t_errno); } else { (void) snprintf(buf, MAXLINE, "syslogd: %s: %s", tmpbuf, t_errlist[t_errno]); } } DPRINT2(5, "logerror_format(%u): out %s\n", mythreadno, buf); } static int logerror_to_console(int nonblock, const char *buf) { int cfd, modes; pthread_t mythreadno; int ret = 0, len; char tmpbuf[MAXLINE + 1]; if (Debug) { mythreadno = pthread_self(); } DPRINT2(1, "logerror_to_console(%u): %s\n", mythreadno, buf); /* * must use open here instead of fopen, because * we need the O_NOCTTY behavior - otherwise we * could hang the console at boot time */ modes = (nonblock) ? O_WRONLY|O_APPEND|O_NOCTTY|O_NONBLOCK : O_WRONLY|O_APPEND|O_NOCTTY; if (((cfd = open(sysmsg, modes)) >= 0) || ((cfd = open(ctty, modes)) >= 0)) { (void) snprintf(tmpbuf, MAXLINE, "%s\n", buf); len = strlen(tmpbuf); if (write(cfd, tmpbuf, len) != len) { ret = 1; } (void) close(cfd); } else { ret = 1; /* punt */ DPRINT1(1, "logerror_console(%u): can't open console\n", mythreadno); } return (ret); } /* * copy current message to saved message in filed structure. */ static void copy_msg(struct filed *f) { (void) strlcpy(f->f_prevmsg.msg, f->f_current.msg, MAXLINE+1); (void) strlcpy(f->f_prevmsg.host, f->f_current.host, SYS_NMLN); f->f_prevmsg.pri = f->f_current.pri; f->f_prevmsg.flags = f->f_current.flags; f->f_prevmsg.time = f->f_current.time; f->f_msgflag |= OLD_VALID; } /* * function to free a host_list_t struct that was allocated * out of cvthname(). There is a special case where we don't * free the hostname list in LocalHostName, because that's * our own addresses, and we just want to have to look it * up once and save it. Also don't free it if it's * NullHostName, because that's a special one we use if * name service lookup fails. * * By having hostname cache, now host_list_t will be shared * by messages and hostname cache. hl_refcnt is used for * the purpose. */ static void freehl(host_list_t *h) { int i, refcnt; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT2(2, "freehl(%u): releasing %p\n", mythreadno, h); if (h == NULL || h == &LocalHostName || h == &NullHostName) { return; } pthread_mutex_lock(&h->hl_mutex); refcnt = --h->hl_refcnt; pthread_mutex_unlock(&h->hl_mutex); if (refcnt != 0) { DPRINT3(5, "freehl(%u): %p has reference %d\n", mythreadno, h, refcnt); return; } pthread_mutex_destroy(&h->hl_mutex); DPRINT2(5, "freehl(%u): freeing %p\n", mythreadno, h); for (i = 0; i < h->hl_cnt; i++) { free(h->hl_hosts[i]); } free(h->hl_hosts); free(h); } /* * Create the door file and the pid file in /var/run. If the filesystem * containing /etc is writable, create symlinks /etc/.syslog_door and * /etc/syslog.pid to them. On systems that do not support /var/run, create * /etc/.syslog_door and /etc/syslog.pid directly. * * Note: it is not considered fatal to fail to create the pid file or its * symlink. Attempts to use them in the usual way will fail, of course, but * syslogd will function nicely without it (not so for the door file). */ static void open_door(void) { struct stat buf; door_info_t info; char line[MAXLINE+1]; pthread_t mythreadno; int err; if (Debug) { mythreadno = pthread_self(); } /* * first see if another syslogd is running by trying * a door call - if it succeeds, there is already * a syslogd process active */ if (!DoorCreated) { int door; if ((door = open(DoorFileName, O_RDONLY)) >= 0) { DPRINT2(5, "open_door(%u): %s opened " "successfully\n", mythreadno, DoorFileName); if (door_info(door, &info) >= 0) { DPRINT2(5, "open_door(%u): " "door_info:info.di_target = %ld\n", mythreadno, info.di_target); if (info.di_target > 0) { (void) sprintf(line, "syslogd pid %ld" " already running. Cannot " "start another syslogd pid %ld", info.di_target, getpid()); DPRINT2(5, "open_door(%u): error: " "%s\n", mythreadno, line); errno = 0; logerror(line); exit(1); } } (void) close(door); } else { if (lstat(DoorFileName, &buf) < 0) { err = errno; DPRINT3(5, "open_door(%u): lstat() of %s " "failed, errno=%d\n", mythreadno, DoorFileName, err); if ((door = creat(DoorFileName, 0644)) < 0) { err = errno; (void) sprintf(line, "creat() of %s " "failed - fatal", DoorFileName); DPRINT3(1, "open_door(%u): error: %s, " "errno=%d\n", mythreadno, line, err); errno = err; logerror(line); delete_doorfiles(); exit(1); } (void) fchmod(door, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); DPRINT2(5, "open_door(%u): creat() of %s " "succeeded\n", mythreadno, DoorFileName); (void) close(door); } } if (strcmp(DoorFileName, DOORFILE) == 0) { if (lstat(OLD_DOORFILE, &buf) == 0) { DPRINT2(5, "open_door(%u): lstat() of %s " "succeeded\n", mythreadno, OLD_DOORFILE); if (S_ISDIR(buf.st_mode)) { (void) sprintf(line, "%s is a " "directory - fatal", OLD_DOORFILE); DPRINT2(1, "open_door(%u): error: " "%s\n", mythreadno, line); errno = 0; logerror(line); delete_doorfiles(); exit(1); } DPRINT2(5, "open_door(%u): %s is not a " "directory\n", mythreadno, OLD_DOORFILE); if (unlink(OLD_DOORFILE) < 0) { err = errno; (void) sprintf(line, "unlink() of %s " "failed", OLD_DOORFILE); DPRINT2(5, "open_door(%u): %s\n", mythreadno, line); if (err != EROFS) { DPRINT3(1, "open_door(%u): " "error: %s, " "errno=%d\n", mythreadno, line, err); (void) strcat(line, " - fatal"); errno = err; logerror(line); delete_doorfiles(); exit(1); } DPRINT1(5, "open_door(%u): unlink " "failure OK on RO file " "system\n", mythreadno); } } else { DPRINT2(5, "open_door(%u): file %s doesn't " "exist\n", mythreadno, OLD_DOORFILE); } if (symlink(RELATIVE_DOORFILE, OLD_DOORFILE) < 0) { err = errno; (void) sprintf(line, "symlink %s -> %s " "failed", OLD_DOORFILE, RELATIVE_DOORFILE); DPRINT2(5, "open_door(%u): %s\n", mythreadno, line); if (err != EROFS) { DPRINT3(1, "open_door(%u): error: %s, " "errno=%d\n", mythreadno, line, err); errno = err; (void) strcat(line, " - fatal"); logerror(line); delete_doorfiles(); exit(1); } DPRINT1(5, "open_door(%u): symlink failure OK " "on RO file system\n", mythreadno); } else { DPRINT3(5, "open_door(%u): symlink %s -> %s " "succeeded\n", mythreadno, OLD_DOORFILE, RELATIVE_DOORFILE); } } if ((DoorFd = door_create(server, 0, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) { err = errno; (void) sprintf(line, "door_create() failed - fatal"); DPRINT3(1, "open_door(%u): error: %s, errno=%d\n", mythreadno, line, err); errno = err; logerror(line); delete_doorfiles(); exit(1); } (void) door_setparam(DoorFd, DOOR_PARAM_DATA_MAX, 0); DPRINT2(5, "open_door(%u): door_create() succeeded, " "DoorFd=%d\n", mythreadno, DoorFd); DoorCreated = 1; } (void) fdetach(DoorFileName); /* just in case... */ if (fattach(DoorFd, DoorFileName) < 0) { err = errno; (void) sprintf(line, "fattach() of fd" " %d to %s failed - fatal", DoorFd, DoorFileName); DPRINT3(1, "open_door(%u): error: %s, errno=%d\n", mythreadno, line, err); errno = err; logerror(line); delete_doorfiles(); exit(1); } DPRINT2(5, "open_door(%u): attached server() to %s\n", mythreadno, DoorFileName); /* * create pidfile anyway, so those using it to control * syslogd (with kill `cat /etc/syslog.pid` perhaps) * don't get broken. */ if (!PidfileCreated) { int pidfd; PidfileCreated = 1; if ((pidfd = open(PidFileName, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) { err = errno; (void) sprintf(line, "open() of %s failed", PidFileName); DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n", mythreadno, line, err); errno = err; logerror(line); return; } (void) fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); (void) sprintf(line, "%ld\n", getpid()); if (write(pidfd, line, strlen(line)) < 0) { err = errno; (void) sprintf(line, "write to %s on fd %d failed", PidFileName, pidfd); DPRINT3(1, "open_door(%u): warning: %s, errno=%d\n", mythreadno, line, err); errno = err; logerror(line); return; } (void) close(pidfd); DPRINT2(5, "open_door(%u): %s created\n", mythreadno, PidFileName); if (strcmp(PidFileName, PIDFILE) == 0) { if (lstat(OLD_PIDFILE, &buf) == 0) { DPRINT2(5, "open_door(%u): lstat() of %s " "succeded\n", mythreadno, OLD_PIDFILE); if (S_ISDIR(buf.st_mode)) { (void) sprintf(line, "file %s is a " "directory", OLD_PIDFILE); DPRINT2(1, "open_door(%u): warning: " "%s\n", mythreadno, line); errno = 0; logerror(line); return; } if (unlink(OLD_PIDFILE) < 0) { err = errno; (void) sprintf(line, "unlink() " "of %s failed", OLD_PIDFILE); DPRINT2(5, "open_door(%u): %s\n", mythreadno, line); if (err != EROFS) { DPRINT3(1, "open_door (%u): " "warning: %s, " "errno=%d\n", mythreadno, line, err); errno = err; logerror(line); return; } DPRINT1(5, "open_door(%u): unlink " "failure OK on RO file " "system\n", mythreadno); } } else { DPRINT2(5, "open_door(%u): file %s doesn't " "exist\n", mythreadno, OLD_PIDFILE); } if (symlink(RELATIVE_PIDFILE, OLD_PIDFILE) < 0) { err = errno; (void) sprintf(line, "symlink %s -> %s " "failed", OLD_PIDFILE, RELATIVE_PIDFILE); DPRINT2(5, "open_door(%u): %s\n", mythreadno, line); if (err != EROFS) { DPRINT3(1, "open_door(%u): warning: " "%s, errno=%d\n", mythreadno, line, err); errno = err; logerror(line); return; } DPRINT1(5, "open_door(%u): symlink failure OK " "on RO file system\n", mythreadno); return; } DPRINT3(5, "open_door(%u): symlink %s -> %s " "succeeded\n", mythreadno, OLD_PIDFILE, RELATIVE_PIDFILE); } } } /* * the 'server' function that we export via the door. It does * nothing but return. */ /*ARGSUSED*/ static void server(void *cookie, char *argp, size_t arg_size, door_desc_t *dp, uint_t n) { (void) door_return(NULL, 0, NULL, 0); /* NOTREACHED */ } /* * checkm4 - used to verify that the external utilities that * syslogd depends on are where we expect them to be. * Returns 0 if all utilities are found, > 0 if any are missing. * Also logs errors so user knows what's missing */ static int checkm4(void) { int notfound = 0; int saverrno; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (access("/usr/ccs/bin/m4", X_OK) < 0) { saverrno = errno; logerror("/usr/ccs/bin/m4"); DPRINT2(1, "checkm4(%u): /usr/ccs/bin/m4 - access " "returned %d\n", mythreadno, saverrno); notfound++; } return (notfound); } /* * INIT -- Initialize syslogd from configuration table, start up * input and logger threads. This routine is called only once. */ static void init(void) { struct utsname *up; pthread_attr_t sys_attr, net_attr, log_attr, hnl_attr; int nthread; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(2, "init(%u): initializing\n", mythreadno); /* hand-craft a host_list_t entry for our local host name */ if ((up = malloc(sizeof (struct utsname))) == NULL) { MALLOC_FAIL_EXIT; } (void) uname(up); LocalHostName.hl_cnt = 1; if ((LocalHostName.hl_hosts = malloc(sizeof (char *))) == NULL) { MALLOC_FAIL_EXIT; } if ((LocalHostName.hl_hosts[0] = strdup(up->nodename)) == NULL) { free(LocalHostName.hl_hosts); MALLOC_FAIL_EXIT; } free(up); /* also hand craft one for use if name resolution fails */ NullHostName.hl_cnt = 1; if ((NullHostName.hl_hosts = malloc(sizeof (char *))) == NULL) { MALLOC_FAIL_EXIT; } if ((NullHostName.hl_hosts[0] = strdup("name lookup failed")) == NULL) { MALLOC_FAIL_EXIT; } hnc_init(0); /* * Note that getnets will allocate network resources, but won't be * binding UDP port. This is because, there could be a race * condition between door. If we bind here, one syslogd could grab * UDP port first, but later another syslogd could take over without * getting UDP port but grab the door file. The 2nd syslogd could * continue to run without listening network. * bindnet() will be called after door was successfully opened. */ getnets(); /* * Start up configured theads */ conf_init(); /* * allocate thread stacks for the persistant threads */ nthread = (turnoff == 0) ? 4 : 2; if ((stack_ptr = alloc_stacks(nthread)) == NULL) { logerror("alloc_stacks failed - fatal"); exit(1); } if (Debug) { dumpstats(STDOUT_FILENO); } (void) dataq_init(&inputq); /* init the input queue */ if (pthread_attr_init(&sys_attr) != 0 || pthread_attr_init(&log_attr) != 0 || pthread_attr_init(&net_attr) != 0 || pthread_attr_init(&hnl_attr) != 0) { logerror("pthread_attr_init failed - fatal"); exit(1); } (void) pthread_attr_setscope(&sys_attr, PTHREAD_SCOPE_PROCESS); (void) pthread_attr_setscope(&log_attr, PTHREAD_SCOPE_PROCESS); (void) pthread_attr_setscope(&net_attr, PTHREAD_SCOPE_PROCESS); (void) pthread_attr_setscope(&hnl_attr, PTHREAD_SCOPE_PROCESS); /* 1: logmsg thread */ (void) pthread_attr_setstacksize(&log_attr, stacksize); (void) pthread_attr_setstackaddr(&log_attr, stack_ptr); stack_ptr += stacksize + redzonesize; if (pthread_create(&log_thread, &log_attr, logmsg, NULL) != 0) { logerror("pthread_create failed - fatal"); exit(1); } /* * open the log device, and pull up all pending message * from the log driver. */ prepare_sys_poll(); /* * Now we can deliver the pending internal error messages. */ enable_errorlog(); /* 2: sys_poll thread */ (void) pthread_attr_setstacksize(&sys_attr, stacksize); (void) pthread_attr_setstackaddr(&sys_attr, stack_ptr); stack_ptr += stacksize + redzonesize; if (pthread_create(&sys_thread, &sys_attr, sys_poll, NULL) != 0) { logerror("pthread_create failed - fatal"); exit(1); } /* * We've started the sys_poll() and logmsg() threads. Now we are ready * to open the door. This cannot happen before spawning sys_poll(), * because after opening the door, syslog() will no longer take care of * LOG_CONS. Therefor, we should pull up all pending log messages and * activate sys_poll() before opening the door, so that log driver * won't drop messages. */ open_door(); DPRINT1(1, "init(%u): accepting messages from local system\n", mythreadno); if (turnoff == 0) { /* init the hostname lookup queue */ (void) dataq_init(&hnlq); /* 3: hostname lookup thread */ (void) pthread_attr_setstacksize(&hnl_attr, stacksize); (void) pthread_attr_setstackaddr(&hnl_attr, stack_ptr); stack_ptr += stacksize + redzonesize; if (pthread_create(&hnl_thread, &hnl_attr, hostname_lookup, NULL) != 0) { logerror("pthread_create failed - fatal"); exit(1); } /* 4: net_poll thread */ (void) pthread_attr_setstacksize(&net_attr, stacksize); (void) pthread_attr_setstackaddr(&net_attr, stack_ptr); stack_ptr += stacksize + redzonesize; /* grab UDP port */ bindnet(); if (pthread_create(&net_thread, &net_attr, net_poll, NULL) != 0) { logerror("pthread_create failed - fatal"); exit(1); } DPRINT1(1, "init(%u): accepting messages from remote\n", mythreadno); } (void) pthread_attr_destroy(&sys_attr); (void) pthread_attr_destroy(&net_attr); (void) pthread_attr_destroy(&log_attr); (void) pthread_attr_destroy(&hnl_attr); curalarm = MarkInterval * 60 / MARKCOUNT; (void) alarm((unsigned)curalarm); DPRINT2(2, "init(%u): Next alarm in %d seconds\n", mythreadno, curalarm); DPRINT1(1, "init(%u): syslogd: started\n", mythreadno); } /* * will print a bunch of debugging stats on 'fd' */ static void dumpstats(int fd) { FILE *out; struct filed *f; int i; char users[1024]; char cbuf[30]; char *dashes = "------------------------"; static int conversion_printed; if ((out = fdopen(fd, "w+")) == NULL) return; (void) fprintf(out, "\n syslogd: version %s\n", Version); (void) fprintf(out, " Started: %s", ctime_r(&start_time, cbuf)); (void) fprintf(out, "Input message count: system %d, network %d\n", sys_msg_count, net_msg_count); (void) fprintf(out, "# Outputs: %d\n\n", nlogs); (void) fprintf(out, "%s priority = [file, facility] %s\n\n", dashes, dashes); for (i = 0; i < LOG_NFACILITIES + 1; i++) { (void) fprintf(out, "%d ", i / 10); } (void) fprintf(out, "\n"); for (i = 0; i < LOG_NFACILITIES + 1; i++) { (void) fprintf(out, "%d ", i % 10); } (void) fprintf(out, "\n"); for (i = 0; i < LOG_NFACILITIES + 1; i++) { (void) fprintf(out, "--"); } (void) fprintf(out, "\n"); for (f = Files; f < &Files[nlogs]; f++) { for (i = 0; i < LOG_NFACILITIES + 1; i++) { if (f->f_pmask[i] == NOPRI) (void) fprintf(out, "X "); else (void) fprintf(out, "%d ", f->f_pmask[i]); } (void) fprintf(out, "%s: ", TypeNames[f->f_type]); switch (f->f_type) { case F_FILE: case F_TTY: case F_CONSOLE: (void) fprintf(out, "%s", f->f_un.f_fname); break; case F_FORW: (void) fprintf(out, "%s", f->f_un.f_forw.f_hname); break; case F_USERS: for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++) { if (!i) (void) fprintf(out, "%s", f->f_un.f_uname[i]); else (void) fprintf(out, ", %s", f->f_un.f_uname[i]); } break; } (void) fprintf(out, "\n"); } if (!conversion_printed) { fprintf(out, "\nFacilities:\n"); for (i = 0; FacNames[i].c_val != -1; i++) { fprintf(out, " [%02d] %s: %3d\n", i, FacNames[i].c_name, FacNames[i].c_val); } fprintf(out, "\nPriorities:\n"); for (i = 0; PriNames[i].c_val != -1; i++) { fprintf(out, " [%02d] %s: %3d\n", i, PriNames[i].c_name, PriNames[i].c_val); } conversion_printed = 1; } (void) fprintf(out, "\n\n\n\t\tPer File Statistics\n"); (void) fprintf(out, "%-24s\tTot\tDups\tNofwd\tErrs\n", "File"); (void) fprintf(out, "%-24s\t---\t----\t-----\t----\n", "----"); for (f = Files; f < &Files[nlogs]; f++) { switch (f->f_type) { case F_FILE: case F_TTY: case F_CONSOLE: (void) fprintf(out, "%-24s", f->f_un.f_fname); break; case F_WALL: (void) fprintf(out, "%-24s", TypeNames[f->f_type]); break; case F_FORW: (void) fprintf(out, "%-24s", f->f_un.f_forw.f_hname); break; case F_USERS: for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++) { if (!i) (void) strcpy(users, f->f_un.f_uname[i]); else { (void) strcat(users, ","); (void) strcat(users, f->f_un.f_uname[i]); } } (void) fprintf(out, "%-24s", users); break; } (void) fprintf(out, "\t%d\t%d\t%d\t%d\n", f->f_stat.total, f->f_stat.dups, f->f_stat.cantfwd, f->f_stat.errs); } (void) fprintf(out, "\n\n"); if (Debug && fd == 1) return; (void) fclose(out); } /* * conf_init - This routine is code seperated from the * init routine in order to be re-callable when we get * a SIGHUP signal. */ static void conf_init(void) { char *p; int i; struct filed *f; char *m4argv[4]; int m4argc = 0; conf_t cf; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(2, "conf_init(%u): starting logger threads\n", mythreadno); m4argv[m4argc++] = "m4"; if (amiloghost() == 1) { DPRINT1(1, "conf_init(%u): I am loghost\n", mythreadno); m4argv[m4argc++] = "-DLOGHOST=1"; } m4argv[m4argc++] = ConfFile; m4argv[m4argc] = NULL; /* * Make sure the configuration file and m4 exist, and then parse * the configuration file with m4. If any of these fail, resort * to our hardcoded fallback configuration. */ if (access(ConfFile, R_OK) == -1) { DPRINT2(1, "conf_init(%u): %s does not exist\n", mythreadno, ConfFile); logerror("can't open configuration file"); /* CSTYLED */ Files = (struct filed *) &fallback; /*lint !e545 */ cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]); cfline("*.PANIC\t*", 0, &Files[1]); nlogs = 2; goto nofile; } if (checkm4() != 0 || conf_open(&cf, "/usr/ccs/bin/m4", m4argv) == -1) { DPRINT2(1, "conf_init(%u): cannot open %s\n", mythreadno, ConfFile); /* CSTYLED */ Files = (struct filed *) &fallback; /*lint !e545 */ cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]); cfline("*.PANIC\t*", 0, &Files[1]); nlogs = 2; goto nofile; } /* Count the number of lines which are not blanks or comments */ nlogs = 0; while ((p = conf_read(&cf)) != NULL) { if (p[0] != '\0' && p[0] != '#') nlogs++; } Files = (struct filed *)malloc(sizeof (struct filed) * nlogs); if (!Files) { DPRINT1(1, "conf_init(%u): malloc failed - can't " "allocate 'Files' array\n", mythreadno); MALLOC_FAIL("loading minimum configuration"); /* CSTYLED */ Files = (struct filed *) &fallback; /*lint !e545 */ cfline("*.ERR\t/dev/sysmsg", 0, &Files[0]); cfline("*.PANIC\t*", 0, &Files[1]); nlogs = 2; conf_close(&cf); goto nofile; } /* * Foreach line in the conf table, open that file. */ conf_rewind(&cf); f = Files; i = 0; while (((p = conf_read(&cf)) != NULL) && (f < &Files[nlogs])) { i++; /* check for end-of-section */ if (p[0] == '\0' || p[0] == '#') continue; cfline(p, i, f); if (f->f_type == F_UNUSED) nlogs--; else f++; } conf_close(&cf); /* * See if marks are to be written to any files. If so, set up a * timeout for marks. */ nofile: Marking = 0; /* * allocate thread stacks - one for each logger thread. */ if ((cstack_ptr = alloc_stacks(nlogs)) == NULL) { logerror("alloc_stacks failed - fatal"); exit(1); } /* And now one thread for each configured file */ for (f = Files; f < &Files[nlogs]; f++) { if (filed_init(f) != 0) { logerror("pthread_create failed - fatal"); exit(1); } pthread_mutex_lock(&cft); ++conf_threads; pthread_mutex_unlock(&cft); if (f->f_type != F_UNUSED && f->f_pmask[LOG_NFACILITIES] != NOPRI) Marking = 1; } } /* * filed init - initialize fields in a file descriptor struct * this is called before multiple threads are running, so no mutex * needs to be held at this time. */ static int filed_init(struct filed *f) { pthread_attr_t stack_attr; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (pthread_mutex_init(&f->filed_mutex, NULL) != 0) { logerror("pthread_mutex_init failed"); return (-1); } DPRINT2(5, "filed_init(%u): dataq_init for queue %p\n", mythreadno, &f->f_queue); (void) dataq_init(&f->f_queue); if (pthread_attr_init(&stack_attr) != 0) { logerror("pthread_attr_init failed"); return (-1); } (void) pthread_attr_setstacksize(&stack_attr, stacksize); (void) pthread_attr_setstackaddr(&stack_attr, cstack_ptr); cstack_ptr += stacksize + redzonesize; f->f_msgflag = 0; f->f_prevmsg.msg[0] = '\0'; f->f_prevmsg.flags = 0; f->f_prevmsg.pri = 0; f->f_prevmsg.host[0] = '\0'; f->f_current.msg[0] = '\0'; f->f_current.flags = 0; f->f_current.pri = 0; f->f_current.host[0] = '\0'; f->f_prevcount = 0; f->f_stat.flag = 0; f->f_stat.total = 0; f->f_stat.dups = 0; f->f_stat.cantfwd = 0; f->f_stat.errs = 0; if (pthread_create(&f->f_thread, NULL, logit, (void *)f) != 0) { logerror("pthread_create failed"); pthread_attr_destroy(&stack_attr); return (-1); } pthread_attr_destroy(&stack_attr); return (0); } /* * Crack a configuration file line */ static void cfline(char *line, int lineno, struct filed *f) { char *p; char *q; int i; char *bp; int pri; char buf[MAXLINE]; char ebuf[SYS_NMLN+1+40]; mode_t fmode, omode = O_WRONLY|O_APPEND|O_NOCTTY; struct stat64 sbuf; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT2(1, "cfline(%u): (%s)\n", mythreadno, line); errno = 0; /* keep errno related stuff out of logerror messages */ /* clear out file entry */ bzero((char *)f, sizeof (*f)); for (i = 0; i <= LOG_NFACILITIES; i++) f->f_pmask[i] = NOPRI; /* scan through the list of selectors */ for (p = line; *p && *p != '\t'; ) { /* find the end of this facility name list */ for (q = p; *q && *q != '\t' && *q++ != '.'; ) continue; /* collect priority name */ for (bp = buf; *q && !strchr("\t,;", *q); ) *bp++ = *q++; *bp = '\0'; /* skip cruft */ while (strchr(", ;", *q)) q++; /* decode priority name */ pri = decode(buf, PriNames); if (pri < 0) { logerror("line %d: unknown priority name \"%s\"", lineno, buf); return; } /* scan facilities */ while (*p && !strchr("\t.;", *p)) { for (bp = buf; *p && !strchr("\t,;.", *p); ) *bp++ = *p++; *bp = '\0'; if (*buf == '*') for (i = 0; i < LOG_NFACILITIES; i++) f->f_pmask[i] = (uchar_t)pri; else { i = decode(buf, FacNames); if (i < 0) { logerror("line %d: unknown facility" " name \"%s\"", lineno, buf); return; } f->f_pmask[i >> 3] = (uchar_t)pri; } while (*p == ',' || *p == ' ') p++; } p = q; } /* skip to action part */ while (*p == '\t' || *p == ' ') p++; switch (*p) { case '\0': errno = 0; logerror("line %d: no action part", lineno); break; case '@': (void) strlcpy(f->f_un.f_forw.f_hname, ++p, SYS_NMLN); if (logforward(f, ebuf) != 0) { logerror("line %d: %s", lineno, ebuf); break; } f->f_type = F_FORW; break; case '/': (void) strlcpy(f->f_un.f_fname, p, MAXPATHLEN); if (stat64(p, &sbuf) < 0) { logerror(p); break; } /* * don't block trying to open a pipe * with no reader on the other end */ fmode = 0; /* reset each pass */ if (S_ISFIFO(sbuf.st_mode)) fmode = O_NONBLOCK; f->f_file = open64(p, omode|fmode); if (f->f_file < 0) { if (fmode && errno == ENXIO) { errno = 0; logerror("%s - no reader", p); } else logerror(p); break; } /* * Fifos are initially opened NONBLOCK * to insure we don't hang, but once * we are open, we need to change the * behavior back to blocking, otherwise * we may get write errors, and the log * will get closed down the line. */ if (S_ISFIFO(sbuf.st_mode)) (void) fcntl(f->f_file, F_SETFL, omode); if (isatty(f->f_file)) { f->f_type = F_TTY; untty(); } else f->f_type = F_FILE; if ((strcmp(p, ctty) == 0) || (strcmp(p, sysmsg) == 0)) f->f_type = F_CONSOLE; break; case '*': f->f_type = F_WALL; break; default: for (i = 0; i < MAXUNAMES && *p; i++) { for (q = p; *q && *q != ','; ) q++; (void) strlcpy(f->f_un.f_uname[i], p, UNAMESZ); if ((q - p) > UNAMESZ) f->f_un.f_uname[i][UNAMESZ] = '\0'; else f->f_un.f_uname[i][q - p] = '\0'; while (*q == ',' || *q == ' ') q++; p = q; } f->f_type = F_USERS; break; } f->f_orig_type = f->f_type; } /* * Decode a symbolic name to a numeric value */ static int decode(char *name, struct code *codetab) { struct code *c; char *p; char buf[40]; if (isdigit(*name)) return (atoi(name)); (void) strncpy(buf, name, sizeof (buf) - 1); for (p = buf; *p; p++) if (isupper(*p)) *p = tolower(*p); for (c = codetab; c->c_name; c++) if (!(strcmp(buf, c->c_name))) return (c->c_val); return (-1); } static int ismyaddr(struct netbuf *nbp) { int i; if (nbp == NULL) return (0); for (i = 1; i < Ninputs; i++) { if (same_addr(nbp, Myaddrs[i])) return (1); } return (0); } static void getnets(void) { struct nd_hostserv hs; struct netconfig *ncp; struct nd_addrlist *nap; struct netbuf *nbp; int i, inputs; void *handle; char *uap; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (turnoff) { DPRINT1(1, "getnets(%u): network is being turned off\n", mythreadno); return; } hs.h_host = HOST_SELF; hs.h_serv = "syslog"; if ((handle = setnetconfig()) == NULL) { return; } while ((ncp = getnetconfig(handle)) != NULL) { if (ncp->nc_semantics != NC_TPI_CLTS) { continue; } if (netdir_getbyname(ncp, &hs, &nap) != 0) { continue; } if (nap == NULL || nap->n_cnt <= 0) { DPRINT1(1, "getnets(%u): found no address\n", mythreadno); netdir_free((void *)nap, ND_ADDRLIST); continue; } if (Debug) { DPRINT2(1, "getnets(%u): found %d addresses", mythreadno, nap->n_cnt); DPRINT0(1, ", they are: "); nbp = nap->n_addrs; for (i = 0; i < nap->n_cnt; i++) { if ((uap = taddr2uaddr(ncp, nbp)) != NULL) { DPRINT1(1, "%s ", uap); free(uap); } nbp++; } DPRINT0(1, "\n"); } inputs = Ninputs + nap->n_cnt; Nfd = realloc(Nfd, inputs * sizeof (struct pollfd)); Ncf = realloc(Ncf, inputs * sizeof (struct netconfig)); Myaddrs = realloc(Myaddrs, inputs * sizeof (struct netbuf *)); Udp = realloc(Udp, inputs * sizeof (struct t_unitdata *)); Errp = realloc(Errp, inputs * sizeof (struct t_uderr *)); /* * all malloc failures here are fatal */ if (Nfd == NULL || Ncf == NULL || Myaddrs == NULL || Udp == NULL || Errp == NULL) { MALLOC_FAIL_EXIT; } nbp = nap->n_addrs; for (i = 0; i < nap->n_cnt; i++, nbp++) { char ebuf[128]; if (addnet(ncp, nbp) == 0) { /* no error */ continue; } (void) strcpy(ebuf, "Unable to configure syslog port"); if ((uap = taddr2uaddr(ncp, nbp)) != NULL) { size_t l = strlen(ebuf); (void) snprintf(ebuf + l, sizeof (ebuf) - l, " for %s", uap); } DPRINT2(1, "getnets(%u): %s", mythreadno, ebuf); if (uap) { free(uap); } logerror(ebuf); /* * Here maybe syslogd can quit. However, syslogd * has been ignoring this error and keep running. * So we won't break it. */ } netdir_free((void *)nap, ND_ADDRLIST); } (void) endnetconfig(handle); } /* * Open the network device, and allocate necessary resources. * Myaddrs will also be filled, so that we can call ismyaddr() before * being bound to the network. */ static int addnet(struct netconfig *ncp, struct netbuf *nbp) { int fd; struct netbuf *bp; fd = t_open(ncp->nc_device, O_RDWR, NULL); if (fd < 0) { return (1); } (void) memcpy(&Ncf[Ninputs], ncp, sizeof (struct netconfig)); /*LINTED*/ Udp[Ninputs] = (struct t_unitdata *)t_alloc(fd, T_UNITDATA, T_ADDR); if (Udp[Ninputs] == NULL) { t_close(fd); return (1); } /*LINTED*/ Errp[Ninputs] = (struct t_uderr *)t_alloc(fd, T_UDERROR, T_ADDR); if (Errp[Ninputs] == NULL) { t_close(fd); t_free((char *)Udp[Ninputs], T_UNITDATA); return (1); } if ((bp = malloc(sizeof (struct netbuf))) == NULL || (bp->buf = malloc(nbp->len)) == NULL) { MALLOC_FAIL("allocating address buffer"); t_close(fd); t_free((char *)Udp[Ninputs], T_UNITDATA); t_free((char *)Errp[Ninputs], T_UDERROR); if (bp) { free(bp); } return (1); } bp->len = nbp->len; (void) memcpy(bp->buf, nbp->buf, nbp->len); Myaddrs[Ninputs] = bp; Nfd[Ninputs].fd = fd; Nfd[Ninputs].events = POLLIN; Ninputs++; return (0); } /* * Allocate UDP buffer to minimize packet loss. */ static void set_udp_buffer(int fd) { struct t_optmgmt req, resp; struct opthdr *opt; size_t optsize, bsize = 256 * 1024; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } optsize = sizeof (struct opthdr) + sizeof (int); if ((opt = malloc(optsize)) == NULL) { MALLOC_FAIL("will have no udp buffer"); return; } opt->level = SOL_SOCKET; opt->name = SO_RCVBUF; opt->len = sizeof (int); *(int *)(opt + 1) = bsize; req.flags = T_NEGOTIATE; req.opt.len = optsize; req.opt.buf = (char *)opt; resp.flags = 0; resp.opt.maxlen = optsize; resp.opt.buf = (char *)opt; while (t_optmgmt(fd, &req, &resp) == -1 || resp.flags != T_SUCCESS) { if (t_errno != TSYSERR || errno != ENOBUFS) { bsize = 0; break; } bsize >>= 1; if (bsize < 8192) { break; } *(int *)(opt + 1) = bsize; } if (bsize == 0) { logerror("failed to allocate UDP buffer"); } DPRINT3(1, "set_udp_buffer(%u): allocate %d for fd %d\n", mythreadno, bsize, fd); free(opt); } /* * Attach the network, and allocate UDP buffer for the interface. */ static void bindnet(void) { struct t_bind bind, *bound; int cnt, i; char *uap; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } cnt = 0; while (cnt < Ninputs) { char ebuf[128]; /*LINTED*/ bound = (struct t_bind *)t_alloc(Nfd[cnt].fd, T_BIND, T_ADDR); bind.addr = *Myaddrs[cnt]; bind.qlen = 0; if (t_bind(Nfd[cnt].fd, &bind, bound) == 0) { if (same_addr(&bind.addr, &bound->addr)) { t_free((char *)bound, T_BIND); set_udp_buffer(Nfd[cnt].fd); cnt++; continue; } } /* failed to bind port */ t_free((char *)bound, T_BIND); (void) strcpy(ebuf, "Unable to bind syslog port"); uap = taddr2uaddr(&Ncf[cnt], Myaddrs[cnt]); if (uap) { i = strlen(ebuf); (void) snprintf(ebuf + i, sizeof (ebuf) - i, " for %s", uap); } DPRINT2(1, "bindnet(%u): failed to bind port (%s)\n", mythreadno, uap ? uap : ""); if (uap) { free(uap); } errno = 0; logerror(ebuf); t_close(Nfd[cnt].fd); free(Myaddrs[cnt]->buf); free(Myaddrs[cnt]); t_free((char *)Udp[cnt], T_UNITDATA); t_free((char *)Errp[cnt], T_UDERROR); for (i = cnt; i < (Ninputs-1); i++) { Nfd[i] = Nfd[i + 1]; Ncf[i] = Ncf[i + 1]; Myaddrs[i] = Myaddrs[i + 1]; Udp[i] = Udp[i + 1]; Errp[i] = Errp[i + 1]; } Ninputs--; } } static int logforward(struct filed *f, char *ebuf) { struct nd_hostserv hs; struct netbuf *nbp; struct netconfig *ncp; struct nd_addrlist *nap; void *handle; char *hp; hp = f->f_un.f_forw.f_hname; hs.h_host = hp; hs.h_serv = "syslog"; if ((handle = setnetconfig()) == NULL) { (void) strcpy(ebuf, "unable to rewind the netconfig database"); errno = 0; return (-1); } nap = (struct nd_addrlist *)NULL; while ((ncp = getnetconfig(handle)) != NULL) { if (ncp->nc_semantics == NC_TPI_CLTS) { if (netdir_getbyname(ncp, &hs, &nap) == 0) { if (!nap) continue; nbp = nap->n_addrs; break; } } } if (ncp == NULL) { endnetconfig(handle); (void) sprintf(ebuf, "WARNING: %s could not be resolved", hp); errno = 0; return (-1); } if (nap == (struct nd_addrlist *)NULL) { endnetconfig(handle); (void) sprintf(ebuf, "unknown host %s", hp); errno = 0; return (-1); } /* CSTYLED */ if (ismyaddr(nbp)) { /*lint !e644 */ netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); (void) sprintf(ebuf, "host %s is this host - logging loop", hp); errno = 0; return (-1); } f->f_un.f_forw.f_addr.buf = malloc(nbp->len); if (f->f_un.f_forw.f_addr.buf == NULL) { netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); (void) strcpy(ebuf, "malloc failed"); return (-1); } bcopy(nbp->buf, f->f_un.f_forw.f_addr.buf, nbp->len); f->f_un.f_forw.f_addr.len = nbp->len; f->f_file = t_open(ncp->nc_device, O_RDWR, NULL); if (f->f_file < 0) { netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); free(f->f_un.f_forw.f_addr.buf); (void) strcpy(ebuf, "t_open"); return (-1); } netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); if (t_bind(f->f_file, NULL, NULL) < 0) { (void) strcpy(ebuf, "t_bind"); free(f->f_un.f_forw.f_addr.buf); t_close(f->f_file); return (-1); } return (0); } static int amiloghost(void) { struct nd_hostserv hs; struct netconfig *ncp; struct nd_addrlist *nap; struct netbuf *nbp; int i, fd; void *handle; char *uap; struct t_bind bind, *bound; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } /* * we need to know if we are running on the loghost. This is * checked by binding to the address associated with "loghost" * and "syslogd" service over the connectionless transport */ hs.h_host = "loghost"; hs.h_serv = "syslog"; if ((handle = setnetconfig()) == NULL) { return (0); } while ((ncp = getnetconfig(handle)) != NULL) { if (ncp->nc_semantics != NC_TPI_CLTS) { continue; } if (netdir_getbyname(ncp, &hs, &nap) != 0) { continue; } if (nap == NULL) { continue; } nbp = nap->n_addrs; for (i = 0; i < nap->n_cnt; i++) { if ((uap = taddr2uaddr(ncp, nbp)) != (char *)NULL) { DPRINT2(1, "amiloghost(%u): testing %s\n", mythreadno, uap); } free(uap); fd = t_open(ncp->nc_device, O_RDWR, NULL); if (fd < 0) { netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); return (0); } /*LINTED*/ bound = (struct t_bind *)t_alloc(fd, T_BIND, T_ADDR); bind.addr = *nbp; bind.qlen = 0; if (t_bind(fd, &bind, bound) == 0) { t_close(fd); t_free((char *)bound, T_BIND); netdir_free((void *)nap, ND_ADDRLIST); endnetconfig(handle); return (1); } else { t_close(fd); t_free((char *)bound, T_BIND); } nbp++; } netdir_free((void *)nap, ND_ADDRLIST); } endnetconfig(handle); return (0); } int same_addr(struct netbuf *na, struct netbuf *nb) { char *a, *b; size_t n; assert(a != NULL && b != NULL); if (na->len != nb->len) { return (0); } a = na->buf; b = nb->buf; n = nb->len; while (n-- > 0) { if (*a++ != *b++) { return (0); } } return (1); } /* * allocates a new message structure, initializes it * and returns a pointer to it */ static log_message_t * new_msg(void) { log_message_t *lm; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if ((lm = malloc(sizeof (log_message_t))) == NULL) return ((log_message_t *)NULL); _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*lm)) if (pthread_mutex_init(&lm->msg_mutex, NULL) != 0) return ((log_message_t *)NULL); lm->refcnt = 0; lm->pri = 0; lm->flags = 0; lm->hlp = NULL; lm->msg[0] = '\0'; lm->ptr = NULL; DPRINT2(3, "new_msg(%u): creating msg %p\n", mythreadno, lm); return (lm); } /* * frees a message structure - should only be called if * the refcount is 0 */ static void free_msg(log_message_t *lm) { pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } assert(lm != NULL && lm->refcnt == 0); if (lm->hlp != NULL) freehl(lm->hlp); DPRINT2(3, "free_msg(%u): freeing msg %p\n", mythreadno, lm); free(lm); } /* * Make sure that the message makes sense in the current locale, and * does not contain stray control characters. */ static void filter_string(char *mbstr, char *filtered, size_t max) { size_t cs = 0; size_t mb_cur_max; unsigned char *p = (unsigned char *)mbstr; pthread_t mythreadno = 0; if (Debug) { mythreadno = pthread_self(); } assert(mbstr != NULL && filtered != NULL); /* * Since the access to MB_CUR_MAX is expensive (because * MB_CUR_MAX lives in a global area), it should be * restrained for the better performance. */ mb_cur_max = (size_t)MB_CUR_MAX; if (mb_cur_max > 1) { /* multibyte locale */ int mlen; wchar_t wc; while (*p != '\0') { if ((mlen = mbtowc(&wc, (char *)p, mb_cur_max)) == -1) { /* * Invalid byte sequence found. * * try to print one byte * in ASCII format. */ DPRINT2(9, "filter_string(%u): Invalid " "MB sequence: %d\n", mythreadno, wc); if (!putctrlc(*p++, &filtered, &cs, max)) { /* not enough buffer */ goto end; } else { continue; } } else { /* * Since *p is not a null byte here, * mbtowc should have never returned 0. * * A valid wide character found. */ if (wc != L'\t' && iswcntrl(wc)) { /* * non-tab, non-newline, and * control character found. * * try to print this wide character * in ASCII-format. */ char *q = filtered; DPRINT2(9, "filter_string(%u): MB" " control character: %d\n", mythreadno, wc); while (mlen--) { if (!putctrlc(*p++, &filtered, &cs, max)) { /* * not enough buffer in * filtered * * cancel already * stored bytes in * filtered for this * wide character. */ filtered = q; goto end; } } continue; } else { /* * tab, newline, or non-control * character found. */ if (cs + mlen < max) { /* enough buffer */ cs += mlen; while (mlen--) { *filtered++ = *p++; } continue; } else { /* not enough buffer */ goto end; } } } } } else { /* singlebyte locale */ while (*p != '\0') { if (*p != '\t' && iscntrl(*p)) { /* * non-tab, non-newline, * and control character found. * * try to print this singlebyte character * in ASCII format. */ DPRINT2(9, "filter_string(%u): control " "character: %d\n", mythreadno, *p); if (!putctrlc(*p++, &filtered, &cs, max)) { /* not enough buffer */ goto end; } else { continue; } } else if (*p != '\t' && !isprint(*p)) { /* * non-tab and non printable character found * this check is required for the C locale */ DPRINT2(9, "filter_string(%u): non-printable " "character: %d\n", mythreadno, *p); if (!putctrlc(*p++, &filtered, &cs, max)) { /* not enough buffer */ goto end; } else { continue; } } else { /* * tab, newline, non-control character, or * printable found. */ if (cs + 1 < max) { *filtered++ = *p++; cs++; continue; } else { /* not enough buffer */ goto end; } } } } end: *filtered = '\0'; if (cs >= 2 && filtered[-2] == '\\' && filtered[-1] == 'n') { filtered[-2] = '\0'; } } static char * alloc_stacks(int numstacks) { size_t pagesize, mapsize; char *stack_top; char *addr; int i; pagesize = (size_t)sysconf(_SC_PAGESIZE); /* * stacksize and redzonesize are global so threads * can be created elsewhere and refer to the sizes */ stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) + DEFAULT_STACKSIZE, pagesize); redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize); /* * allocate an additional "redzonesize" chunk in addition * to what we require, so we can create a redzone at the * bottom of the last stack as well. */ mapsize = redzonesize + numstacks * (stacksize + redzonesize); stack_top = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (stack_top == MAP_FAILED) return (NULL); addr = stack_top; /* * this loop is intentionally <= instead of <, so we can * protect the redzone at the bottom of the last stack */ for (i = 0; i <= numstacks; i++) { (void) mprotect(addr, redzonesize, PROT_NONE); addr += stacksize + redzonesize; } return ((char *)(stack_top + redzonesize)); } static void dealloc_stacks(int numstacks) { size_t pagesize, mapsize; pagesize = (size_t)sysconf(_SC_PAGESIZE); stacksize = (size_t)roundup(sysconf(_SC_THREAD_STACK_MIN) + DEFAULT_STACKSIZE, pagesize); redzonesize = (size_t)roundup(DEFAULT_REDZONESIZE, pagesize); mapsize = redzonesize + numstacks * (stacksize + redzonesize); (void) munmap(cstack_ptr - mapsize, mapsize); } static void filed_destroy(struct filed *f) { (void) dataq_destroy(&f->f_queue); pthread_mutex_destroy(&f->filed_mutex); } static void close_door(void) { pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } (void) fdetach(DoorFileName); DPRINT2(5, "close_door(%u): detached server() from %s\n", mythreadno, DoorFileName); } static void delete_doorfiles(void) { pthread_t mythreadno; struct stat sb; int err; char line[MAXLINE+1]; if (Debug) { mythreadno = pthread_self(); } if (lstat(DoorFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) { if (unlink(DoorFileName) < 0) { err = errno; (void) sprintf(line, "unlink() of %s failed - fatal", DoorFileName); errno = err; logerror(line); DPRINT3(1, "delete_doorfiles(%u): error: %s, " "errno=%d\n", mythreadno, line, err); exit(1); } DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno, DoorFileName); } if (strcmp(DoorFileName, DOORFILE) == 0) { if (lstat(OLD_DOORFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) { if (unlink(OLD_DOORFILE) < 0) { err = errno; (void) sprintf(line, "unlink() of %s " "failed", OLD_DOORFILE); DPRINT2(5, "delete_doorfiles(%u): %s\n", mythreadno, line); if (err != EROFS) { errno = err; (void) strcat(line, " - fatal"); logerror(line); DPRINT3(1, "delete_doorfiles(%u): " "error: %s, errno=%d\n", mythreadno, line, err); exit(1); } DPRINT1(5, "delete_doorfiles(%u): unlink() " "failure OK on RO file system\n", mythreadno); } DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno, OLD_DOORFILE); } } if (lstat(PidFileName, &sb) == 0 && !S_ISDIR(sb.st_mode)) { if (unlink(PidFileName) < 0) { err = errno; (void) sprintf(line, "unlink() of %s failed" " - fatal", PidFileName); errno = err; logerror(line); DPRINT3(1, "delete_doorfiles(%u): error: %s, " "errno=%d\n", mythreadno, line, err); exit(1); } DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno, PidFileName); } if (strcmp(PidFileName, PIDFILE) == 0) { if (lstat(OLD_PIDFILE, &sb) == 0 && !S_ISDIR(sb.st_mode)) { if (unlink(OLD_PIDFILE) < 0) { err = errno; (void) sprintf(line, "unlink() of %s failed", OLD_PIDFILE); DPRINT2(5, "delete_doorfiles(%u): %s, \n", mythreadno, line); if (err != EROFS) { errno = err; (void) strcat(line, " - fatal"); logerror(line); DPRINT3(1, "delete_doorfiles(%u): " "error: %s, errno=%d\n", mythreadno, line, err); exit(1); } DPRINT1(5, "delete_doorfiles(%u): unlink " "failure OK on RO file system\n", mythreadno); } DPRINT2(5, "delete_doorfiles(%u): deleted %s\n", mythreadno, OLD_PIDFILE); } } if (DoorFd != -1) { (void) door_revoke(DoorFd); } DPRINT2(1, "delete_doorfiles(%u): revoked door: DoorFd=%d\n", mythreadno, DoorFd); } /*ARGSUSED*/ static void signull(int sig, siginfo_t *sip, void *utp) { DPRINT1(1, "signull(%u): THIS CALL SHOULD NEVER HAPPEN\n", pthread_self()); /* * Do nothing, as this is a place-holder used in conjunction with * sigaction()/sigwait() to ensure that the proper disposition is * given to the signals we handle in main(). */ } /* * putctrlc returns zero, if failed due to not enough buffer. * Otherwise, putctrlc returns non-zero. * * c: a byte to print in ASCII format * **buf: a pointer to the pointer to the output buffer. * *cl: current length of characters in the output buffer * max: maximum length of the buffer */ static int putctrlc(int c, char **buf, size_t *cl, size_t max) { char *p = *buf; if (c == '\n') { if (*cl + 2 < max) { *p++ = '\\'; *p++ = 'n'; *cl += 2; *buf = p; return (2); } else { return (0); } } else if (c < 0200) { /* ascii control character */ if (*cl + 2 < max) { *p++ = '^'; *p++ = c ^ 0100; *cl += 2; *buf = p; return (2); } else { return (0); } } else { if (*cl + 4 < max) { *p++ = '\\'; *p++ = ((c >> 6) & 07) + '0'; *p++ = ((c >> 3) & 07) + '0'; *p++ = (c & 07) + '0'; *cl += 4; *buf = p; return (4); } else { return (0); } } } /* * findnl_bkwd: * Scans each character in buf until it finds the last newline in buf, * or the scanned character becomes the last COMPLETE character in buf. * Returns the number of scanned bytes. * * buf - pointer to a buffer containing the message string * len - the length of the buffer */ size_t findnl_bkwd(const char *buf, const size_t len) { const char *p; size_t mb_cur_max; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (len == 0) { return (0); } mb_cur_max = MB_CUR_MAX; if (mb_cur_max == 1) { /* single-byte locale */ for (p = buf + len - 1; p != buf; p--) { if (*p == '\n') { return ((size_t)(p - buf)); } } return ((size_t)len); } else { /* multi-byte locale */ int mlen; const char *nl; size_t rem; p = buf; nl = NULL; for (rem = len; rem >= mb_cur_max; ) { mlen = mblen(p, mb_cur_max); if (mlen == -1) { /* * Invalid character found. */ DPRINT1(9, "findnl_bkwd(%u): Invalid MB " "sequence\n", mythreadno); /* * handle as a single byte character. */ p++; rem--; } else { /* * It's guaranteed that *p points to * the 1st byte of a multibyte character. */ if (*p == '\n') { nl = p; } p += mlen; rem -= mlen; } } if (nl) { return ((size_t)(nl - buf)); } /* * no newline nor null byte found. * Also it's guaranteed that *p points to * the 1st byte of a (multibyte) character * at this point. */ return (len - rem); } } /* * copynl_frwd: * Scans each character in buf and copies the scanned character to obuf * until it finds a null byte or a newline, or * the number of the remaining bytes in obuf gets to exceed obuflen * if copying the scanned character to obuf. * Returns the number of scanned bytes. * * obuf - buffer to be copied the scanned character * obuflen - the size of obuf * buf - pointer to a buffer containing the message string * len - the length of the buffer */ size_t copynl_frwd(char *obuf, const size_t obuflen, const char *buf, const size_t len) { const char *p; char *q = obuf; size_t olen = 0; size_t mb_cur_max; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (len == 0) { return (0); } mb_cur_max = MB_CUR_MAX; if (mb_cur_max == 1) { /* single-byte locale */ for (p = buf; *p; ) { if (obuflen > olen + 1) { if (*p != '\n') { *q++ = *p++; olen++; } else { *q = '\0'; return ((size_t)(p - buf)); } } else { *q = '\0'; return ((size_t)(p - buf)); } } *q = '\0'; return ((size_t)(p - buf)); } else { /* multi-byte locale */ int mlen; for (p = buf; *p; ) { mlen = mblen(p, mb_cur_max); if (mlen == -1) { /* * Invalid character found. */ DPRINT1(9, "copynl_frwd(%u): Invalid MB " "sequence\n", mythreadno); /* * handle as a single byte character. */ if (obuflen > olen + 1) { *q++ = *p++; olen++; } else { *q = '\0'; return ((size_t)(p - buf)); } } else { /* * It's guaranteed that *p points to * the 1st byte of a multibyte character. */ if (*p == '\n') { *q = '\0'; return ((size_t)(p - buf)); } if (obuflen > olen + mlen) { int n; for (n = 0; n < mlen; n++) { *q++ = *p++; } olen += mlen; } else { *q = '\0'; return ((size_t)(p - buf)); } } } /* * no newline nor null byte found. * Also it's guaranteed that *p points to * the 1st byte of a (multibyte) character * at this point. */ *q = '\0'; return ((size_t)(p - buf)); } } /* * copy_frwd: * Scans each character in buf and copies the scanned character to obuf * until the number of the remaining bytes in obuf gets to exceed obuflen * if copying the scanned character to obuf. * Returns the number of scanned (copied) bytes. * * obuf - buffer to be copied the scanned character * obuflen - the size of obuf * buf - pointer to a buffer containing the message string * len - the length of the buffer */ size_t copy_frwd(char *obuf, const size_t obuflen, const char *buf, const size_t len) { const char *p; char *q = obuf; size_t olen = 0; size_t mb_cur_max; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (len == 0) { return (0); } mb_cur_max = MB_CUR_MAX; if (mb_cur_max == 1) { /* single-byte locale */ if (obuflen > len) { (void) memcpy(obuf, buf, len); obuf[len] = '\0'; return ((size_t)len); } else { (void) memcpy(obuf, buf, obuflen - 1); obuf[obuflen - 1] = '\0'; return (obuflen - 1); } } else { /* multi-byte locale */ int mlen; for (p = buf; *p; ) { mlen = mblen(p, mb_cur_max); if (mlen == -1) { /* * Invalid character found. */ DPRINT1(9, "copy_frwd(%u): Invalid MB " "sequence\n", mythreadno); /* * handle as a single byte character. */ if (obuflen > olen + 1) { *q++ = *p++; olen++; } else { *q = '\0'; return ((size_t)(p - buf)); } } else { if (obuflen > olen + mlen) { int n; for (n = 0; n < mlen; n++) { *q++ = *p++; } olen += mlen; } else { *q = '\0'; return ((size_t)(p - buf)); } } } *q = '\0'; return ((size_t)(p - buf)); } } /* * defaults: * Read defaults from file. */ static void defaults(void) { int flags; char *ptr; if (defopen(DflFile) == 0) { /* * ignore case */ flags = defcntl(DC_GETFLAGS, 0); TURNOFF(flags, DC_CASE); defcntl(DC_SETFLAGS, flags); if ((ptr = defread("LOG_FROM_REMOTE=")) != NULL) { turnoff = strcasecmp(ptr, "NO") == 0; } (void) defopen((char *)NULL); } } /* * close all the input devices. */ static void shutdown_input(void) { int cnt; shutting_down = 1; for (cnt = 0; cnt < Ninputs; cnt++) { (void) t_close(Nfd[cnt].fd); } (void) close(Pfd.fd); } /* * This is for the one thread that dedicates to resolve the * hostname. This will get the messages from net_poll() through * hnlq, and resolve the hostname, and push the messages back * into the inputq. */ /*ARGSUSED*/ static void * hostname_lookup(void *ap) { char *uap; log_message_t *mp; host_info_t *hip; char failsafe_addr[SYS_NMLN + 1]; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT1(1, "hostname_lookup(%u): hostname_lookup started\n", mythreadno); for (;;) { (void) dataq_dequeue(&hnlq, (void **)&mp, 0); DPRINT3(5, "hostname_lookup(%u): dequeued msg %p" " from queue %p\n", mythreadno, mp, &hnlq); hip = (host_info_t *)mp->ptr; if ((uap = taddr2uaddr(hip->ncp, &hip->addr)) != NULL) { (void) strlcpy(failsafe_addr, uap, SYS_NMLN); free(uap); } else { (void) strlcpy(failsafe_addr, "", SYS_NMLN); } mp->hlp = cvthname(&hip->addr, hip->ncp, failsafe_addr); if (mp->hlp == NULL) { mp->hlp = &NullHostName; } free(hip->addr.buf); free(hip); mp->ptr = NULL; if (dataq_enqueue(&inputq, (void *)mp) == -1) { MALLOC_FAIL("dropping message from remote"); free_msg(mp); continue; } DPRINT3(5, "hostname_lookup(%u): enqueued msg %p on queue %p\n", mythreadno, mp, &inputq); } /*NOTREACHED*/ return (NULL); } /* * Does all HUP(re-configuration) process. */ static void reconfigure() { int cnt, loop, drops; int really_stuck; int console_stuck = 0; struct filed *f; char buf[LINE_MAX]; struct utsname up; char cbuf[30]; time_t tim; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } /* If we get here then we must need to regen */ flushmsg(0); if (logmymsg(LOG_SYSLOG|LOG_INFO, "syslogd: configuration restart", ADDDATE, 0) == -1) { MALLOC_FAIL("dropping message"); } /* * make sure the logmsg thread is not in the waiting state. * Otherwise, changing hup_state will prevent the logmsg thread * getting out from the waiting loop. */ if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: awaiting logmsg()" " moving to the safe place\n", mythreadno, ctime_r(&tim, cbuf)+4); } for (loop = 0; loop < LOOP_MAX; loop++) { /* we don't need the mutex to read */ if (hup_state == HUP_ACCEPTABLE) break; (void) sleep(1); } if (hup_state != HUP_ACCEPTABLE) { goto thread_stuck; } if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: logmsg() will accept HUP\n", mythreadno, ctime_r(&tim, cbuf)+4); } /* * Prevent logging until we are truly done processing the HUP */ (void) pthread_mutex_lock(&hup_lock); hup_state = HUP_INPROGRESS; (void) pthread_mutex_unlock(&hup_lock); /* * We will be going into a critical state. Any error message * from syslogd needs to be dumped to the console by default * immediately. Also, those error messages are quened in a temporary * queue to be able to post into the regular stream later. */ disable_errorlog(); if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: sending SHUTDOWN\n", mythreadno, ctime_r(&tim, cbuf)+4); } /* stop configured threads */ if (shutdown_msg() == -1) { /* * No memory, message will be dumped to the console. */ MALLOC_FAIL("unable to restart syslogd"); goto out; } /* make sure logmsg() is in suspended state */ for (loop = 0; loop < LOOP_INTERVAL; loop++) { if (hup_state & HUP_LOGMSG_SUSPENDED) break; (void) sleep(1); } if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) { if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: logmsg() does not " "stop. enforcing\n", mythreadno, ctime_r(&tim, cbuf)+4); } /* probably we have too long input queue, or really stuck */ (void) pthread_mutex_lock(&hup_lock); hup_state |= HUP_SUSP_LOGMSG_REQD; (void) pthread_mutex_unlock(&hup_lock); for (loop = 0; loop < LOOP_MAX; loop++) { if (hup_state & HUP_LOGMSG_SUSPENDED) break; (void) sleep(1); } if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) { if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: logmsg()" " does not stop. give up\n", mythreadno, ctime_r(&tim, cbuf)+4); } logerror("could not suspend logmsg - fatal"); goto thread_stuck; } } if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: logmsg() suspended\n", mythreadno, ctime_r(&tim, cbuf)+4); } /* * Will wait for LOOP_MAX secs with watching queue lengths for the * each logger threads. If they have backlogs, and no change in the * length of queue found in 30 seconds, those will be counted as * "really stuck". * If all running logger threads become "really stuck" state, there * should be no worth waiting for them to quit. * In that case, we will go ahead and close out file descriptors to * have them pull out from hanging system call, and give them a last * chance(LOOP_INTERVAL sec) to quit. */ if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: awaiting logit() to be" " shutdown\n", mythreadno, ctime_r(&tim, cbuf)+4); } cnt = 0; really_stuck = 0; while (cnt < (LOOP_MAX/LOOP_INTERVAL) && conf_threads > really_stuck) { /* save initial queue count */ for (f = Files; f < &Files[nlogs]; f++) { f->f_prev_queue_count = (f->f_type == F_UNUSED) ? -1 : f->f_queue_count; } for (loop = 0; loop < LOOP_INTERVAL; loop++) { if (conf_threads == 0) break; (void) sleep(1); } if (conf_threads == 0) break; if (Debug) { tim = time(NULL); DPRINT3(3, "reconfigure(%u): %.15s: " "%d threads are still alive.\n", mythreadno, ctime_r(&tim, cbuf)+4, conf_threads); } really_stuck = 0; for (f = Files; f < &Files[nlogs]; f++) { if (f->f_type == F_UNUSED) { f->f_prev_queue_count = -1; continue; } if (f->f_prev_queue_count == f->f_queue_count) { really_stuck++; f->f_prev_queue_count = 1; DPRINT2(3, "reconfigure(%u): " "tid=%d is really stuck.\n", mythreadno, f->f_thread); } else { f->f_prev_queue_count = 0; DPRINT2(3, "reconfigure(%u): " "tid=%d is still active.\n", mythreadno, f->f_thread); } } /* * Here we have one of following values in the * f_prev_queue_count: * 0: logger thread is still actively working. * 1: logger thread is really stuck. * -1: logger thread has already died. */ cnt++; } if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s:" " complete awaiting logit()\n", mythreadno, ctime_r(&tim, cbuf)+4); DPRINT3(3, "reconfigure(%u): %d threads alive." " %d threads stuck\n", mythreadno, conf_threads, really_stuck); } /* * Still running? If so, mark it as UNUSED, and close * the fd so that logger threads can bail out from the loop. */ drops = 0; if (conf_threads) { for (f = Files; f < &Files[nlogs]; f++) { if (f->f_type == F_CONSOLE && f->f_prev_queue_count == 1) { /* console is really stuck */ console_stuck = 1; } if (f->f_type == F_USERS || f->f_type == F_WALL || f->f_type == F_UNUSED) continue; cnt = f->f_queue_count; drops += (cnt > 0) ? cnt - 1: 0; f->f_type = F_UNUSED; if (f->f_orig_type == F_FORW) t_close(f->f_file); else close(f->f_file); } if (Debug) { tim = time(NULL); DPRINT1(3, "reconfigure(%u): terminating logit()\n", mythreadno); } /* last chance to exit */ for (loop = 0; loop < LOOP_MAX; loop++) { if (conf_threads == 0) break; (void) sleep(1); } if (Debug) { tim = time(NULL); DPRINT3(3, "reconfigure(%u): %.15s: %d alive\n", mythreadno, ctime_r(&tim, cbuf)+4, conf_threads); } } if (conf_threads == 0 && drops) { errno = 0; logerror("Could not completely output pending messages" " while preparing re-configuration"); logerror("discarded %d messages and restart configuration.", drops); if (Debug) { tim = time(NULL); DPRINT3(3, "reconfigure(%u): %.15s: " "discarded %d messages\n", mythreadno, ctime_r(&tim, cbuf)+4, drops); } } /* * If all threads still haven't exited * something is stuck or hosed. We just * have no option but to exit. */ if (conf_threads) { thread_stuck: if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: really stuck\n", mythreadno, ctime_r(&tim, cbuf)+4); } shutdown_input(); delete_doorfiles(); uname(&up); (void) sprintf(buf, "syslogd(%s): some logger thread(s) " "are stuck%s; syslogd is shutting down.", up.nodename, console_stuck ? " (including the console)" : ""); if (console_stuck) { FILE *m = popen(MAILCMD, "w"); if (m != NULL) { fprintf(m, "%s\n", buf); pclose(m); } } disable_errorlog(); logerror(buf); exit(1); } /* Free up some resources */ if (Files != (struct filed *)&fallback) { for (f = Files; f < &Files[nlogs]; f++) { (void) pthread_join(f->f_thread, NULL); filed_destroy(f); } free(Files); } dealloc_stacks(nlogs); if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: cleanup complete\n", mythreadno, ctime_r(&tim, cbuf)+4); } hnc_init(1); /* purge hostname cache */ conf_init(); /* start reconfigure */ out:; /* Now should be ready to dispatch error messages from syslogd. */ enable_errorlog(); /* Wake up the log thread */ if (Debug) { tim = time(NULL); DPRINT2(3, "reconfigure(%u): %.15s: resuming logmsg()\n", mythreadno, ctime_r(&tim, cbuf)+4); } (void) pthread_mutex_lock(&hup_lock); hup_state = HUP_COMPLETED; (void) pthread_cond_signal(&hup_done); (void) pthread_mutex_unlock(&hup_lock); } /* * The following function implements simple hostname cache mechanism. * Host name cache consists of single linked list structure which contains * host_list_t and netbuf pair. All cache entries(hnc_size) are allocated * initially and linked to "hnc_freeq". If cache register request comes, * then one element will be pulled from freeq, and will be linked to * "hnc_active" with given netbuf, host_list_t and expiration time. All valid * cahces are linked from hnc_active. If the cache element has run * out, most unused element will be re-used for the new request. * * hnc_init(): * allocate and initialize the cache. If reinit is set, * invalidate all cache entries. * hnc_look(): * lookup the cache entries by following single linked list * from hnc_active. If cached entry was found, it will be * put in the head of the list, and return. While going through * the entries, an entry which has already expired will be invalidated. * hnc_register(): * take one element from freeq, and put the new entry at the top * of hnc_active. * hnc_unreg(): * invalidate the cache. i.e unlink from hnc_active, and linked the * element to freeq. */ static void hnc_init(int reinit) { struct hostname_cache **hpp; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (reinit) { pthread_mutex_lock(&hnc_mutex); for (hpp = &hnc_active; *hpp != NULL; ) { hnc_unreg(hpp); } pthread_mutex_unlock(&hnc_mutex); DPRINT1(2, "hnc_init(%u): hostname cache re-configured\n", mythreadno); } else { int i; hnc_cache = malloc(hnc_size * sizeof (struct hostname_cache)); if (hnc_cache == NULL) { MALLOC_FAIL("hostname cache"); logerror("hostname cache disabled"); return; } for (i = 0; i < hnc_size; i++) { hnc_cache[i].h = NULL; hnc_cache[i].next = hnc_cache + i + 1; } hnc_cache[hnc_size - 1].next = NULL; hnc_freeq = hnc_cache; hnc_active = NULL; DPRINT3(1, "hnc_init(%u): hostname cache configured %d entry" " ttl:%d\n", mythreadno, hnc_size, hnc_ttl); } } static host_list_t * hnc_lookup(struct netbuf *nbp, struct netconfig *ncp) { struct hostname_cache **hpp, *hp; time_t now; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (hnc_cache == NULL) { return (NULL); } pthread_mutex_lock(&hnc_mutex); now = time(0); for (hpp = &hnc_active; (hp = *hpp) != NULL; ) { DPRINT4(10, "hnc_lookup(%u): check %p on %p for %s\n", mythreadno, hp->h, hp, hp->h->hl_hosts[0]); if (hp->expire < now) { DPRINT2(9, "hnc_lookup(%u): purge %p\n", mythreadno, hp); /* Note: hnc_unreg changes *hpp */ hnc_unreg(hpp); continue; } if (ncp == hp->ncp && same_addr(&hp->addr, nbp)) { /* * found! * Put the entry at the top. */ if (hp != hnc_active) { /* unlink from active list */ *hpp = (*hpp)->next; /* push it onto the top */ hp->next = hnc_active; hnc_active = hp; } pthread_mutex_lock(&hp->h->hl_mutex); hp->h->hl_refcnt++; pthread_mutex_unlock(&hp->h->hl_mutex); DPRINT4(9, "hnc_lookup(%u): found %p on %p for %s\n", mythreadno, hp->h, hp, hp->h->hl_hosts[0]); pthread_mutex_unlock(&hnc_mutex); return (hp->h); } hpp = &hp->next; } pthread_mutex_unlock(&hnc_mutex); return (NULL); } static void hnc_register(struct netbuf *nbp, struct netconfig *ncp, host_list_t *h) { struct hostname_cache **hpp, **tailp, *hp; void *addrbuf; time_t now; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } if (hnc_cache == NULL) { return; } if ((addrbuf = malloc(nbp->len)) == NULL) { MALLOC_FAIL("pushing hostname cache"); return; } pthread_mutex_lock(&hnc_mutex); if (hnc_freeq == NULL) { DPRINT1(9, "hnc_register(%u): freeq empty\n", mythreadno); now = time(NULL); /* * first go through active list, and discard the * caches which has been invalid. */ for (hpp = &hnc_active; (hp = *hpp) != NULL; ) { tailp = hpp; if (hp->expire < now) { DPRINT2(9, "hnc_register(%u): discard %p\n", mythreadno, hp); hnc_unreg(hpp); } else { hpp = &hp->next; } } if (hnc_freeq == NULL) { DPRINT2(9, "hnc_register(%u): stealing %p\n", mythreadno, *tailp); /* * If still no inactive cache, then steal the least * active element. */ hnc_unreg(tailp); } } hp = hnc_freeq; hnc_freeq = hnc_freeq->next; /* push it on the top */ hp->next = hnc_active; hnc_active = hp; (void) memcpy(addrbuf, nbp->buf, nbp->len); hp->addr.len = nbp->len; hp->addr.buf = addrbuf; hp->ncp = ncp; hp->h = h; hp->expire = time(NULL) + hnc_ttl; /* * As far as cache is valid, corresponding host_list must * also be valid. Increments the refcnt to avoid freeing * host_list. */ h->hl_refcnt++; DPRINT4(9, "hnc_register(%u): reg %p onto %p for %s\n", mythreadno, h, hp, hp->h->hl_hosts[0]); pthread_mutex_unlock(&hnc_mutex); } static void hnc_unreg(struct hostname_cache **hpp) { struct hostname_cache *hp = *hpp; pthread_t mythreadno; if (Debug) { mythreadno = pthread_self(); } DPRINT4(9, "hnc_unreg(%u): unreg %p on %p for %s\n", mythreadno, hp->h, hp, hp->h->hl_hosts[0]); free(hp->addr.buf); freehl(hp->h); /* unlink from active list */ *hpp = (*hpp)->next; /* put in freeq */ hp->next = hnc_freeq; hnc_freeq = hp; } /* * Once this is called, error messages through logerror() will go to * the console immediately. Also, messages are queued into the tmpq * to be able to later put them into inputq. */ static void disable_errorlog() { dataq_init(&tmpq); pthread_mutex_lock(&logerror_lock); interrorlog = 0; pthread_mutex_unlock(&logerror_lock); } /* * Turn internal error messages to regular input stream. * All pending messages are pulled and pushed into the regular * input queue. */ static void enable_errorlog() { log_message_t *mp; pthread_mutex_lock(&logerror_lock); interrorlog = 1; pthread_mutex_unlock(&logerror_lock); /* * push all the pending messages into inputq. */ while (dataq_dequeue(&tmpq, (void **)&mp, 1) == 0) { (void) dataq_enqueue(&inputq, mp); } dataq_destroy(&tmpq); }