syslogd.c revision ace1a5f11236a072fca1b5e0ea1416a083a9f2aa
/*
* 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
* 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 "<n>" 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 <unistd.h>
#include <note.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>
#include <string.h>
#include <strings.h>
#include <deflt.h>
#include <netconfig.h>
#include <netdir.h>
#include <pwd.h>
#include <tiuser.h>
#include <utmpx.h>
#include <limits.h>
#include <pthread.h>
#include <fcntl.h>
#include <stropts.h>
#include <assert.h>
#include <sys/sysmacros.h>
#include <sys/resource.h>
#include <door.h>
#include <wchar.h>
#include <locale.h>
#include <stdarg.h>
#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 *ConfFile = "/etc/syslog.conf";
static int DoorFd = -1;
static int DoorCreated = 0;
static int PidfileCreated = 0;
static char *DoorFileName = DOORFILE;
static char *PidFileName = PIDFILE;
/*
* configuration file directives
*/
"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
};
"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
*/
static int conf_threads = 0;
static int hup_state = HUP_ACCEPTABLE;
static char *stack_ptr; /* ptr to allocated stacks */
static char *cstack_ptr; /* ptr to conf_thr stacks */
static time_t start_time;
static int nlogs;
static int Debug; /* debug flag */
static int interrorlog; /* internal error logging */
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 t_unitdata **Udp;
static int turnoff = 0;
static int shutting_down;
static unsigned int hnc_ttl = DEF_HNC_TTL;
#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!)
*/
/*
* Interval(sec) to check the status of output queue while processing
* HUP signal.
*/
#define LOOP_INTERVAL (15)
int
{
int i;
char *pstr;
char *debugstr;
int mcount = 0;
pthread_t mythreadno = 0;
char cbuf [30];
#ifdef DEBUG
DEBUGDIR);
#endif /* DEBUG */
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);
}
defaults();
switch (i) {
case 'f': /* configuration file */
break;
case 'd': /* debug */
Debug++;
break;
case 'p': /* path */
break;
case 'm': /* mark interval */
"Illegal interval\n");
usage();
}
}
"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();
}
}
usage();
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.
*/
/*
* ensure that file descriptor limit is "high enough"
*/
logerror("Unable to increase file descriptor limit.");
/* block all signals from all threads initially */
(void) sigfillset(&allsigs);
init(); /* read configuration, start threads */
/* now set up to catch signals we care about */
(void) sigemptyset(&sigs);
/*
* 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.
*/
/* we now turn into the signal handling thread */
for (;;) {
switch (sig) {
case SIGALRM:
"dropping MARK message");
}
mcount = 0;
}
break;
case SIGHUP:
"reconfiguring\n", mythreadno);
reconfigure();
break;
case SIGQUIT:
case SIGINT:
if (!Debug) {
/* allow these signals if debugging */
break;
}
/* FALLTHROUGH */
case SIGTERM:
mythreadno, sig);
(void) alarm(0);
flushmsg(0);
errno = 0;
t_errno = 0;
disable_errorlog(); /* force msg to console */
(void) shutdown_msg(); /* stop threads */
close_door();
return (0);
break;
case SIGUSR1: /* secret debug dump mode */
/* if in debug mode, use stdout */
if (Debug) {
break;
}
/* otherwise dump to a debug file */
0644)) < 0)
break;
break;
default:
mythreadno, sig);
break;
}
}
}
/*
* Attempts to open the local log device
* and return a file descriptor.
*/
static int
{
int fd;
if (Debug) {
mythreadno = pthread_self();
}
return (-1);
}
logerror("cannot register to log console messages");
return (-1);
}
return (fd);
}
/*
* Open the log device, and pull up all pending messages.
*/
static void
{
logerror("can't open kernel log device - fatal");
exit(1);
}
for (;;) {
if (nfds <= 0) {
if (sys_init_msg_count > 0)
break;
}
getkmsg(0);
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 *
{
int nfds;
static int klogerrs = 0;
if (Debug) {
mythreadno = pthread_self();
}
/*
* 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;
if (nfds == 0)
continue;
if (nfds < 0) {
logerror("poll");
continue;
}
} else {
if (shutting_down) {
pthread_exit(0);
}
logerror("kernel log driver poll error");
}
}
}
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
{
int flags = 0, i;
char *lastline;
if (Debug) {
mythreadno = pthread_self();
}
*lastline = '\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) {
} else {
}
/* If anything remains in buf */
/* skip newline */
len++;
}
/*
* Move the remaining bytes to
* the beginnning of buf.
*/
} else {
}
}
/*
* 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) {
} else {
}
if (!shutting_down) {
logerror("kernel log driver read error");
}
}
}
/*
* 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 *
{
int nfds, i;
int flags = 0;
struct t_unitdata *udp;
char *uap;
if (Debug) {
mythreadno = pthread_self();
}
for (;;) {
errno = 0;
t_errno = 0;
if (nfds == 0)
continue;
if (nfds < 0) {
logerror("poll");
continue;
}
if (shutting_down) {
pthread_exit(0);
}
logerror("POLLNVAL|POLLHUP|POLLERR");
nfds--;
}
continue;
}
flags = 0;
if (!shutting_down) {
logerror("t_rcvuderr");
}
}
} else {
if (!shutting_down) {
logerror("t_rcvudata");
}
}
nfds--;
if (shutting_down) {
pthread_exit(0);
}
continue;
}
nfds--;
if (Debug) {
}
" received empty packet"
" from %s\n", mythreadno,
if (uap)
}
continue; /* No data */
}
/*
* 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.
*/
"exceeds max line size\n", mythreadno);
continue;
}
MALLOC_FAIL("dropping message from "
"remote");
continue;
}
if (Debug) {
" from %s\n", mythreadno,
}
MALLOC_FAIL("dropping message from "
"remote");
if (hinfo) {
}
continue;
}
MALLOC_FAIL("dropping message from "
"remote");
continue;
}
}
}
/*NOTREACHED*/
return (NULL);
}
static void
usage(void)
{
"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
{
if (Debug) {
mythreadno = pthread_self();
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* Generate an internal shutdown message
*/
static int
shutdown_msg(void)
{
if (Debug) {
mythreadno = pthread_self();
}
return (-1);
}
return (-1);
}
return (0);
}
/*
* Generate an internal flush message
*/
static void
{
if (Debug) {
mythreadno = pthread_self();
}
MALLOC_FAIL("dropping flush msg");
return;
}
MALLOC_FAIL("dropping flush msg");
return;
}
}
/*
* Do some processing on messages received from the net
*/
static void
{
char *p;
int pri;
if (Debug) {
mythreadno = pthread_self();
}
/* test for special codes */
p);
pri = 0;
while (isdigit(*++p))
if (*p == '>')
++p;
}
}
/*
* Do some processing on messages generated by this host
* and then enqueue the log message.
*/
static void
{
char *p, *q;
char cbuf[30];
if (Debug) {
mythreadno = pthread_self();
}
msg);
/* msglen includes the null termination */
for (p = msg; *p != '\0'; ) {
/*
* Allocate a log_message_t structure.
* We should do it here since a single message (msg)
* could be composed of many lines.
*/
MALLOC_FAIL("dropping message");
/*
* Should bail out from the loop.
*/
break;
}
/* extract facility */
} else {
}
mythreadno, len);
p += len;
if (*p == '\n') {
/* skip newline */
p++;
}
if (len != 0) {
MALLOC_FAIL("dropping message");
break;
}
} else
}
}
/*
* Log a message to the appropriate log files, users, etc. based on
* the priority.
*/
/*ARGSUSED*/
static void *
{
struct filed *f;
int fake_shutdown, skip_shutdown;
if (Debug) {
mythreadno = pthread_self();
}
fake_shutdown = skip_shutdown = 0;
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.
*/
} else {
}
/*
* 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) &&
/* create a fake SHUTDOWN msg */
MALLOC_FAIL("dropping message");
(void) logerror_to_console(1,
"unable to shutdown "
"logger thread");
}
continue;
}
fake_shutdown = 1;
}
/*
* is it a shutdown or flush message ?
*/
!fake_shutdown && skip_shutdown > 0) {
"arrived SHUTDOWN. pending %d\n",
continue;
}
continue;
}
f->f_queue_count++;
if (dataq_enqueue(&f->f_queue,
(void *)mp) == -1) {
f->f_queue_count--;
MALLOC_FAIL("dropping message");
(void) logerror_to_console(1,
"unable to shutdown "
"logger thread");
}
continue;
}
&f->f_queue);
}
/*
* flags value needs to be saved because mp may
* have been freed before SHUTDOWN test below.
*/
if (refcnt == 0)
while (hup_state != HUP_COMPLETED) {
(void) pthread_cond_wait(&hup_done,
&hup_lock);
}
fake_shutdown = 0;
}
continue;
}
/*
* Check to see if msg looks non-standard.
*/
/* extract facility and priority level */
/*
* 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.
*/
/* skip messages that are incorrect priority */
continue;
if (f->f_queue_count > Q_HIGHWATER_MARK) {
"%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.
*/
continue;
}
f->f_queue_count++;
f->f_queue_count--;
MALLOC_FAIL("dropping message");
continue;
}
}
if (refcnt == 0)
}
/*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 *
{
int forwardingloop = 0;
char *errmsg = "logit(%u): %s to %s forwarding loop detected\n";
"%p\n",
assert(f->f_queue_count > 0);
f->f_queue_count--;
/*
* is it a shutdown message ?
*/
if (refcnt == 0)
break;
}
/*
* Is it a logsync message?
*/
goto out; /* nothing to do */
if (f->f_file < 0) {
}
goto out;
}
/*
* If the message flags include both flush and sync,
* then just sync the file out to disk if appropriate.
*/
"for filed %p\n", f->f_thread,
f);
}
goto out;
}
/*
* Otherwise if it's a standard flush message, write
* out any saved messages to the file.
*/
set_flush_msg(f);
goto out;
}
SYS_NMLN);
f->f_msgflag &= ~CURRENT_VALID;
/*
* Should not forward MARK messages, as they are
* not defined outside of the current system.
*/
"Mark\n", f->f_thread);
goto out;
}
/*
* can not forward message if we do
* not have a host to forward to
*/
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;
forwardingloop = 1;
break;
}
}
if (forwardingloop == 1) {
goto out;
}
}
f->f_msgflag |= CURRENT_VALID;
/* check for dup message */
/* a dup */
if (currofst == 16) {
}
f->f_prevcount++;
f->f_msgflag &= ~CURRENT_VALID;
} else {
/* new: mark or prior dups exist */
set_flush_msg(f);
}
}
if (f->f_msgflag & CURRENT_VALID)
copy_msg(f);
"mark - %p)\n",
} else {
"message - %p\n",
}
} else { /* new message */
copy_msg(f);
}
}
/*
* 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:
if (refcnt == 0)
}
/* register our exit */
/*
* Pull out all pending messages, if they exist.
*/
while (f->f_queue_count > 0) {
"%p\n",
if (refcnt == 0)
f->f_queue_count--;
}
else
}
/*
* Since f_type may have been changed before this point, we need
* to test orig_type.
*/
if (f->f_orig_type == F_FORW) {
}
--conf_threads;
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];
if (f->f_prevcount == 1)
else
f->f_prevcount = 0;
}
/*
* 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
{
char *cp, *p;
int pri;
int flags;
int l;
struct t_unitdata ud;
char cbuf[30];
char *filtered;
char *msgid_start, *msgid_end;
if (Debug) {
mythreadno = pthread_self();
}
switch (selection) {
default:
case CURRENT: /* print current message */
f->f_msgflag &= ~CURRENT_VALID;
break;
case SAVED: /* print saved message */
break;
}
if (msg[0] == '\0')
return;
else
else
errno = 0;
t_errno = 0;
switch (f->f_type) {
case F_UNUSED:
break;
case F_FORW:
p = text;
while (l > 0) {
p, l);
mythreadno, tmpbuf);
len);
if ((hup_state & HUP_INPROGRESS) &&
break;
}
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:
/*
* 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.
*/
if (!filtered) {
MALLOC_FAIL("dropping message");
/* seems we can just return */
return;
}
"filtered allocated (%p: %d bytes)\n",
/* -3 : we may add "\r\n" to ecomp(filtered) later */
/*
* If we're writing to the console, strip out the message ID
* to reduce visual clutter.
*/
/* CSTYLED */
/*
* Since wallmsg messes with utmpx we need
* to guarantee single threadedness...
*/
(void) pthread_mutex_lock(&wmp);
(void) pthread_mutex_unlock(&wmp);
/*
* The contents of filtered have been copied
* out to the struct walldev. We should free it here.
*/
/* exiting the switch */
break;
/* CSTYLED */
} else {
} else {
/* CSTYLED */
}
}
int e = errno;
if ((hup_state & HUP_INPROGRESS) &&
break;
}
/*
* Check for EBADF on TTY's due
* to vhangup() XXX
*/
if (f->f_file < 0) {
}
untty();
} else {
errno = e;
}
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
{
int i;
walldev_t *w;
char cbuf[30];
if (Debug) {
mythreadno = pthread_self();
}
return;
"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.");
return;
}
"\r\n\7Message from syslogd@%s "
if (!buf) {
MALLOC_FAIL("dropping message");
return;
}
mythreadno, clen);
mythreadno, buf);
} else {
mythreadno, clen);
}
/* scan the user login file */
setutxent();
/* is this slot used? */
continue;
/* should we send the message to this user? */
for (i = 0; i < MAXUNAMES; i++) {
i = MAXUNAMES;
break;
}
break;
}
if (i >= MAXUNAMES)
continue;
}
/* compute the device name */
} else {
}
dev);
int rc;
(void) pthread_attr_init(&w->thread_attr);
(void) pthread_attr_setdetachstate(&w->thread_attr,
sizeof (w->ut_name));
writetodev, (void *) w)) != 0) {
"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)
{
int ttyf;
int len;
if (Debug) {
mythreadno = pthread_self();
}
if (ttyf >= 0) {
mythreadno, w->dev);
errno = 0;
mythreadno, w->dev);
/*
* We might hit dtremote here. Don't generate
* error message.
*/
errno = 0;
errno = 0;
logerror("%s %s owns '%s' %s %.*s",
errno = 0;
}
mythreadno, w->dev);
} else {
mythreadno, w->dev);
}
free(w);
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 *
{
int i;
host_list_t *h;
struct nd_hostservlist *hsp;
struct nd_hostserv *hspp;
char *uap;
if (Debug) {
mythreadno = pthread_self();
}
if (Debug)
h->hl_hosts[0]);
return (h);
}
if (Debug)
return (NULL);
}
/* memory allocation failure here is fatal */
MALLOC_FAIL("host name conversion");
return (NULL);
}
free(h);
return (NULL);
}
MALLOC_FAIL("host name conversion");
goto out;
}
mythreadno, h->hl_cnt);
for (i = 0; i < h->hl_cnt; i++) {
h->hl_hosts[i] = (char *)
int j;
for (j = 0; j < i; j++) {
}
MALLOC_FAIL("host name conversion");
goto out;
}
hspp++;
}
} else { /* unknown address */
h->hl_cnt = 1;
free(h);
MALLOC_FAIL("host name conversion");
return (NULL);
}
free(h);
MALLOC_FAIL("host name conversion");
return (NULL);
}
"- using address %s instead\n",
mythreadno, h->hl_hosts[0]);
}
h->hl_refcnt = 1;
logerror("pthread_mutex_init failed");
/* This host_list won't be shared by the cache. */
return (h);
}
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
{
int flag;
if (Debug) {
mythreadno = pthread_self();
}
if (!interrorlog) {
flag = 0;
/* has written to the console */
}
} else {
}
}
errno = 0;
t_errno = 0;
}
static void
{
if (Debug) {
mythreadno = pthread_self();
}
char *errstr;
if (errno == 0) {
} else {
}
} else {
} else {
}
}
}
static int
{
if (Debug) {
mythreadno = pthread_self();
}
/*
* must use open here instead of fopen, because
* we need the O_NOCTTY behavior - otherwise we
* could hang the console at boot time
*/
ret = 1;
}
} else {
ret = 1;
/* punt */
}
return (ret);
}
/*
* copy current message to saved message in filed structure.
*/
static void
{
}
/*
* 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;
if (Debug) {
mythreadno = pthread_self();
}
return;
}
pthread_mutex_lock(&h->hl_mutex);
pthread_mutex_unlock(&h->hl_mutex);
if (refcnt != 0) {
mythreadno, h, refcnt);
return;
}
for (i = 0; i < h->hl_cnt; i++) {
}
free(h);
}
/*
* 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)
{
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;
"door_info:info.di_target = %ld\n",
" already running. Cannot "
"start another syslogd pid %ld",
errno = 0;
exit(1);
}
}
} else {
"failed, errno=%d\n",
"failed - fatal", DoorFileName);
err);
exit(1);
}
"succeeded\n", mythreadno,
}
}
"succeeded\n", mythreadno,
"directory - fatal",
errno = 0;
exit(1);
}
"directory\n",
if (unlink(OLD_DOORFILE) < 0) {
"failed", OLD_DOORFILE);
mythreadno, line);
"error: %s, "
"errno=%d\n",
exit(1);
}
"failure OK on RO file "
"system\n", mythreadno);
}
} else {
}
"failed", OLD_DOORFILE,
line);
err);
exit(1);
}
"on RO file system\n", mythreadno);
} else {
"succeeded\n", mythreadno,
}
}
DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) < 0) {
exit(1);
}
DoorCreated = 1;
}
" %d to %s failed - fatal",
exit(1);
}
/*
* 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;
< 0) {
return;
}
PidFileName, pidfd);
return;
}
"directory",
errno = 0;
return;
}
if (unlink(OLD_PIDFILE) < 0) {
"of %s failed", OLD_PIDFILE);
mythreadno, line);
"warning: %s, "
"errno=%d\n",
return;
}
"failure OK on RO file "
"system\n", mythreadno);
}
} else {
}
"failed", OLD_PIDFILE,
line);
"%s, errno=%d\n", mythreadno,
return;
}
"on RO file system\n", mythreadno);
return;
}
}
}
}
/*
* the 'server' function that we export via the door. It does
* nothing but return.
*/
/*ARGSUSED*/
static void
{
/* 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;
if (Debug) {
mythreadno = pthread_self();
}
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)
{
int nthread;
if (Debug) {
mythreadno = pthread_self();
}
/* hand-craft a host_list_t entry for our local host name */
}
}
}
/* also hand craft one for use if name resolution fails */
}
}
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
*/
logerror("alloc_stacks failed - fatal");
exit(1);
}
if (Debug) {
}
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);
}
/* 1: logmsg thread */
logerror("pthread_create failed - fatal");
exit(1);
}
/*
* open the log device, and pull up all pending message
* from the log driver.
*/
/*
* Now we can deliver the pending internal error messages.
*/
/* 2: sys_poll thread */
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();
if (turnoff == 0) {
/* init the hostname lookup queue */
(void) dataq_init(&hnlq);
/* 3: hostname lookup thread */
hostname_lookup, NULL) != 0) {
logerror("pthread_create failed - fatal");
exit(1);
}
/* 4: net_poll thread */
/* grab UDP port */
bindnet();
NULL) != 0) {
logerror("pthread_create failed - fatal");
exit(1);
}
}
(void) pthread_attr_destroy(&sys_attr);
(void) pthread_attr_destroy(&net_attr);
(void) pthread_attr_destroy(&log_attr);
(void) pthread_attr_destroy(&hnl_attr);
}
/*
* will print a bunch of debugging stats on 'fd'
*/
static void
{
struct filed *f;
int i;
char users[1024];
char cbuf[30];
char *dashes = "------------------------";
static int conversion_printed;
return;
for (i = 0; i < LOG_NFACILITIES + 1; i++) {
}
for (i = 0; i < LOG_NFACILITIES + 1; i++) {
}
for (i = 0; i < LOG_NFACILITIES + 1; i++) {
}
for (i = 0; i < LOG_NFACILITIES + 1; i++) {
else
f->f_pmask[i]);
}
switch (f->f_type) {
case F_FILE:
case F_TTY:
case F_CONSOLE:
break;
case F_FORW:
break;
case F_USERS:
for (i = 0; i < MAXUNAMES &&
if (!i)
else
}
break;
}
}
if (!conversion_printed) {
}
}
conversion_printed = 1;
}
switch (f->f_type) {
case F_FILE:
case F_TTY:
case F_CONSOLE:
break;
case F_WALL:
break;
case F_FORW:
break;
case F_USERS:
for (i = 0; i < MAXUNAMES &&
if (!i)
else {
}
}
break;
}
}
return;
}
/*
* 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;
if (Debug) {
mythreadno = pthread_self();
}
if (amiloghost() == 1) {
}
/*
* 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.
*/
ConfFile);
logerror("can't open configuration file");
/* CSTYLED */
nlogs = 2;
goto nofile;
}
ConfFile);
/* CSTYLED */
nlogs = 2;
goto nofile;
}
/* Count the number of lines which are not blanks or comments */
nlogs = 0;
if (p[0] != '\0' && p[0] != '#')
nlogs++;
}
if (!Files) {
"allocate 'Files' array\n", mythreadno);
MALLOC_FAIL("loading minimum configuration");
/* CSTYLED */
nlogs = 2;
conf_close(&cf);
goto nofile;
}
/*
* Foreach line in the conf table, open that file.
*/
conf_rewind(&cf);
f = Files;
i = 0;
i++;
/* check for end-of-section */
if (p[0] == '\0' || p[0] == '#')
continue;
cfline(p, i, f);
nlogs--;
else
f++;
}
conf_close(&cf);
/*
* See if marks are to be written to any files. If so, set up a
* timeout for marks.
*/
Marking = 0;
/*
* allocate thread stacks - one for each logger thread.
*/
logerror("alloc_stacks failed - fatal");
exit(1);
}
/* And now one thread for each configured file */
if (filed_init(f) != 0) {
logerror("pthread_create failed - fatal");
exit(1);
}
++conf_threads;
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)
{
if (Debug) {
mythreadno = pthread_self();
}
logerror("pthread_mutex_init failed");
return (-1);
}
mythreadno, &f->f_queue);
(void) dataq_init(&f->f_queue);
if (pthread_attr_init(&stack_attr) != 0) {
logerror("pthread_attr_init failed");
return (-1);
}
f->f_msgflag = 0;
f->f_prevcount = 0;
logerror("pthread_create failed");
return (-1);
}
return (0);
}
/*
* Crack a configuration file line
*/
static void
{
char *p;
char *q;
int i;
char *bp;
int pri;
if (Debug) {
mythreadno = pthread_self();
}
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++)
/* 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 */
*bp++ = *q++;
*bp = '\0';
/* skip cruft */
while (strchr(", ;", *q))
q++;
/* decode priority name */
if (pri < 0) {
logerror("line %d: unknown priority name \"%s\"",
return;
}
/* scan facilities */
while (*p && !strchr("\t.;", *p)) {
*bp++ = *p++;
*bp = '\0';
if (*buf == '*')
for (i = 0; i < LOG_NFACILITIES; i++)
else {
if (i < 0) {
logerror("line %d: unknown facility"
return;
}
}
while (*p == ',' || *p == ' ')
p++;
}
p = q;
}
/* skip to action part */
while (*p == '\t' || *p == ' ')
p++;
switch (*p) {
case '\0':
errno = 0;
break;
case '@':
if (logforward(f, ebuf) != 0) {
break;
}
break;
case '/':
logerror(p);
break;
}
/*
* don't block trying to open a pipe
* with no reader on the other end
*/
fmode = 0; /* reset each pass */
fmode = O_NONBLOCK;
if (f->f_file < 0) {
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.
*/
untty();
} else
break;
case '*':
break;
default:
for (i = 0; i < MAXUNAMES && *p; i++) {
for (q = p; *q && *q != ','; )
q++;
if ((q - p) > UNAMESZ)
else
while (*q == ',' || *q == ' ')
q++;
p = q;
}
break;
}
f->f_orig_type = f->f_type;
}
/*
* Decode a symbolic name to a numeric value
*/
static int
{
struct code *c;
char *p;
char buf[40];
for (p = buf; *p; p++)
if (isupper(*p))
*p = tolower(*p);
return (c->c_val);
return (-1);
}
static int
{
int i;
return (0);
for (i = 1; i < Ninputs; i++) {
return (1);
}
return (0);
}
static void
getnets(void)
{
struct nd_hostserv hs;
struct nd_addrlist *nap;
int i, inputs;
void *handle;
char *uap;
if (Debug) {
mythreadno = pthread_self();
}
if (turnoff) {
return;
}
return;
}
continue;
}
continue;
}
continue;
}
if (Debug) {
}
nbp++;
}
}
/*
* all malloc failures here are fatal
*/
}
char ebuf[128];
/* no error */
continue;
}
" for %s", uap);
}
mythreadno, ebuf);
if (uap) {
}
/*
* Here maybe syslogd can quit. However, syslogd
* has been ignoring this error and keep running.
* So we won't break it.
*/
}
}
(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
{
int fd;
if (fd < 0) {
return (1);
}
/*LINTED*/
return (1);
}
/*LINTED*/
return (1);
}
MALLOC_FAIL("allocating address buffer");
if (bp) {
}
return (1);
}
Ninputs++;
return (0);
}
/*
* Allocate UDP buffer to minimize packet loss.
*/
static void
set_udp_buffer(int fd)
{
if (Debug) {
mythreadno = pthread_self();
}
MALLOC_FAIL("will have no udp buffer");
return;
}
bsize = 0;
break;
}
bsize >>= 1;
if (bsize < 8192) {
break;
}
}
if (bsize == 0) {
logerror("failed to allocate UDP buffer");
}
}
/*
* Attach the network, and allocate UDP buffer for the interface.
*/
static void
bindnet(void)
{
int cnt, i;
char *uap;
if (Debug) {
mythreadno = pthread_self();
}
cnt = 0;
char ebuf[128];
/*LINTED*/
cnt++;
continue;
}
}
/* failed to bind port */
if (uap) {
" for %s", uap);
}
if (uap) {
}
errno = 0;
}
Ninputs--;
}
}
static int
{
struct nd_hostserv hs;
struct nd_addrlist *nap;
void *handle;
char *hp;
"unable to rewind the netconfig database");
errno = 0;
return (-1);
}
if (!nap)
continue;
break;
}
}
}
errno = 0;
return (-1);
}
errno = 0;
return (-1);
}
/* CSTYLED */
hp);
errno = 0;
return (-1);
}
return (-1);
}
if (f->f_file < 0) {
return (-1);
}
return (-1);
}
return (0);
}
static int
amiloghost(void)
{
struct nd_hostserv hs;
struct nd_addrlist *nap;
int i, fd;
void *handle;
char *uap;
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
*/
return (0);
}
continue;
}
continue;
}
continue;
}
mythreadno, uap);
}
if (fd < 0) {
return (0);
}
/*LINTED*/
return (1);
} else {
}
nbp++;
}
}
return (0);
}
int
{
char *a, *b;
size_t n;
return (0);
}
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)
{
if (Debug) {
mythreadno = pthread_self();
}
return ((log_message_t *)NULL);
return ((log_message_t *)NULL);
return (lm);
}
/*
* frees a message structure - should only be called if
* the refcount is 0
*/
static void
{
if (Debug) {
mythreadno = pthread_self();
}
}
/*
* Make sure that the message makes sense in the current locale, and
* does not contain stray control characters.
*/
static void
{
unsigned char *p = (unsigned char *)mbstr;
pthread_t mythreadno = 0;
if (Debug) {
mythreadno = pthread_self();
}
/*
* 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.
*/
if (mb_cur_max > 1) {
/* multibyte locale */
int mlen;
while (*p != '\0') {
mb_cur_max)) == -1) {
/*
* Invalid byte sequence found.
*
* try to print one byte
* in ASCII format.
*/
"MB sequence: %d\n", mythreadno,
wc);
/* 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.
*/
/*
* non-tab, non-newline, and
* control character found.
*
* try to print this wide character
* in ASCII-format.
*/
char *q = filtered;
" control character: %d\n",
mythreadno, wc);
while (mlen--) {
/*
* 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.
*/
/* enough buffer */
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.
*/
"character: %d\n", mythreadno, *p);
/* 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
*/
"character: %d\n", mythreadno, *p);
/* not enough buffer */
goto end;
} else {
continue;
}
} else {
/*
* tab, newline, non-control character, or
* printable found.
*/
*filtered++ = *p++;
cs++;
continue;
} else {
/* not enough buffer */
goto end;
}
}
}
}
end:
*filtered = '\0';
if (cs >= 2 &&
}
}
static char *
alloc_stacks(int numstacks)
{
char *stack_top;
char *addr;
int i;
/*
* stacksize and redzonesize are global so threads
* can be created elsewhere and refer to the sizes
*/
/*
* 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.
*/
if (stack_top == MAP_FAILED)
return (NULL);
/*
* 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++) {
}
return ((char *)(stack_top + redzonesize));
}
static void
dealloc_stacks(int numstacks)
{
}
static void
filed_destroy(struct filed *f)
{
(void) dataq_destroy(&f->f_queue);
}
static void
close_door(void)
{
if (Debug) {
mythreadno = pthread_self();
}
(void) fdetach(DoorFileName);
}
static void
delete_doorfiles(void)
{
int err;
if (Debug) {
mythreadno = pthread_self();
}
if (unlink(DoorFileName) < 0) {
exit(1);
}
}
if (unlink(OLD_DOORFILE) < 0) {
"failed", OLD_DOORFILE);
mythreadno, line);
"error: %s, errno=%d\n",
exit(1);
}
"failure OK on RO file system\n",
}
}
}
if (unlink(PidFileName) < 0) {
" - fatal", PidFileName);
exit(1);
}
}
if (unlink(OLD_PIDFILE) < 0) {
mythreadno, line);
"error: %s, errno=%d\n",
exit(1);
}
"failure OK on RO file system\n",
}
}
}
if (DoorFd != -1) {
(void) door_revoke(DoorFd);
}
mythreadno, DoorFd);
}
/*ARGSUSED*/
static void
{
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
{
char *p = *buf;
if (c == '\n') {
*p++ = '\\';
*p++ = 'n';
*cl += 2;
*buf = p;
return (2);
} else {
return (0);
}
} else if (c < 0200) {
/* ascii control character */
*p++ = '^';
*p++ = c ^ 0100;
*cl += 2;
*buf = p;
return (2);
} else {
return (0);
}
} else {
*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
*/
{
const char *p;
if (Debug) {
mythreadno = pthread_self();
}
if (len == 0) {
return (0);
}
if (mb_cur_max == 1) {
/* single-byte locale */
if (*p == '\n') {
}
}
} else {
/* multi-byte locale */
int mlen;
const char *nl;
p = buf;
if (mlen == -1) {
/*
* Invalid character found.
*/
"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;
}
}
if (nl) {
}
/*
* no newline nor null byte found.
* Also it's guaranteed that *p points to
* the 1st byte of a (multibyte) character
* at this point.
*/
}
}
/*
* 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
*/
{
const char *p;
char *q = obuf;
if (Debug) {
mythreadno = pthread_self();
}
if (len == 0) {
return (0);
}
if (mb_cur_max == 1) {
/* single-byte locale */
for (p = buf; *p; ) {
if (*p != '\n') {
*q++ = *p++;
olen++;
} else {
*q = '\0';
}
} else {
*q = '\0';
}
}
*q = '\0';
} else {
/* multi-byte locale */
int mlen;
for (p = buf; *p; ) {
if (mlen == -1) {
/*
* Invalid character found.
*/
"sequence\n", mythreadno);
/*
* handle as a single byte character.
*/
*q++ = *p++;
olen++;
} else {
*q = '\0';
}
} else {
/*
* It's guaranteed that *p points to
* the 1st byte of a multibyte character.
*/
if (*p == '\n') {
*q = '\0';
}
int n;
for (n = 0; n < mlen; n++) {
*q++ = *p++;
}
} else {
*q = '\0';
}
}
}
/*
* 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';
}
}
/*
* 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
*/
{
const char *p;
char *q = obuf;
if (Debug) {
mythreadno = pthread_self();
}
if (len == 0) {
return (0);
}
if (mb_cur_max == 1) {
/* single-byte locale */
} else {
return (obuflen - 1);
}
} else {
/* multi-byte locale */
int mlen;
for (p = buf; *p; ) {
if (mlen == -1) {
/*
* Invalid character found.
*/
"sequence\n", mythreadno);
/*
* handle as a single byte character.
*/
*q++ = *p++;
olen++;
} else {
*q = '\0';
}
} else {
int n;
for (n = 0; n < mlen; n++) {
*q++ = *p++;
}
} else {
*q = '\0';
}
}
}
*q = '\0';
}
}
/*
* defaults:
* Read defaults from file.
*/
static void
defaults(void)
{
int flags;
char *ptr;
/*
* ignore case
*/
}
}
}
/*
* close all the input devices.
*/
static void
shutdown_input(void)
{
int cnt;
shutting_down = 1;
}
}
/*
* 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;
if (Debug) {
mythreadno = pthread_self();
}
for (;;) {
} else {
}
}
MALLOC_FAIL("dropping message from remote");
continue;
}
}
/*NOTREACHED*/
return (NULL);
}
/*
* Does all HUP(re-configuration) process.
*/
static void
{
int really_stuck;
int console_stuck = 0;
struct filed *f;
char cbuf[30];
if (Debug) {
mythreadno = pthread_self();
}
/* If we get here then we must need to regen */
flushmsg(0);
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) {
" moving to the safe place\n",
}
/* 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) {
}
/*
* Prevent logging until we are truly done processing the HUP
*/
(void) pthread_mutex_lock(&hup_lock);
(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.
*/
if (Debug) {
}
/* 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 */
if (hup_state & HUP_LOGMSG_SUSPENDED)
break;
(void) sleep(1);
}
if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
if (Debug) {
"stop. enforcing\n",
}
/* probably we have too long input queue, or really stuck */
(void) pthread_mutex_lock(&hup_lock);
(void) pthread_mutex_unlock(&hup_lock);
if (hup_state & HUP_LOGMSG_SUSPENDED)
break;
(void) sleep(1);
}
if ((hup_state & HUP_LOGMSG_SUSPENDED) == 0) {
if (Debug) {
" does not stop. give up\n",
}
logerror("could not suspend logmsg - fatal");
goto thread_stuck;
}
}
if (Debug) {
}
/*
* 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) {
}
cnt = 0;
really_stuck = 0;
conf_threads > really_stuck) {
/* save initial queue count */
-1 : f->f_queue_count;
}
if (conf_threads == 0)
break;
(void) sleep(1);
}
if (conf_threads == 0)
break;
if (Debug) {
"%d threads are still alive.\n",
}
really_stuck = 0;
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;
"tid=%d is really stuck.\n",
mythreadno, f->f_thread);
} else {
f->f_prev_queue_count = 0;
"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) {
" complete awaiting logit()\n",
" %d threads stuck\n",
}
/*
* 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) {
f->f_prev_queue_count == 1) {
/* console is really stuck */
console_stuck = 1;
}
continue;
cnt = f->f_queue_count;
if (f->f_orig_type == F_FORW)
else
}
if (Debug) {
}
/* last chance to exit */
if (conf_threads == 0)
break;
(void) sleep(1);
}
if (Debug) {
}
}
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) {
"discarded %d messages\n",
}
}
/*
* If all threads still haven't exited
* something is stuck or hosed. We just
* have no option but to exit.
*/
if (conf_threads) {
if (Debug) {
}
"syslogd(%s): some logger thread(s) "
"are stuck%s; syslogd is shutting down.",
if (console_stuck) {
if (m != NULL) {
pclose(m);
}
}
exit(1);
}
/* Free up some resources */
filed_destroy(f);
}
}
if (Debug) {
}
conf_init(); /* start reconfigure */
out:;
/* Now should be ready to dispatch error messages from syslogd. */
/* Wake up the log thread */
if (Debug) {
}
(void) pthread_mutex_lock(&hup_lock);
(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
{
struct hostname_cache **hpp;
if (Debug) {
mythreadno = pthread_self();
}
if (reinit) {
}
} else {
int i;
MALLOC_FAIL("hostname cache");
logerror("hostname cache disabled");
return;
}
for (i = 0; i < hnc_size; i++) {
}
hnc_active = NULL;
}
}
static host_list_t *
{
if (Debug) {
mythreadno = pthread_self();
}
return (NULL);
}
mythreadno, hp);
/* Note: hnc_unreg changes *hpp */
continue;
}
/*
* found!
* Put the entry at the top.
*/
if (hp != hnc_active) {
/* unlink from active list */
/* push it onto the top */
hnc_active = hp;
}
return (hp->h);
}
}
return (NULL);
}
static void
{
void *addrbuf;
if (Debug) {
mythreadno = pthread_self();
}
return;
}
MALLOC_FAIL("pushing hostname cache");
return;
}
/*
* first go through active list, and discard the
* caches which has been invalid.
*/
mythreadno, hp);
} else {
}
}
mythreadno, *tailp);
/*
* If still no inactive cache, then steal the least
* active element.
*/
}
}
/* push it on the top */
hnc_active = hp;
hp->h = h;
/*
* As far as cache is valid, corresponding host_list must
* also be valid. Increments the refcnt to avoid freeing
* host_list.
*/
h->hl_refcnt++;
}
static void
{
if (Debug) {
mythreadno = pthread_self();
}
/* unlink from active list */
/* put in freeq */
}
/*
* 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
{
dataq_init(&tmpq);
interrorlog = 0;
}
/*
* Turn internal error messages to regular input stream.
* All pending messages are pulled and pushed into the regular
* input queue.
*/
static void
{
interrorlog = 1;
/*
* push all the pending messages into inputq.
*/
}
}