/* * 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 */ /* * PPPoE Server-mode daemon log file support. * * Copyright (c) 2000-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "logging.h" /* Not all functions are used by all applications. Let lint know this. */ /*LINTLIBRARY*/ const char *prog_name = "none"; /* Subsystem name for syslog */ int log_level; /* Higher number for more detail. */ static int curlogfd = -1; /* Current log file */ static const char *curfname; /* Name of current log file */ static const char *stderr_name = "stderr"; #define SMALLSTR 254 /* Don't allocate for most strings. */ /* * Returns -1 on error (with errno set), 0 on blocked write (file * system full), or N (buffer length) on success. */ static int dowrite(int fd, const void *buf, int len) { int retv; const uint8_t *bp = (uint8_t *)buf; while (len > 0) { retv = write(fd, bp, len); if (retv == 0) { break; } if (retv == -1) { if (errno != EINTR) break; } else { bp += retv; len -= retv; } } if (len <= 0) return (bp - (uint8_t *)buf); return (retv); } /* A close that avoids closing stderr */ static int doclose(void) { int retval = 0; if (curlogfd == -1) return (0); if ((curlogfd != STDERR_FILENO) || (curfname != stderr_name)) retval = close(curlogfd); curlogfd = -1; return (retval); } /* * Log levels are 0 for no messages, 1 for errors, 2 for warnings, 3 * for informational messages, and 4 for debugging messages. */ static void vlogat(int loglev, const char *fmt, va_list args) { char timbuf[64]; char regbuf[SMALLSTR+2]; char *ostr; int timlen; int slen; char *nstr; int err1, err2; int sloglev; int retv; va_list args2; static int xlate_loglev[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG }; if (loglev >= log_level) return; timbuf[0] = '\0'; timlen = 0; if (curlogfd >= 0) { time_t now = time(NULL); /* * Form a time/date string for file (non-syslog) logging. * Caution: string broken in two so that SCCS doesn't mangle * the %-T-% sequence. */ timlen = strftime(timbuf, sizeof (timbuf), "%Y/%m/%d %T" "%Z: ", localtime(&now)); } /* Try formatting once into the small buffer. */ va_copy(args2, args); slen = vsnprintf(regbuf, SMALLSTR, fmt, args); if (slen < SMALLSTR) { ostr = regbuf; } else { /* * Length returned by vsnprintf doesn't include null, * and may also be missing a terminating \n. */ ostr = alloca(slen + 2); slen = vsnprintf(ostr, slen + 1, fmt, args2); } /* Don't bother logging empty lines. */ if (slen <= 0) return; /* Tack on a \n if needed. */ if (ostr[slen - 1] != '\n') { ostr[slen++] = '\n'; ostr[slen] = '\0'; } /* Translate our log levels into syslog standard values */ assert(loglev >= 0 && loglev < Dim(xlate_loglev)); sloglev = xlate_loglev[loglev]; /* Log each line separately */ for (; *ostr != '\0'; ostr = nstr + 1) { nstr = strchr(ostr, '\n'); /* Ignore zero-length lines. */ if (nstr == ostr) continue; slen = nstr - ostr + 1; /* * If we're supposed to be logging to a file, then try * that first. Ditch the file and revert to syslog if * any errors occur. */ if (curlogfd >= 0) { if ((retv = dowrite(curlogfd, timbuf, timlen)) > 0) retv = dowrite(curlogfd, ostr, slen); /* * If we've successfully logged this line, * then go do the next one. */ if (retv > 0) continue; /* Save errno (if any) and close log file */ err1 = errno; if (doclose() == -1) err2 = errno; else err2 = 0; /* * Recursion is safe here because we cleared * out curlogfd above. */ if (retv == -1) logerr("write log %s: %s", curfname, mystrerror(err1)); else logerr("cannot write %s", curfname); if (err2 == 0) logdbg("closed log %s", curfname); else logerr("closing log %s: %s", curfname, mystrerror(err2)); } syslog(sloglev, "%.*s", slen, ostr); } } /* Log at debug level */ void logdbg(const char *fmt, ...) { va_list args; va_start(args, fmt); vlogat(LOGLVL_DBG, fmt, args); va_end(args); } /* Log informational messages */ void loginfo(const char *fmt, ...) { va_list args; va_start(args, fmt); vlogat(LOGLVL_INFO, fmt, args); va_end(args); } /* Log warning messages */ void logwarn(const char *fmt, ...) { va_list args; va_start(args, fmt); vlogat(LOGLVL_WARN, fmt, args); va_end(args); } /* Log error messages */ void logerr(const char *fmt, ...) { va_list args; va_start(args, fmt); vlogat(LOGLVL_ERR, fmt, args); va_end(args); } /* Log a strerror message */ void logstrerror(const char *emsg) { logerr("%s: %s\n", emsg, mystrerror(errno)); } void log_to_stderr(int dbglvl) { log_level = dbglvl; if (curlogfd >= 0) close_log_files(); curlogfd = STDERR_FILENO; curfname = stderr_name; } /* * Set indicated log file and debug level. */ void log_for_service(const char *fname, int dbglvl) { int err1, err2; boolean_t closed; log_level = dbglvl; if (fname != NULL && (*fname == '\0' || strcasecmp(fname, "syslog") == 0)) fname = NULL; if (fname == NULL && curfname == NULL) return; err1 = err2 = 0; closed = B_FALSE; if (curlogfd >= 0) { if (fname == curfname || (fname != NULL && strcmp(fname, curfname) == 0)) { curfname = fname; return; } if (doclose() == -1) err1 = errno; closed = B_TRUE; } if (fname != NULL) { curlogfd = open(fname, O_WRONLY|O_APPEND|O_CREAT, 0600); if (curlogfd == -1) err2 = errno; } if (closed) { if (err1 == 0) logdbg("closed log %s", curfname); else logerr("closing log %s: %s", curfname, mystrerror(err1)); } if (fname != NULL) { if (err2 == 0) logdbg("opened log %s", fname); else logerr("opening log %s: %s", fname, mystrerror(err2)); } curfname = fname; } /* * Close any open log file. This is used for SIGHUP (to support log * file rotation) and when execing. */ void close_log_files(void) { int err = 0; if (curlogfd >= 0) { if (doclose() == -1) err = errno; if (err == 0) logdbg("closed log %s", curfname); else logerr("closing log %s: %s", curfname, mystrerror(err)); } } /* * Reopen syslog connection; in case it was closed. */ void reopen_log(void) { openlog(prog_name, LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON); /* I control the log level */ (void) setlogmask(LOG_UPTO(LOG_DEBUG)); }