2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 1989, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/* Copyright (c) 1988 AT&T */
2N/A/* All Rights Reserved */
2N/A
2N/A/*
2N/A * University Copyright- Copyright (c) 1982, 1986, 1988
2N/A * The Regents of the University of California
2N/A * All Rights Reserved
2N/A *
2N/A * University Acknowledgment- Portions of this document are derived from
2N/A * software developed by the University of California, Berkeley, and its
2N/A * contributors.
2N/A */
2N/A
2N/A
2N/A/*
2N/A * SYSLOG -- print message on log file
2N/A *
2N/A * This routine looks a lot like printf, except that it
2N/A * outputs to the log file instead of the standard output.
2N/A * Also:
2N/A * adds a timestamp,
2N/A * prints the module name in front of the message,
2N/A * has some other formatting types (or will sometime),
2N/A * adds a newline on the end of the message.
2N/A *
2N/A * The output of this routine is intended to be read by /etc/syslogd.
2N/A */
2N/A
2N/A#pragma weak _syslog = syslog
2N/A
2N/A#include "lint.h"
2N/A#include <sys/types.h>
2N/A#include <sys/types32.h>
2N/A#include <sys/mman.h>
2N/A#include <sys/stropts.h>
2N/A#include <sys/strlog.h>
2N/A#include <sys/log.h> /* for LOG_MAXPS */
2N/A#include <stdlib.h>
2N/A#include <procfs.h>
2N/A#include <syslog.h>
2N/A#include <signal.h>
2N/A#include <fcntl.h>
2N/A#include <string.h>
2N/A#include <stdarg.h>
2N/A#include <unistd.h>
2N/A#include <wait.h>
2N/A#include <stdio.h>
2N/A#include <string.h>
2N/A#include <errno.h>
2N/A#include <thread.h>
2N/A#include <synch.h>
2N/A#include <sys/door.h>
2N/A#include <sys/stat.h>
2N/A#include <stropts.h>
2N/A#include <sys/fork.h>
2N/A#include <sys/wait.h>
2N/A#include <paths.h>
2N/A#include "libc.h"
2N/A
2N/A#define MAXLINE 1024 /* max message size (but see below) */
2N/A
2N/A#define PRIMASK(p) (1 << ((p) & LOG_PRIMASK))
2N/A#define PRIFAC(p) (((p) & LOG_FACMASK) >> 3)
2N/A#define IMPORTANT LOG_ERR
2N/A
2N/A#ifndef FALSE
2N/A#define FALSE 0
2N/A#endif
2N/A
2N/A#ifndef TRUE
2N/A#define TRUE 1
2N/A#endif
2N/A
2N/A#define logname "/dev/conslog"
2N/A#define ctty "/dev/syscon"
2N/A#define sysmsg "/dev/sysmsg"
2N/A
2N/A#define DOORFILE _PATH_SYSVOL "/syslog_door"
2N/A
2N/Astatic struct __syslog {
2N/A int _LogFile;
2N/A int _LogStat;
2N/A const char *_LogTag;
2N/A int _LogMask;
2N/A char *_SyslogHost;
2N/A int _LogFacility;
2N/A int _LogFileInvalid;
2N/A int _OpenLogCalled;
2N/A dev_t _LogDev;
2N/A char _ProcName[PRFNSZ + 1];
2N/A} __syslog = {
2N/A -1, /* fd for log */
2N/A 0, /* status bits, set by openlog() */
2N/A "syslog", /* string to tag the entry with */
2N/A 0xff, /* mask of priorities to be logged */
2N/A NULL,
2N/A LOG_USER, /* default facility code */
2N/A FALSE, /* check for validity of fd for log */
2N/A 0, /* openlog has not yet been called */
2N/A};
2N/A
2N/A#define LogFile (__syslog._LogFile)
2N/A#define LogStat (__syslog._LogStat)
2N/A#define LogTag (__syslog._LogTag)
2N/A#define LogMask (__syslog._LogMask)
2N/A#define SyslogHost (__syslog._SyslogHost)
2N/A#define LogFacility (__syslog._LogFacility)
2N/A#define LogFileInvalid (__syslog._LogFileInvalid)
2N/A#define OpenLogCalled (__syslog._OpenLogCalled)
2N/A#define LogDev (__syslog._LogDev)
2N/A#define ProcName (__syslog._ProcName)
2N/A
2N/Astatic int syslogd_ok(void);
2N/A
2N/A/*
2N/A * Regrettably, there are several instances inside libc where
2N/A * syslog() is called from the bottom of a deep call stack
2N/A * and a critical lock was acquired near the top of the stack.
2N/A *
2N/A * Because syslog() uses stdio (and it is called from within stdio)
2N/A * it runs the danger of deadlocking, perhaps with an interposed
2N/A * malloc() when fork() is occurring concurrently, perhaps with
2N/A * some other lock within libc.
2N/A *
2N/A * The only fix for this problem is to restructure libc not to do
2N/A * this thing and always to call syslog() with no locks held.
2N/A * This restructuring will require a substantial effort.
2N/A *
2N/A * Meanwhile, we just hope that on the rare occasion that syslog()
2N/A * is called from within libc (such occurrences should "never happen")
2N/A * that we don't get caught in a race condition deadlock.
2N/A */
2N/Avoid
2N/Asyslog(int pri, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A
2N/A va_start(ap, fmt);
2N/A vsyslog(pri, fmt, ap);
2N/A va_end(ap);
2N/A}
2N/A
2N/A
2N/Avoid
2N/Avsyslog(int pri, const char *fmt, va_list ap)
2N/A{
2N/A char *b, *f, *o;
2N/A char c;
2N/A int clen;
2N/A char buf[MAXLINE + 2];
2N/A char outline[MAXLINE + 256]; /* pad to allow date, system name... */
2N/A time_t now;
2N/A pid_t pid;
2N/A struct log_ctl hdr;
2N/A struct strbuf dat;
2N/A struct strbuf ctl;
2N/A char timestr[26]; /* hardwired value 26 due to Posix */
2N/A size_t taglen;
2N/A int olderrno = errno;
2N/A struct stat statbuff;
2N/A int procfd;
2N/A char procfile[32];
2N/A psinfo_t p;
2N/A int showpid;
2N/A uint32_t msgid;
2N/A char *msgid_start, *msgid_end;
2N/A int nowait;
2N/A
2N/A/*
2N/A * Maximum tag length is 256 (the pad in outline) minus the size of the
2N/A * other things that can go in the pad.
2N/A */
2N/A#define MAX_TAG 230
2N/A
2N/A /* see if we should just throw out this message */
2N/A if (pri < 0 || PRIFAC(pri) >= LOG_NFACILITIES ||
2N/A (PRIMASK(pri) & LogMask) == 0)
2N/A return;
2N/A
2N/A if (LogFileInvalid)
2N/A return;
2N/A
2N/A /*
2N/A * if openlog() has not been called by the application,
2N/A * try to get the name of the application and set it
2N/A * as the ident string for messages. If unable to get
2N/A * it for any reason, fall back to using the default
2N/A * of syslog. If we succeed in getting the name, also
2N/A * turn on LOG_PID, to provide greater detail.
2N/A */
2N/A showpid = 0;
2N/A if (OpenLogCalled == 0) {
2N/A (void) sprintf(procfile, "/proc/%d/psinfo", (int)getpid());
2N/A if ((procfd = open(procfile, O_RDONLY)) >= 0) {
2N/A if (read(procfd, &p, sizeof (psinfo_t)) >= 0) {
2N/A (void) strncpy(ProcName, p.pr_fname, PRFNSZ);
2N/A LogTag = (const char *) &ProcName;
2N/A showpid = LOG_PID;
2N/A }
2N/A (void) close(procfd);
2N/A }
2N/A }
2N/A if (LogFile < 0)
2N/A openlog(LogTag, LogStat|LOG_NDELAY|showpid, 0);
2N/A
2N/A if ((fstat(LogFile, &statbuff) != 0) ||
2N/A (!S_ISCHR(statbuff.st_mode)) || (statbuff.st_rdev != LogDev)) {
2N/A LogFileInvalid = TRUE;
2N/A return;
2N/A }
2N/A
2N/A /* set default facility if none specified */
2N/A if ((pri & LOG_FACMASK) == 0)
2N/A pri |= LogFacility;
2N/A
2N/A /* build the header */
2N/A hdr.pri = pri;
2N/A hdr.flags = SL_CONSOLE;
2N/A hdr.level = 0;
2N/A
2N/A /* build the message */
2N/A /*
2N/A * To avoid potential security problems, bounds checking is done
2N/A * on outline and buf.
2N/A * The following code presumes that the header information will
2N/A * fit in 250-odd bytes, as was accounted for in the buffer size
2N/A * allocation. This is dependent on the assumption that the LogTag
2N/A * and the string returned by sprintf() for getpid() will return
2N/A * be less than 230-odd characters combined.
2N/A */
2N/A o = outline;
2N/A (void) time(&now);
2N/A (void) sprintf(o, "%.15s ", ctime_r(&now, timestr, 26) + 4);
2N/A o += strlen(o);
2N/A
2N/A if (LogTag) {
2N/A taglen = strlen(LogTag) < MAX_TAG ? strlen(LogTag) : MAX_TAG;
2N/A (void) strncpy(o, LogTag, taglen);
2N/A o[taglen] = '\0';
2N/A o += strlen(o);
2N/A }
2N/A if (LogStat & LOG_PID) {
2N/A (void) sprintf(o, "[%d]", (int)getpid());
2N/A o += strlen(o);
2N/A }
2N/A if (LogTag) {
2N/A (void) strcpy(o, ": ");
2N/A o += 2;
2N/A }
2N/A
2N/A STRLOG_MAKE_MSGID(fmt, msgid);
2N/A (void) sprintf(o, "[ID %u FACILITY_AND_PRIORITY] ", msgid);
2N/A o += strlen(o);
2N/A
2N/A b = buf;
2N/A f = (char *)fmt;
2N/A while ((c = *f++) != '\0' && b < &buf[MAXLINE]) {
2N/A char *errmsg;
2N/A if (c != '%') {
2N/A *b++ = c;
2N/A continue;
2N/A }
2N/A if ((c = *f++) != 'm') {
2N/A *b++ = '%';
2N/A *b++ = c;
2N/A continue;
2N/A }
2N/A if ((errmsg = strerror(olderrno)) == NULL)
2N/A (void) snprintf(b, &buf[MAXLINE] - b, "error %d",
2N/A olderrno);
2N/A else {
2N/A while (*errmsg != '\0' && b < &buf[MAXLINE]) {
2N/A if (*errmsg == '%') {
2N/A (void) strcpy(b, "%%");
2N/A b += 2;
2N/A }
2N/A else
2N/A *b++ = *errmsg;
2N/A errmsg++;
2N/A }
2N/A *b = '\0';
2N/A }
2N/A b += strlen(b);
2N/A }
2N/A if (b > buf && *(b-1) != '\n') /* ensure at least one newline */
2N/A *b++ = '\n';
2N/A *b = '\0';
2N/A /* LINTED variable format specifier */
2N/A (void) vsnprintf(o, &outline[sizeof (outline)] - o, buf, ap);
2N/A clen = (int)strlen(outline) + 1; /* add one for NULL byte */
2N/A if (clen > MAXLINE) {
2N/A clen = MAXLINE;
2N/A outline[MAXLINE-1] = '\0';
2N/A }
2N/A
2N/A /*
2N/A * 1136432 points out that the underlying log driver actually
2N/A * refuses to accept (ERANGE) messages longer than LOG_MAXPS
2N/A * bytes. So it really doesn't make much sense to putmsg a
2N/A * longer message..
2N/A */
2N/A if (clen > LOG_MAXPS) {
2N/A clen = LOG_MAXPS;
2N/A outline[LOG_MAXPS-1] = '\0';
2N/A }
2N/A
2N/A /* set up the strbufs */
2N/A ctl.maxlen = sizeof (struct log_ctl);
2N/A ctl.len = sizeof (struct log_ctl);
2N/A ctl.buf = (caddr_t)&hdr;
2N/A dat.maxlen = sizeof (outline);
2N/A dat.len = clen;
2N/A dat.buf = outline;
2N/A
2N/A /* output the message to the local logger */
2N/A if ((putmsg(LogFile, &ctl, &dat, 0) >= 0) && syslogd_ok())
2N/A return;
2N/A if (!(LogStat & LOG_CONS))
2N/A return;
2N/A
2N/A /*
2N/A * Output the message to the console directly. To reduce visual
2N/A * clutter, we strip out the message ID.
2N/A */
2N/A if ((msgid_start = strstr(outline, "[ID ")) != NULL &&
2N/A (msgid_end = strstr(msgid_start, "] ")) != NULL)
2N/A (void) strcpy(msgid_start, msgid_end + 2);
2N/A
2N/A clen = strlen(outline) + 1;
2N/A
2N/A nowait = (LogStat & LOG_NOWAIT);
2N/A pid = forkx(nowait? 0 : (FORK_NOSIGCHLD | FORK_WAITPID));
2N/A if (pid == -1)
2N/A return;
2N/A
2N/A if (pid == 0) {
2N/A sigset_t sigs;
2N/A int fd;
2N/A
2N/A (void) sigset(SIGALRM, SIG_DFL);
2N/A (void) sigemptyset(&sigs);
2N/A (void) sigaddset(&sigs, SIGALRM);
2N/A (void) sigprocmask(SIG_UNBLOCK, &sigs, NULL);
2N/A (void) alarm(5);
2N/A if (((fd = open(sysmsg, O_WRONLY)) >= 0) ||
2N/A (fd = open(ctty, O_WRONLY)) >= 0) {
2N/A (void) alarm(0);
2N/A outline[clen - 1] = '\r';
2N/A (void) write(fd, outline, clen);
2N/A (void) close(fd);
2N/A }
2N/A _exit(0);
2N/A }
2N/A if (!nowait)
2N/A while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
2N/A continue;
2N/A}
2N/A
2N/A/*
2N/A * Use a door call to syslogd to see if it's alive.
2N/A */
2N/Astatic int
2N/Asyslogd_ok(void)
2N/A{
2N/A int d;
2N/A int s;
2N/A door_arg_t darg;
2N/A door_info_t info;
2N/A
2N/A if ((d = open(DOORFILE, O_RDONLY)) < 0)
2N/A return (0);
2N/A /*
2N/A * see if our pid matches the pid of the door server.
2N/A * If so, syslogd has called syslog(), probably as
2N/A * a result of some name service library error, and
2N/A * we don't want to let syslog continue and possibly
2N/A * fork here.
2N/A */
2N/A info.di_target = 0;
2N/A if (__door_info(d, &info) < 0 || info.di_target == getpid()) {
2N/A (void) close(d);
2N/A return (0);
2N/A }
2N/A darg.data_ptr = NULL;
2N/A darg.data_size = 0;
2N/A darg.desc_ptr = NULL;
2N/A darg.desc_num = 0;
2N/A darg.rbuf = NULL;
2N/A darg.rsize = 0;
2N/A s = __door_call(d, &darg);
2N/A (void) close(d);
2N/A if (s < 0)
2N/A return (0); /* failure - syslogd dead */
2N/A else
2N/A return (1);
2N/A}
2N/A
2N/A/*
2N/A * OPENLOG -- open system log
2N/A */
2N/A
2N/Avoid
2N/Aopenlog(const char *ident, int logstat, int logfac)
2N/A{
2N/A struct stat statbuff;
2N/A
2N/A OpenLogCalled = 1;
2N/A if (ident != NULL)
2N/A LogTag = ident;
2N/A LogStat = logstat;
2N/A if (logfac != 0)
2N/A LogFacility = logfac & LOG_FACMASK;
2N/A
2N/A /*
2N/A * if the fstat(2) fails or the st_rdev has changed
2N/A * then we must open the file
2N/A */
2N/A if ((fstat(LogFile, &statbuff) == 0) &&
2N/A (S_ISCHR(statbuff.st_mode)) && (statbuff.st_rdev == LogDev))
2N/A return;
2N/A
2N/A if (LogStat & LOG_NDELAY) {
2N/A LogFile = open(logname, O_WRONLY);
2N/A (void) fcntl(LogFile, F_SETFD, 1);
2N/A (void) fstat(LogFile, &statbuff);
2N/A LogDev = statbuff.st_rdev;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * CLOSELOG -- close the system log
2N/A */
2N/A
2N/Avoid
2N/Acloselog(void)
2N/A{
2N/A struct stat statbuff;
2N/A
2N/A OpenLogCalled = 0;
2N/A
2N/A /* if the LogFile is invalid it can not be closed */
2N/A if (LogFileInvalid)
2N/A return;
2N/A
2N/A /*
2N/A * if the fstat(2) fails or the st_rdev has changed
2N/A * then we can not close the file
2N/A */
2N/A if ((fstat(LogFile, &statbuff) == 0) && (statbuff.st_rdev == LogDev)) {
2N/A (void) close(LogFile);
2N/A LogFile = -1;
2N/A LogStat = 0;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * SETLOGMASK -- set the log mask level
2N/A */
2N/Aint
2N/Asetlogmask(int pmask)
2N/A{
2N/A int omask = 0;
2N/A
2N/A omask = LogMask;
2N/A if (pmask != 0)
2N/A LogMask = pmask;
2N/A return (omask);
2N/A}