|  | /* $OpenBSD: log.c,v 1.45 2013/05/16 09:08:41 dtucker Exp $ */ | 
|  | /* | 
|  | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 
|  | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 
|  | *                    All rights reserved | 
|  | * | 
|  | * As far as I am concerned, the code I have written for this software | 
|  | * can be used freely for any purpose.  Any derived versions of this | 
|  | * software must be clearly marked as such, and if the derived work is | 
|  | * incompatible with the protocol description in the RFC file, it must be | 
|  | * called by a name other than "ssh" or "Secure Shell". | 
|  | */ | 
|  | /* | 
|  | * Copyright (c) 2000 Markus Friedl.  All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #include "includes.h" | 
|  |  | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <fcntl.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <syslog.h> | 
|  | #include <unistd.h> | 
|  | #include <errno.h> | 
|  | #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) | 
|  | # include <vis.h> | 
|  | #endif | 
|  |  | 
|  | #include "xmalloc.h" | 
|  | #include "log.h" | 
|  |  | 
|  | static LogLevel log_level = SYSLOG_LEVEL_INFO; | 
|  | static int log_on_stderr = 1; | 
|  | static int log_stderr_fd = STDERR_FILENO; | 
|  | static int log_facility = LOG_AUTH; | 
|  | static char *argv0; | 
|  | static log_handler_fn *log_handler; | 
|  | static void *log_handler_ctx; | 
|  |  | 
|  | extern char *__progname; | 
|  |  | 
|  | #define LOG_SYSLOG_VIS	(VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL) | 
|  | #define LOG_STDERR_VIS	(VIS_SAFE|VIS_OCTAL) | 
|  |  | 
|  | /* textual representation of log-facilities/levels */ | 
|  |  | 
|  | static struct { | 
|  | const char *name; | 
|  | SyslogFacility val; | 
|  | } log_facilities[] = { | 
|  | { "DAEMON",	SYSLOG_FACILITY_DAEMON }, | 
|  | { "USER",	SYSLOG_FACILITY_USER }, | 
|  | { "AUTH",	SYSLOG_FACILITY_AUTH }, | 
|  | #ifdef LOG_AUTHPRIV | 
|  | { "AUTHPRIV",	SYSLOG_FACILITY_AUTHPRIV }, | 
|  | #endif | 
|  | { "LOCAL0",	SYSLOG_FACILITY_LOCAL0 }, | 
|  | { "LOCAL1",	SYSLOG_FACILITY_LOCAL1 }, | 
|  | { "LOCAL2",	SYSLOG_FACILITY_LOCAL2 }, | 
|  | { "LOCAL3",	SYSLOG_FACILITY_LOCAL3 }, | 
|  | { "LOCAL4",	SYSLOG_FACILITY_LOCAL4 }, | 
|  | { "LOCAL5",	SYSLOG_FACILITY_LOCAL5 }, | 
|  | { "LOCAL6",	SYSLOG_FACILITY_LOCAL6 }, | 
|  | { "LOCAL7",	SYSLOG_FACILITY_LOCAL7 }, | 
|  | { NULL,		SYSLOG_FACILITY_NOT_SET } | 
|  | }; | 
|  |  | 
|  | static struct { | 
|  | const char *name; | 
|  | LogLevel val; | 
|  | } log_levels[] = | 
|  | { | 
|  | { "QUIET",	SYSLOG_LEVEL_QUIET }, | 
|  | { "FATAL",	SYSLOG_LEVEL_FATAL }, | 
|  | { "ERROR",	SYSLOG_LEVEL_ERROR }, | 
|  | { "INFO",	SYSLOG_LEVEL_INFO }, | 
|  | { "VERBOSE",	SYSLOG_LEVEL_VERBOSE }, | 
|  | { "DEBUG",	SYSLOG_LEVEL_DEBUG1 }, | 
|  | { "DEBUG1",	SYSLOG_LEVEL_DEBUG1 }, | 
|  | { "DEBUG2",	SYSLOG_LEVEL_DEBUG2 }, | 
|  | { "DEBUG3",	SYSLOG_LEVEL_DEBUG3 }, | 
|  | { NULL,		SYSLOG_LEVEL_NOT_SET } | 
|  | }; | 
|  |  | 
|  | SyslogFacility | 
|  | log_facility_number(char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (name != NULL) | 
|  | for (i = 0; log_facilities[i].name; i++) | 
|  | if (strcasecmp(log_facilities[i].name, name) == 0) | 
|  | return log_facilities[i].val; | 
|  | return SYSLOG_FACILITY_NOT_SET; | 
|  | } | 
|  |  | 
|  | const char * | 
|  | log_facility_name(SyslogFacility facility) | 
|  | { | 
|  | u_int i; | 
|  |  | 
|  | for (i = 0;  log_facilities[i].name; i++) | 
|  | if (log_facilities[i].val == facility) | 
|  | return log_facilities[i].name; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | LogLevel | 
|  | log_level_number(char *name) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | if (name != NULL) | 
|  | for (i = 0; log_levels[i].name; i++) | 
|  | if (strcasecmp(log_levels[i].name, name) == 0) | 
|  | return log_levels[i].val; | 
|  | return SYSLOG_LEVEL_NOT_SET; | 
|  | } | 
|  |  | 
|  | const char * | 
|  | log_level_name(LogLevel level) | 
|  | { | 
|  | u_int i; | 
|  |  | 
|  | for (i = 0; log_levels[i].name != NULL; i++) | 
|  | if (log_levels[i].val == level) | 
|  | return log_levels[i].name; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | /* Error messages that should be logged. */ | 
|  |  | 
|  | void | 
|  | error(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_ERROR, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void | 
|  | sigdie(const char *fmt,...) | 
|  | { | 
|  | #ifdef DO_LOG_SAFE_IN_SIGHAND | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_FATAL, fmt, args); | 
|  | va_end(args); | 
|  | #endif | 
|  | _exit(1); | 
|  | } | 
|  |  | 
|  |  | 
|  | /* Log this message (information that usually should go to the log). */ | 
|  |  | 
|  | void | 
|  | logit(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_INFO, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | /* More detailed messages (information that does not need to go to the log). */ | 
|  |  | 
|  | void | 
|  | verbose(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_VERBOSE, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | /* Debugging messages that should not be logged during normal operation. */ | 
|  |  | 
|  | void | 
|  | debug(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_DEBUG1, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void | 
|  | debug2(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_DEBUG2, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void | 
|  | debug3(const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(SYSLOG_LEVEL_DEBUG3, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Initialize the log. | 
|  | */ | 
|  |  | 
|  | void | 
|  | log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) | 
|  | { | 
|  | #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) | 
|  | struct syslog_data sdata = SYSLOG_DATA_INIT; | 
|  | #endif | 
|  |  | 
|  | argv0 = av0; | 
|  |  | 
|  | switch (level) { | 
|  | case SYSLOG_LEVEL_QUIET: | 
|  | case SYSLOG_LEVEL_FATAL: | 
|  | case SYSLOG_LEVEL_ERROR: | 
|  | case SYSLOG_LEVEL_INFO: | 
|  | case SYSLOG_LEVEL_VERBOSE: | 
|  | case SYSLOG_LEVEL_DEBUG1: | 
|  | case SYSLOG_LEVEL_DEBUG2: | 
|  | case SYSLOG_LEVEL_DEBUG3: | 
|  | log_level = level; | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, "Unrecognized internal syslog level code %d\n", | 
|  | (int) level); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | log_handler = NULL; | 
|  | log_handler_ctx = NULL; | 
|  |  | 
|  | log_on_stderr = on_stderr; | 
|  | if (on_stderr) | 
|  | return; | 
|  |  | 
|  | switch (facility) { | 
|  | case SYSLOG_FACILITY_DAEMON: | 
|  | log_facility = LOG_DAEMON; | 
|  | break; | 
|  | case SYSLOG_FACILITY_USER: | 
|  | log_facility = LOG_USER; | 
|  | break; | 
|  | case SYSLOG_FACILITY_AUTH: | 
|  | log_facility = LOG_AUTH; | 
|  | break; | 
|  | #ifdef LOG_AUTHPRIV | 
|  | case SYSLOG_FACILITY_AUTHPRIV: | 
|  | log_facility = LOG_AUTHPRIV; | 
|  | break; | 
|  | #endif | 
|  | case SYSLOG_FACILITY_LOCAL0: | 
|  | log_facility = LOG_LOCAL0; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL1: | 
|  | log_facility = LOG_LOCAL1; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL2: | 
|  | log_facility = LOG_LOCAL2; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL3: | 
|  | log_facility = LOG_LOCAL3; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL4: | 
|  | log_facility = LOG_LOCAL4; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL5: | 
|  | log_facility = LOG_LOCAL5; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL6: | 
|  | log_facility = LOG_LOCAL6; | 
|  | break; | 
|  | case SYSLOG_FACILITY_LOCAL7: | 
|  | log_facility = LOG_LOCAL7; | 
|  | break; | 
|  | default: | 
|  | fprintf(stderr, | 
|  | "Unrecognized internal syslog facility code %d\n", | 
|  | (int) facility); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If an external library (eg libwrap) attempts to use syslog | 
|  | * immediately after reexec, syslog may be pointing to the wrong | 
|  | * facility, so we force an open/close of syslog here. | 
|  | */ | 
|  | #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) | 
|  | openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); | 
|  | closelog_r(&sdata); | 
|  | #else | 
|  | openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); | 
|  | closelog(); | 
|  | #endif | 
|  | } | 
|  |  | 
|  | void | 
|  | log_change_level(LogLevel new_log_level) | 
|  | { | 
|  | /* no-op if log_init has not been called */ | 
|  | if (argv0 == NULL) | 
|  | return; | 
|  | log_init(argv0, new_log_level, log_facility, log_on_stderr); | 
|  | } | 
|  |  | 
|  | int | 
|  | log_is_on_stderr(void) | 
|  | { | 
|  | return log_on_stderr; | 
|  | } | 
|  |  | 
|  | /* redirect what would usually get written to stderr to specified file */ | 
|  | void | 
|  | log_redirect_stderr_to(const char *logfile) | 
|  | { | 
|  | int fd; | 
|  |  | 
|  | if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) { | 
|  | fprintf(stderr, "Couldn't open logfile %s: %s\n", logfile, | 
|  | strerror(errno)); | 
|  | exit(1); | 
|  | } | 
|  | log_stderr_fd = fd; | 
|  | } | 
|  |  | 
|  | #define MSGBUFSIZ 1024 | 
|  |  | 
|  | void | 
|  | set_log_handler(log_handler_fn *handler, void *ctx) | 
|  | { | 
|  | log_handler = handler; | 
|  | log_handler_ctx = ctx; | 
|  | } | 
|  |  | 
|  | void | 
|  | do_log2(LogLevel level, const char *fmt,...) | 
|  | { | 
|  | va_list args; | 
|  |  | 
|  | va_start(args, fmt); | 
|  | do_log(level, fmt, args); | 
|  | va_end(args); | 
|  | } | 
|  |  | 
|  | void | 
|  | do_log(LogLevel level, const char *fmt, va_list args) | 
|  | { | 
|  | #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) | 
|  | struct syslog_data sdata = SYSLOG_DATA_INIT; | 
|  | #endif | 
|  | char msgbuf[MSGBUFSIZ]; | 
|  | char fmtbuf[MSGBUFSIZ]; | 
|  | char *txt = NULL; | 
|  | int pri = LOG_INFO; | 
|  | int saved_errno = errno; | 
|  | log_handler_fn *tmp_handler; | 
|  |  | 
|  | if (level > log_level) | 
|  | return; | 
|  |  | 
|  | switch (level) { | 
|  | case SYSLOG_LEVEL_FATAL: | 
|  | if (!log_on_stderr) | 
|  | txt = "fatal"; | 
|  | pri = LOG_CRIT; | 
|  | break; | 
|  | case SYSLOG_LEVEL_ERROR: | 
|  | if (!log_on_stderr) | 
|  | txt = "error"; | 
|  | pri = LOG_ERR; | 
|  | break; | 
|  | case SYSLOG_LEVEL_INFO: | 
|  | pri = LOG_INFO; | 
|  | break; | 
|  | case SYSLOG_LEVEL_VERBOSE: | 
|  | pri = LOG_INFO; | 
|  | break; | 
|  | case SYSLOG_LEVEL_DEBUG1: | 
|  | txt = "debug1"; | 
|  | pri = LOG_DEBUG; | 
|  | break; | 
|  | case SYSLOG_LEVEL_DEBUG2: | 
|  | txt = "debug2"; | 
|  | pri = LOG_DEBUG; | 
|  | break; | 
|  | case SYSLOG_LEVEL_DEBUG3: | 
|  | txt = "debug3"; | 
|  | pri = LOG_DEBUG; | 
|  | break; | 
|  | default: | 
|  | txt = "internal error"; | 
|  | pri = LOG_ERR; | 
|  | break; | 
|  | } | 
|  | if (txt != NULL && log_handler == NULL) { | 
|  | snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); | 
|  | vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); | 
|  | } else { | 
|  | vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); | 
|  | } | 
|  | strnvis(fmtbuf, msgbuf, sizeof(fmtbuf), | 
|  | log_on_stderr ? LOG_STDERR_VIS : LOG_SYSLOG_VIS); | 
|  | if (log_handler != NULL) { | 
|  | /* Avoid recursion */ | 
|  | tmp_handler = log_handler; | 
|  | log_handler = NULL; | 
|  | tmp_handler(level, fmtbuf, log_handler_ctx); | 
|  | log_handler = tmp_handler; | 
|  | } else if (log_on_stderr) { | 
|  | snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf); | 
|  | (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); | 
|  | } else { | 
|  | #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) | 
|  | openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); | 
|  | syslog_r(pri, &sdata, "%.500s", fmtbuf); | 
|  | closelog_r(&sdata); | 
|  | #else | 
|  | openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); | 
|  | syslog(pri, "%.500s", fmtbuf); | 
|  | closelog(); | 
|  | #endif | 
|  | } | 
|  | errno = saved_errno; | 
|  | } |