diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/syslogd | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/syslogd')
-rw-r--r-- | usr/src/cmd/syslogd/Makefile | 65 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/Makefile.com | 111 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/README | 28 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/amd64/Makefile | 34 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/conf.c | 219 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/conf.h | 52 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/dataq.h | 58 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/i386/Makefile | 35 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/list.c | 118 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/llt.h | 49 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/newsyslog | 36 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/queue.c | 144 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/sparc/Makefile | 35 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/sparcv9/Makefile | 35 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/syslog.conf | 56 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/syslogd.c | 5501 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/syslogd.dfl | 36 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/syslogd.h | 309 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/system-log | 54 | ||||
-rw-r--r-- | usr/src/cmd/syslogd/system-log.xml | 136 |
20 files changed, 7111 insertions, 0 deletions
diff --git a/usr/src/cmd/syslogd/Makefile b/usr/src/cmd/syslogd/Makefile new file mode 100644 index 0000000000..577a43398e --- /dev/null +++ b/usr/src/cmd/syslogd/Makefile @@ -0,0 +1,65 @@ +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#ident "%Z%%M% %I% %E% SMI" +# +# cmd/syslogd/Makefile +# + +PROG= syslogd +MANIFEST= system-log.xml +SVCMETHOD= system-log + +include ../Makefile.cmd + +SUBDIRS= $(MACH) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +ROOTMANIFESTDIR= $(ROOTSVCSYSTEM) + +$(ROOTMANIFEST) := FILEMODE = 0444 + +.KEEP_STATE: + +all: $(SUBDIRS) + +clean clobber lint: $(SUBDIRS) + +install: $(ROOTMANIFEST) $(ROOTSVCMETHOD) $(SUBDIRS) + +check: $(CHKMANIFEST) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../Makefile.targ + diff --git a/usr/src/cmd/syslogd/Makefile.com b/usr/src/cmd/syslogd/Makefile.com new file mode 100644 index 0000000000..8ef3ed5a24 --- /dev/null +++ b/usr/src/cmd/syslogd/Makefile.com @@ -0,0 +1,111 @@ +# +# 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. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= syslogd +ROTATESCRIPT= newsyslog +CONFIGFILE= syslog.conf +TXTS= syslog.conf +PRODUCT= $(PROG) $(ROTATESCRIPT) +OBJS= syslogd.o queue.o list.o conf.o +SRCS= $(OBJS:%.o=../%.c) +LLOBJS= $(OBJS:%.o=%.ll) + +include ../../Makefile.cmd + +$(PROG) := LDLIBS += -lcmd -lnsl +CCVERBOSE = +CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS -D_REENTRANT +CPPFLAGS += -DNDEBUG + +VARSYSLOG= syslog +VARAUTHLOG= authlog +ROOTVARLOGD= $(ROOT)/var/log + +OWNER= root +GROUP= sys + +DFLTD= $(ROOTETC)/default + +ROOTETCCONFIG= $(CONFIGFILE:%=$(ROOTETC)/%) +ROOTETCDFLT= $(PROG:%=$(DFLTD)/%) +ROOTLIBROTATE= $(ROTATESCRIPT:%=$(ROOTLIB)/%) +ROOTVARSYSLOG= $(VARSYSLOG:%=$(ROOTVARLOGD)/%) +ROOTVARAUTHLOG= $(VARAUTHLOG:%=$(ROOTVARLOGD)/%) + +$(ROOTUSRSBINPROG) := FILEMODE = 0555 +$(ROOTUSRLIBROTATE) := FILEMODE = 0555 +$(ROOTETCCONFIG) := FILEMODE = 0644 +$(ROOTVARSYSLOG) := FILEMODE = 0644 +$(ROOTVARAUTHLOG) := FILEMODE = 0600 +$(ROOTETCDFLT) := FILEMODE = 0444 + +$(ROOTVARLOGD)/% : % + $(INS.file) + +$(ROOTETC)/%: ../% + $(INS.file) + +$(ROOTLIB)/%: ../% + $(INS.file) + +$(DFLTD)/%: ../%.dfl + $(INS.rename) + +.KEEP_STATE: + +.SUFFIXES: $(SUFFIXES) .ll + +.c.ll: + $(CC) $(CFLAGS) $(CPPFLAGS) -Zll -o $@ $< + +.PARALLEL: $(OBJS) + + +$(VARSYSLOG) $(VARAUTHLOG): + $(ECHO) '\c' > $@ + +%.o: ../%.c + $(COMPILE.c) $< + +%.ll: ../%.c + $(CC) $(CFLAGS) $(CPPFLAGS) -Zll -o $@ $< + +syslogd: $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +logfiles: $(ROOTVARSYSLOG) $(ROOTVARAUTHLOG) + +clean: + $(RM) $(OBJS) $(LLOBJS) $(VARSYSLOG) $(VARAUTHLOG) + +lint: lint_SRCS + +lock_lint: $(LLOBJS) + +include ../../Makefile.targ diff --git a/usr/src/cmd/syslogd/README b/usr/src/cmd/syslogd/README new file mode 100644 index 0000000000..fd51649f23 --- /dev/null +++ b/usr/src/cmd/syslogd/README @@ -0,0 +1,28 @@ +# +# 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 (c) 2001 by Sun Microsystems, Inc. +All rights reserved. + +#ident "%Z%%M% %I% %E% SMI" + +Note: These sources should be kept as backwards portable as possible on +all OS releases back to Solaris 2.6 for patch releases. diff --git a/usr/src/cmd/syslogd/amd64/Makefile b/usr/src/cmd/syslogd/amd64/Makefile new file mode 100644 index 0000000000..3d68e808fe --- /dev/null +++ b/usr/src/cmd/syslogd/amd64/Makefile @@ -0,0 +1,34 @@ +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../Makefile.cmd.64 + +all: $(PROG) + +install: all $(ROOTUSRSBINPROG64) diff --git a/usr/src/cmd/syslogd/conf.c b/usr/src/cmd/syslogd/conf.c new file mode 100644 index 0000000000..2e60db8529 --- /dev/null +++ b/usr/src/cmd/syslogd/conf.c @@ -0,0 +1,219 @@ +/* + * 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 (c) 1997-2000 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/wait.h> + +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + +#include "conf.h" + +#define CF_DEFSIZE 32 /* Starting table size */ +#define CF_GROW 2 /* Table size multiplier on grow */ + +extern void logerror(char *); + +static FILE * +open_conf_pipe(const char *cmd, char *argv[], pid_t *pidp) +{ + int pfds[2]; + pid_t pid; + struct sigaction act; + + /* Create a pipe and fork a child process to run the command */ + + if (pipe(pfds) == -1) { + logerror("failed to create pipe"); + return (NULL); + } + + if ((pid = fork1()) == -1) { + logerror("failed to fork1"); + goto err; + } + + /* If we're in the child, run the command and output to the pipe */ + + if (pid == 0) { + /* + * We must set up to ignore these signals, which may be + * propogated from the calling process. + */ + + act.sa_handler = SIG_IGN; + + (void) sigaction(SIGHUP, &act, NULL); + (void) sigaction(SIGALRM, &act, NULL); + (void) sigaction(SIGUSR1, &act, NULL); + + (void) close(pfds[0]); + (void) close(STDOUT_FILENO); + + if (dup2(pfds[1], STDOUT_FILENO) == -1) { + logerror("failed to dup to stdout"); + (void) close(pfds[1]); + _exit(127); + } + + (void) execvp(cmd, argv); + logerror("failed to parse configuration file"); + _exit(127); + /*NOTREACHED*/ + } + + /* If we're in the parent, open the read end of the pipe and return */ + + *pidp = pid; + (void) close(pfds[1]); + return (fdopen(pfds[0], "r")); + +err: + (void) close(pfds[0]); + (void) close(pfds[1]); + return (NULL); +} + +static void +close_conf_pipe(FILE *fp, pid_t pid) +{ + int status; + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) + break; + } + + (void) fclose(fp); +} + +static int +grow_conf_file(conf_t *cf) +{ + int ndsize = cf->cf_dsize ? cf->cf_dsize * CF_GROW : CF_DEFSIZE; + void *ndtab = realloc(cf->cf_dtab, sizeof (char *) * ndsize); + + register char *p; + int odsize, lines, i; + + if (ndtab == NULL) { + logerror("failed to allocate config file table"); + return (-1); + } + + lines = ndsize - cf->cf_dsize; + odsize = cf->cf_dsize; + + cf->cf_dtab = (char **)ndtab; + cf->cf_dsize = ndsize; + + for (i = 0; i < lines; i++) { + if ((p = (char *)malloc(BUFSIZ)) == NULL) { + logerror("failed to allocate config file buffer"); + return (-1); + } + + cf->cf_dtab[odsize + i] = p; + } + + return (0); +} + +int +conf_open(conf_t *cf, const char *cmd, char *argv[]) +{ + char *line, *p; + pid_t pid; + FILE *fp; + + (void) memset(cf, 0, sizeof (conf_t)); + + if ((fp = open_conf_pipe(cmd, argv, &pid)) == NULL) + return (-1); + + for (;;) { + /* If we need to grow the table, do so now */ + + if (cf->cf_lines >= cf->cf_dsize) { + if (grow_conf_file(cf) == -1) + goto err; + } + + line = cf->cf_dtab[cf->cf_lines]; + + /* Read the next line, and break out if we're done */ + + if (fgets(line, BUFSIZ, fp) == NULL) + break; + + /* Strip newline and bump line counter */ + + if ((p = strchr(line, '\n')) != NULL) + *p = '\0'; + + cf->cf_lines++; + } + + close_conf_pipe(fp, pid); + return (0); + +err: + close_conf_pipe(fp, pid); + return (-1); +} + +void +conf_rewind(conf_t *cf) +{ + cf->cf_ptr = 0; +} + +char * +conf_read(conf_t *cf) +{ + if (cf->cf_ptr < cf->cf_lines) + return (cf->cf_dtab[cf->cf_ptr++]); + + return (NULL); +} + +void +conf_close(conf_t *cf) +{ + int i; + + if (cf->cf_dtab != NULL) { + for (i = 0; i < cf->cf_dsize; i++) + free(cf->cf_dtab[i]); + free(cf->cf_dtab); + } +} diff --git a/usr/src/cmd/syslogd/conf.h b/usr/src/cmd/syslogd/conf.h new file mode 100644 index 0000000000..d531e85ae5 --- /dev/null +++ b/usr/src/cmd/syslogd/conf.h @@ -0,0 +1,52 @@ +/* + * 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 (c) 1997 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _CONF_H +#define _CONF_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char **cf_dtab; /* Array of pointers to line data */ + int cf_dsize; /* Number of allocated lines in dtab */ + int cf_lines; /* Number of valid lines in dtab */ + int cf_ptr; /* Current dtab location for read/rewind */ +} conf_t; + +int conf_open(conf_t *, const char *, char *[]); +void conf_close(conf_t *); +void conf_rewind(conf_t *); +char *conf_read(conf_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _CONF_H */ diff --git a/usr/src/cmd/syslogd/dataq.h b/usr/src/cmd/syslogd/dataq.h new file mode 100644 index 0000000000..259949445b --- /dev/null +++ b/usr/src/cmd/syslogd/dataq.h @@ -0,0 +1,58 @@ +/* + * 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 1996-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _DATAQ_H +#define _DATAQ_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "llt.h" + +typedef struct dataq_data { + ll_t list; + void *data; +} dataq_data_t; + +typedef struct dataq_waiter { + ll_t list; + pthread_cond_t cv; + int wakeup; +} dataq_waiter_t; + +typedef struct dataq { + pthread_mutex_t lock; + int num_data; + int num_waiters; + llh_t data; + llh_t waiters; +} dataq_t; + +int dataq_init(dataq_t *ptr); +int dataq_enqueue(dataq_t *dataq, void *in); +int dataq_dequeue(dataq_t *dataq, void **outptr, int); +int dataq_destroy(dataq_t *dataq); + +#endif /* _DATAQ_H */ diff --git a/usr/src/cmd/syslogd/i386/Makefile b/usr/src/cmd/syslogd/i386/Makefile new file mode 100644 index 0000000000..d22fbe3201 --- /dev/null +++ b/usr/src/cmd/syslogd/i386/Makefile @@ -0,0 +1,35 @@ +# +# 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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, 2001 by Sun Microsystems, Inc. +# All rights reserved. +# +# +# cmd/syslogd/i386/Makefile + +include ../Makefile.com + +all: $(PROG) $(VARSYSLOG) $(VARAUTHLOG) + +install: all $(ROOTUSRSBINPROG) $(ROOTETCDFLT) $(ROOTETCCONFIG) \ + $(ROOTLIBROTATE) logfiles diff --git a/usr/src/cmd/syslogd/list.c b/usr/src/cmd/syslogd/list.c new file mode 100644 index 0000000000..a69d9cc899 --- /dev/null +++ b/usr/src/cmd/syslogd/list.c @@ -0,0 +1,118 @@ +/* + * 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 (c) 1996-1999 by Sun Microsystems, Inc. + * All rights reserved. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <pthread.h> +#include <malloc.h> +#include <memory.h> +#include <assert.h> +#include <poll.h> +#include <stdio.h> +#include "llt.h" +void +ll_init(llh_t *head) +{ + head->back = &head->front; + head->front = NULL; +} +void +ll_enqueue(llh_t *head, ll_t *data) +{ + data->n = NULL; + *head->back = data; + head->back = &data->n; +} +/* + * apply the function func to every element of the ll in sequence. Can + * be used to free up the element, so "n" is computed before func is + * called on it. + */ +void +ll_mapf(llh_t *head, void (*func)(void *)) +{ + ll_t * t = head->front; + ll_t * n; + + while (t) { + n = t->n; + func(t); + t = n; + } +} +ll_t * +ll_peek(llh_t *head) +{ + return (head->front); +} +ll_t * +ll_dequeue(llh_t *head) +{ + ll_t *ptr; + ptr = head->front; + if (ptr && ((head->front = ptr->n) == NULL)) + head->back = &head->front; + return (ptr); +} +ll_t * +ll_traverse(llh_t *ptr, int (*func)(void *, void *), void *user) +{ + ll_t *t; + ll_t **prev = &ptr->front; + + t = ptr->front; + while (t) { + switch (func(t, user)) { + case 1: + return (NULL); + case 0: + prev = &(t->n); + t = t->n; + break; + case -1: + if ((*prev = t->n) == NULL) + ptr->back = prev; + return (t); + } + } + return (NULL); +} +/* Make sure the list isn't corrupt and returns number of list items */ +int +ll_check(llh_t *head) +{ + int i = 0; + ll_t *ptr = head->front; + ll_t **prev = &head->front; + + while (ptr) { + i++; + prev = &ptr->n; + ptr = ptr->n; + } + assert(head->back == prev); + return (i); +} diff --git a/usr/src/cmd/syslogd/llt.h b/usr/src/cmd/syslogd/llt.h new file mode 100644 index 0000000000..bc0bd089aa --- /dev/null +++ b/usr/src/cmd/syslogd/llt.h @@ -0,0 +1,49 @@ +/* + * 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(c) 1996-1999, by Sun Microsystems, Inc. + * All rights reserved. + */ + +#ifndef _LLT_H +#define _LLT_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +typedef struct ll { + struct ll *n; +} ll_t; + +typedef struct llh { + ll_t *front; + ll_t **back; +} llh_t; + +void ll_init(llh_t *head); +void ll_enqueue(llh_t *head, ll_t *data); +void ll_mapf(llh_t *head, void (*func)(void *)); +ll_t * ll_peek(llh_t *head); +ll_t * ll_dequeue(llh_t *head); +ll_t * ll_traverse(llh_t *ptr, int (*func)(void *, void *), void *user); +int ll_check(llh_t *head); + +#endif /* _LLT_H */ diff --git a/usr/src/cmd/syslogd/newsyslog b/usr/src/cmd/syslogd/newsyslog new file mode 100644 index 0000000000..09041e2833 --- /dev/null +++ b/usr/src/cmd/syslogd/newsyslog @@ -0,0 +1,36 @@ +#!/bin/sh +# +# 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 (c) 1997-2001 by Sun Microsystems, Inc. +# All rights reserved. +# +#ident "%Z%%M% %I% %E% SMI" +# +# NOTE: This script is no longer used -- see logadm(1M) +# +# Running this script checks to see if /var/log/syslog or /var/adm/messages +# need rotating, according to the policies in /etc/logadm.conf. Since logadm +# normally runs from root's crontab every morning, running this script is no +# longer necessary. +# +/usr/sbin/logadm /var/log/syslog /var/adm/messages diff --git a/usr/src/cmd/syslogd/queue.c b/usr/src/cmd/syslogd/queue.c new file mode 100644 index 0000000000..e546605e40 --- /dev/null +++ b/usr/src/cmd/syslogd/queue.c @@ -0,0 +1,144 @@ +/* + * 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 1996-2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <pthread.h> +#include <malloc.h> +#include <memory.h> +#include "dataq.h" +#include <assert.h> + +#ifndef NDEBUG +static int +dataq_check(dataq_t *ptr) /* call while holding lock! */ +{ + assert(ptr->num_data == ll_check(&ptr->data)); + assert(ptr->num_waiters == ll_check(&ptr->waiters)); + return (1); +} +#endif + +int +dataq_init(dataq_t *ptr) +{ + ptr->num_data = 0; + ptr->num_waiters = 0; + ll_init(&ptr->data); + ll_init(&ptr->waiters); + pthread_mutex_init(&ptr->lock, NULL); + assert((pthread_mutex_lock(&ptr->lock) == 0) && + (dataq_check(ptr) == 1) && + (pthread_mutex_unlock(&ptr->lock) == 0)); + return (0); +} + +int +dataq_enqueue(dataq_t *dataq, void *in) +{ + dataq_data_t *ptr = (dataq_data_t *)malloc(sizeof (*ptr)); + dataq_waiter_t *sleeper; + + if (ptr == NULL) + return (-1); + ptr->data = in; + pthread_mutex_lock(&dataq->lock); + assert(dataq_check(dataq)); + ll_enqueue(&dataq->data, &ptr->list); + dataq->num_data++; + if (dataq->num_waiters) { + /*LINTED*/ + sleeper = (dataq_waiter_t *)ll_peek(&dataq->waiters); + sleeper->wakeup = 1; + pthread_cond_signal(&sleeper->cv); + } + assert(dataq_check(dataq)); + pthread_mutex_unlock(&dataq->lock); + return (0); +} + +int +dataq_dequeue(dataq_t *dataq, void **outptr, int try) +{ + dataq_data_t *dptr; + dataq_waiter_t *sleeper; + + pthread_mutex_lock(&dataq->lock); + if ((dataq->num_waiters > 0) || + ((dptr = (dataq_data_t *)ll_dequeue(&dataq->data)) == NULL)) { + dataq_waiter_t wait; + if (try) { + pthread_mutex_unlock(&dataq->lock); + return (1); + } + wait.wakeup = 0; + pthread_cond_init(&wait.cv, NULL); + dataq->num_waiters++; + ll_enqueue(&dataq->waiters, &wait.list); + while (wait.wakeup == 0) + pthread_cond_wait(&wait.cv, &dataq->lock); + ll_dequeue(&dataq->waiters); + dataq->num_waiters--; + pthread_cond_destroy(&wait.cv); + dptr = (dataq_data_t *)ll_dequeue(&dataq->data); + } + dataq->num_data--; + if (dataq->num_data && dataq->num_waiters) { + /*LINTED*/ + sleeper = (dataq_waiter_t *)ll_peek(&dataq->waiters); + sleeper->wakeup = 1; + pthread_cond_signal(&sleeper->cv); + } + pthread_mutex_unlock(&dataq->lock); + *outptr = dptr->data; + free(dptr); + return (0); +} + +static void +dataq_data_destroy(void * p) +{ + dataq_data_t *d = (dataq_data_t *)p; + free(d->data); + free(d); +} + +static void +dataq_waiters_destroy(void * p) +{ + dataq_waiter_t *d = (dataq_waiter_t *)p; + pthread_cond_destroy(&d->cv); + free(d); +} + +int +dataq_destroy(dataq_t *dataq) +{ + pthread_mutex_destroy(&dataq->lock); + ll_mapf(&dataq->data, dataq_data_destroy); + ll_mapf(&dataq->waiters, dataq_waiters_destroy); + return (0); +} diff --git a/usr/src/cmd/syslogd/sparc/Makefile b/usr/src/cmd/syslogd/sparc/Makefile new file mode 100644 index 0000000000..9b1bc3c1da --- /dev/null +++ b/usr/src/cmd/syslogd/sparc/Makefile @@ -0,0 +1,35 @@ +# +# 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 +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, 2001 by Sun Microsystems, Inc. +# All rights reserved. +# +# +# cmd/syslogd/sparc/Makefile + +include ../Makefile.com + +all: $(PROG) $(VARSYSLOG) $(VARAUTHLOG) + +install: all $(ROOTUSRSBINPROG) $(ROOTETCDFLT) $(ROOTETCCONFIG) \ + $(ROOTLIBROTATE) logfiles diff --git a/usr/src/cmd/syslogd/sparcv9/Makefile b/usr/src/cmd/syslogd/sparcv9/Makefile new file mode 100644 index 0000000000..31cc061bd3 --- /dev/null +++ b/usr/src/cmd/syslogd/sparcv9/Makefile @@ -0,0 +1,35 @@ +# +# 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 +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, by Sun Microsystems, Inc. +# All rights reserved. +# +# +# cmd/syslogd/sparcv9/Makefile + +include ../Makefile.com +include ../../Makefile.cmd.64 + +all: $(PROG) + +install: all $(ROOTUSRSBINPROG64) diff --git a/usr/src/cmd/syslogd/syslog.conf b/usr/src/cmd/syslogd/syslog.conf new file mode 100644 index 0000000000..1ed7a40d39 --- /dev/null +++ b/usr/src/cmd/syslogd/syslog.conf @@ -0,0 +1,56 @@ +# +# 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 +# +#ident "%Z%%M% %I% %E% SMI" /* SunOS 5.0 */ +# +# Copyright (c) 1991-1998 by Sun Microsystems, Inc. +# All rights reserved. +# +# syslog configuration file. +# +# This file is processed by m4 so be careful to quote (`') names +# that match m4 reserved words. Also, within ifdef's, arguments +# containing commas must be quoted. +# +*.err;kern.notice;auth.notice /dev/sysmsg +*.err;kern.debug;daemon.notice;mail.crit /var/adm/messages + +*.alert;kern.err;daemon.err operator +*.alert root + +*.emerg * + +# if a non-loghost machine chooses to have authentication messages +# sent to the loghost machine, un-comment out the following line: +#auth.notice ifdef(`LOGHOST', /var/log/authlog, @loghost) + +mail.debug ifdef(`LOGHOST', /var/log/syslog, @loghost) + +# +# non-loghost machines will use the following lines to cause "user" +# log messages to be logged locally. +# +ifdef(`LOGHOST', , +user.err /dev/sysmsg +user.err /var/adm/messages +user.alert `root, operator' +user.emerg * +) diff --git a/usr/src/cmd/syslogd/syslogd.c b/usr/src/cmd/syslogd/syslogd.c new file mode 100644 index 0000000000..a1372289cb --- /dev/null +++ b/usr/src/cmd/syslogd/syslogd.c @@ -0,0 +1,5501 @@ +/* + * 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 "<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 <sys/types.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 <sys/socket.h> +#include <tiuser.h> +#include <utmpx.h> +#include <limits.h> +#include <pthread.h> +#include <fcntl.h> +#include <stropts.h> +#include <assert.h> +#include <sys/statvfs.h> + +#include <sys/param.h> +#include <sys/sysmacros.h> +#include <sys/syslog.h> +#include <sys/strlog.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/utsname.h> +#include <sys/poll.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <sys/mman.h> +#include <sys/note.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 *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 : "<unknown>"); + 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 : "<unknown>"); + 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 : "<unknown>"); + + if ((h = hnc_lookup(nbp, ncp)) != NULL) { + DPRINT4(2, "cvthname(%u): Cache found %p for %s (%s)\n", + mythreadno, h, uap ? uap : "<unknown>", + h->hl_hosts[0]); + return (h); + } + DPRINT2(2, "cvthname(%u): No cache found for %s\n", + mythreadno, uap ? uap : "<unknown>"); + + 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 +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 sys_errlist 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 : "<unknown>"); + + 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, "<unknown>", 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); +} diff --git a/usr/src/cmd/syslogd/syslogd.dfl b/usr/src/cmd/syslogd/syslogd.dfl new file mode 100644 index 0000000000..9c98a4028e --- /dev/null +++ b/usr/src/cmd/syslogd/syslogd.dfl @@ -0,0 +1,36 @@ +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2005 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# 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 +# +# /etc/default/syslogd +# +# syslogd default settings processed via syslogd(1M). +# +# LOG_FROM_REMOTE affects the logging of remote messages, see syslogd(1M) +# for details. The default value is "YES". A value of "NO" (any case) +# results in disabling of remote logging; any other value is ignored. +# +# Copy and uncomment the following default lines to change the values. +# +#LOG_FROM_REMOTE=YES diff --git a/usr/src/cmd/syslogd/syslogd.h b/usr/src/cmd/syslogd/syslogd.h new file mode 100644 index 0000000000..e5d23740e7 --- /dev/null +++ b/usr/src/cmd/syslogd/syslogd.h @@ -0,0 +1,309 @@ +/* + * 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 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 1983,1984,1985,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. + */ + +#ifndef _SYSLOGD_H +#define _SYSLOGD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + + struct utmpx dummy; /* for sizeof ut_user, ut_line */ + +/* + * Various constants & tunable values for syslogd + */ +#define DEBUGFILE "/var/adm/syslog.debug" +#define MAXLINE 1024 /* maximum line length */ +#define DEFUPRI (LOG_USER|LOG_INFO) +#define DEFSPRI (LOG_KERN|LOG_CRIT) +#define MARKCOUNT 3 /* ratio of minor to major marks */ +#define UNAMESZ (sizeof (dummy.ut_user)) /* length of a login name */ +#define UDEVSZ (sizeof (dummy.ut_line)) /* length of login dev name */ +#define MAXUNAMES 20 /* maximum number of user names */ +#define Q_HIGHWATER_MARK 10000 /* max outstanding msgs per file */ +#define NOPRI 0x10 /* the "no priority" priority */ +#define LOG_MARK (LOG_NFACILITIES << 3) /* mark "facility" */ + +/* + * host_list_t structure contains a list of hostnames for a given address + */ +typedef struct host_list { + int hl_cnt; /* number of hl_hosts entries */ + char **hl_hosts; /* hostnames */ + pthread_mutex_t hl_mutex; /* protects this structs members */ + int hl_refcnt; /* reference count */ +} host_list_t; + +/* + * host_info_t structure contains address information for a host + * from which we received a message. + */ +typedef struct host_info { + struct netconfig *ncp; + struct netbuf addr; +} host_info_t; + +/* + * statistics structure attached to each filed for debugging + */ +typedef struct filed_stats { + int flag; /* flag word */ + int total; /* total messages logged */ + int dups; /* duplicate messages */ + int cantfwd; /* can't forward */ + int errs; /* write errors */ +} filed_stats_t; + + +/* + * internal representation of a log message. Includes all routing & bookkeeping + * information for the message. created in the system & network poll routines, + * and passed among the various processing threads as necessary + */ + +typedef struct log_message { + pthread_mutex_t msg_mutex; /* protects this structs members */ + int refcnt; /* message reference count */ + int pri; /* message priority */ + int flags; /* misc flags */ + time_t ts; /* timestamp */ + host_list_t *hlp; /* ptr to host list struct */ + void *ptr; /* for anonymous use */ + char msg[MAXLINE+1]; /* the message itself */ +} log_message_t; + +_NOTE(MUTEX_PROTECTS_DATA(log_message_t::msg_mutex, log_message_t)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(log_message_t)) + + +/* + * format of a saved message. For each active file we are logging + * we save the last message and the current message, to make it + * possible to suppress duplicates on a per file basis. Earlier + * syslogd's used a global buffer for duplicate checking, so + * strict per file duplicate suppression was not always possible. + */ +typedef struct saved_msg { + int pri; + int flags; + time_t time; + char host[SYS_NMLN+1]; + char msg[MAXLINE+1]; +} saved_message_t; + + +/* + * Flags to logmsg(). + */ + +#define IGN_CONS 0x001 /* don't print on console */ +#define IGN_FILE 0x002 /* don't write to log file */ +#define SYNC_FILE 0x004 /* do fsync on file after printing */ +#define NOCOPY 0x008 /* don't suppress duplicate messages */ +#define ADDDATE 0x010 /* add a date to the message */ +#define MARK 0x020 /* this message is a mark */ +#define LOGSYNC 0x040 /* nightly log update message */ +#define NETWORK 0x100 /* message came from the net */ +#define SHUTDOWN 0x200 /* internal shutdown message */ +#define FLUSHMSG 0x400 /* internal flush message */ + +/* + * This structure represents the files that will have log + * copies printed. There is one instance of this for each + * file that is being logged to. + */ +struct filed { + pthread_mutex_t filed_mutex; /* protects this filed */ + pthread_t f_thread; /* thread that handles this file */ + dataq_t f_queue; /* queue of messages for this file */ + int f_queue_count; /* count of messages on the queue */ + int f_prev_queue_count; /* prev count of msgs on the queue */ + short f_type; /* entry type, see below */ + short f_orig_type; /* save entry type */ + int f_file; /* file descriptor */ + int f_msgflag; /* message disposition */ + filed_stats_t f_stat; /* statistics */ + saved_message_t f_prevmsg; /* previous message */ + saved_message_t f_current; /* current message */ + int f_prevcount; /* message repeat count */ + uchar_t f_pmask[LOG_NFACILITIES+1]; /* priority mask */ + union { + char f_uname[MAXUNAMES][SYS_NMLN + 1]; + struct { + char f_hname[SYS_NMLN + 1]; + struct netbuf f_addr; + } f_forw; /* forwarding address */ + char f_fname[MAXPATHLEN + 1]; + } f_un; +}; + +_NOTE(MUTEX_PROTECTS_DATA(filed::filed_mutex, filed)) +_NOTE(DATA_READABLE_WITHOUT_LOCK(filed)) + +/* values for f_type */ +#define F_UNUSED 0 /* unused entry */ +#define F_FILE 1 /* regular file */ +#define F_TTY 2 /* terminal */ +#define F_CONSOLE 3 /* console terminal */ +#define F_FORW 4 /* remote machine */ +#define F_USERS 5 /* list of users */ +#define F_WALL 6 /* everyone logged on */ + +/* + * values for logit routine + */ +#define CURRENT 0 /* print current message */ +#define SAVED 1 /* print saved message */ +/* + * values for f_msgflag + */ +#define CURRENT_VALID 0x01 /* new message is good */ +#define OLD_VALID 0x02 /* old message is valid */ + +/* + * code translation struct for use in processing config file + */ +struct code { + char *c_name; + int c_val; +}; + +/* + * structure describing a message to be sent to the wall thread. + * the thread id and attributes are stored in the structure + * passed to the thread, and the thread is created detached. + */ +typedef struct wall_device { + pthread_t thread; + pthread_attr_t thread_attr; + char dev[PATH_MAX + 1]; + char msg[MAXLINE+1]; + char ut_name[sizeof (dummy.ut_name)]; +} walldev_t; + +/* + * hostname caching struct to reduce hostname name lookup. + */ +struct hostname_cache { + struct hostname_cache *next; + struct netbuf addr; + struct netconfig *ncp; + host_list_t *h; + time_t expire; +}; + +#define DEF_HNC_SIZE 128 +#define DEF_HNC_TTL 600 /* 10 minutes */ + +/* + * function prototypes + */ +int main(int argc, char **argv); +static void usage(void); +static void untty(void); +static void formatnet(struct netbuf *nbp, log_message_t *mp); +static void formatsys(struct log_ctl *lp, char *msg, int sync); +static void *logmsg(void *ap); +static void wallmsg(struct filed *f, char *from, char *msg); +static host_list_t *cvthname(struct netbuf *nbp, struct netconfig *ncp, char *); +static void set_flush_msg(struct filed *f); +static void flushmsg(int flags); +void logerror(const char *type, ...); +static void init(void); +static void conf_init(void); +static void cfline(char *line, int lineno, struct filed *f); +static int decode(char *name, struct code *codetab); +static int ismyaddr(struct netbuf *nbp); +static void getnets(void); +static int addnet(struct netconfig *ncp, struct netbuf *nbp); +static void bindnet(void); +static int logforward(struct filed *f, char *ebuf); +static int amiloghost(void); +static int same_addr(struct netbuf *, struct netbuf *); +static void prepare_sys_poll(void); +static void *sys_poll(void *ap); +static void getkmsg(int); +static void *net_poll(void *ap); +static log_message_t *new_msg(void); +static void free_msg(log_message_t *lm); +static int logmymsg(int pri, char *msg, int flags, int); +static void *logit(void *ap); +static void freehl(host_list_t *h); +static int filed_init(struct filed *h); +static void copy_msg(struct filed *f); +static void dumpstats(int fd); +static void filter_string(char *orig, char *new, size_t max); +static int openklog(char *name, int mode); +static void writemsg(int selection, struct filed *f); +static void *writetodev(void *ap); +static int shutdown_msg(void); +static void server(void *, char *, size_t, door_desc_t *, uint_t); +static char *alloc_stacks(int); +static void dealloc_stacks(int); +static int checkm4(void); +static void filed_destroy(struct filed *f); +static void open_door(void); +static void close_door(void); +static void delete_doorfiles(void); +static void signull(int, siginfo_t *, void *); +static int putctrlc(int, char **, size_t *, size_t); +static size_t findnl_bkwd(const char *, const size_t); +static size_t copynl_frwd(char *, const size_t, const char *, const size_t); +static size_t copy_frwd(char *, const size_t, const char *, const size_t); +static void logerror_format(const char *, char *, va_list); +static int logerror_to_console(int, const char *); +static void defaults(void); +static void shutdown_input(void); +static void *hostname_lookup(void *); +static void reconfigure(void); +static void disable_errorlog(void); +static void enable_errorlog(void); + +static void hnc_init(int); +static host_list_t *hnc_lookup(struct netbuf *, struct netconfig *); +static void hnc_register(struct netbuf *, struct netconfig *, host_list_t *); +static void hnc_unreg(struct hostname_cache **); + + +#ifdef __cplusplus +} +#endif + +#endif /* _SYSLOGD_H */ diff --git a/usr/src/cmd/syslogd/system-log b/usr/src/cmd/syslogd/system-log new file mode 100644 index 0000000000..967a59a63d --- /dev/null +++ b/usr/src/cmd/syslogd/system-log @@ -0,0 +1,54 @@ +#!/sbin/sh +# +# 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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" + +. /lib/svc/share/smf_include.sh + +if [ -f /etc/syslog.conf -a -f /usr/sbin/syslogd ]; then + echo 'syslog service starting.' + if [ "${_INIT_ZONENAME:=`/sbin/zonename`}" = "global" ]; then + # + # Before syslogd starts, save any messages from + # previous crash dumps so that messages appear + # in chronological order. + # + /usr/bin/savecore -m + if [ -r /etc/dumpadm.conf ]; then + . /etc/dumpadm.conf + [ -n "$DUMPADM_DEVICE" -a \ + "x$DUMPADM_DEVICE" != xswap ] && \ + /usr/bin/savecore -m -f $DUMPADM_DEVICE + fi + fi + if [ ! -f /var/adm/messages ]; then + /usr/bin/cp /dev/null /var/adm/messages + /usr/bin/chmod 0644 /var/adm/messages + fi + /usr/sbin/syslogd >/dev/msglog 2>&1 & +else + exit $SMF_EXIT_ERR_CONFIG +fi diff --git a/usr/src/cmd/syslogd/system-log.xml b/usr/src/cmd/syslogd/system-log.xml new file mode 100644 index 0000000000..2ac7bc8140 --- /dev/null +++ b/usr/src/cmd/syslogd/system-log.xml @@ -0,0 +1,136 @@ +<?xml version="1.0"?> +<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1"> +<!-- + Copyright 2005 Sun Microsystems, Inc. All rights reserved. + Use is subject to license terms. + + 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 + + ident "%Z%%M% %I% %E% SMI" + + NOTE: This service manifest is not editable; its contents will + be overwritten by package or patch operations, including + operating system upgrade. Make customizations in a different + file. +--> + +<service_bundle type='manifest' name='SUNWcsr:syslog'> + +<service + name='system/system-log' + type='service' + version='1'> + + <create_default_instance enabled='false' /> + + <single_instance/> + + <dependency + name='milestone' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/milestone/sysconfig' /> + </dependency> + + <!-- + syslogd(1M) can log to non-root local directories. + --> + <dependency + name='filesystem' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/local' /> + </dependency> + + <!-- + syslogd(1M) can log to automounted files. + --> + <dependency + name='autofs' + grouping='optional_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/system/filesystem/autofs' /> + </dependency> + + <!-- + syslogd(1M) needs nameservice resolution to log to remote hosts. + --> + <dependency + name='name-services' + grouping='require_all' + restart_on='none' + type='service'> + <service_fmri value='svc:/milestone/name-services' /> + </dependency> + + <dependent + name='system-log_single-user' + grouping='optional_all' + restart_on='none'> + <service_fmri value='svc:/milestone/multi-user' /> + </dependent> + + <!-- + The system-log start method includes a "savecore -m". + Use an appropriately long timeout value. + --> + <exec_method + type='method' + name='start' + exec='/lib/svc/method/system-log' + timeout_seconds='600' /> + + <exec_method + type='method' + name='stop' + exec=':kill' + timeout_seconds='60' /> + + <exec_method + type='method' + name='refresh' + exec=':kill -HUP' + timeout_seconds='60' /> + + <property_group name='general' type='framework'> + <!-- to start stop syslog daemon --> + <propval name='action_authorization' type='astring' + value='solaris.smf.manage.system-log' /> + </property_group> + + <stability value='Unstable' /> + + <template> + <common_name> + <loctext xml:lang='C'> system log + </loctext> + </common_name> + <documentation> + <manpage title='syslogd' section='1M' + manpath='/usr/share/man' /> + </documentation> + </template> +</service> + +</service_bundle> |