/* * This file is part of DisOrder. * Copyright (C) 2004-2008 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** @file lib/log.c @brief Errors and logging * * All messages are initially emitted by one of the four functions below. * debug() is generally invoked via D() so that mostly you just do a test * rather than a complete subroutine call. * * Messages are dispatched via @ref log_default. This defaults to @ref * log_stderr. daemonize() will turn off @ref log_stderr and use @ref * log_syslog instead. * * fatal() will call exitfn() with a nonzero status. The default value is * exit(), but it should be set to _exit() anywhere but the 'main line' of the * program, to guarantee that exit() gets called at most once. */ #define NO_MEMORY_ALLOCATION /* because the memory allocation functions report errors */ #include "common.h" #include #include #include #include "log.h" #include "disorder.h" #include "printf.h" /** @brief Definition of a log output */ struct log_output { /** @brief Function to call */ void (*fn)(int pri, const char *msg, void *user); /** @brief User data */ void *user; }; /** @brief Function to call on a fatal error * * This is normally @c exit() but in the presence of @c fork() it * sometimes gets set to @c _exit(). */ void (*exitfn)(int) attribute((noreturn)) = exit; /** @brief Debug flag */ int debugging; /** @brief Program name */ const char *progname; /** @brief Filename for debug messages */ const char *debug_filename; /** @brief Line number for debug messages */ int debug_lineno; /** @brief Pointer to chosen log output structure */ struct log_output *log_default = &log_stderr; /** @brief Filename to debug for */ static const char *debug_only; /** @brief Construct log line, encoding special characters * * We might be receiving things in any old encoding, or binary rubbish * in no encoding at all, so escape anything we don't like the look * of. We limit the log message to a kilobyte. */ static void format(char buffer[], size_t bufsize, const char *fmt, va_list ap) { char t[1024]; const char *p; int ch; size_t n = 0; if(byte_vsnprintf(t, sizeof t, fmt, ap) < 0) { strcpy(t, "[byte_vsnprintf failed: "); strncat(t, fmt, sizeof t - strlen(t) - 1); } p = t; while((ch = (unsigned char)*p++)) { if(ch >= ' ' && ch <= 126) { if(n < bufsize) buffer[n++] = ch; } else { if(n < bufsize) buffer[n++] = '\\'; if(n < bufsize) buffer[n++] = '0' + ((ch >> 6) & 7); if(n < bufsize) buffer[n++] = '0' + ((ch >> 3) & 7); if(n < bufsize) buffer[n++] = '0' + ((ch >> 0) & 7); } } if(n >= bufsize) n = bufsize - 1; buffer[n] = 0; } /** @brief Log to a file * @param pri Message priority (as per syslog) * @param msg Messagge to log * @param user The @c FILE @c * to log to or NULL for @c stderr */ static void logfp(int pri, const char *msg, void *user) { struct timeval tv; FILE *fp = user ? user : stderr; /* ...because stderr is not a constant so we can't initialize log_stderr * sanely */ const char *p; if(progname) fprintf(fp, "%s: ", progname); if(pri <= LOG_ERR) fputs("ERROR: ", fp); else if(pri < LOG_DEBUG) fputs("INFO: ", fp); else { if(!debug_only) { if(!(debug_only = getenv("DISORDER_DEBUG_ONLY"))) debug_only = ""; } gettimeofday(&tv, 0); p = debug_filename; while(!strncmp(p, "../", 3)) p += 3; if(*debug_only && strcmp(p, debug_only)) return; fprintf(fp, "%llu.%06lu: %s:%d: ", (unsigned long long)tv.tv_sec, (unsigned long)tv.tv_usec, p, debug_lineno); } fputs(msg, fp); fputc('\n', fp); } /** @brief Log to syslog */ static void logsyslog(int pri, const char *msg, void attribute((unused)) *user) { if(pri < LOG_DEBUG) syslog(pri, "%s", msg); else syslog(pri, "%s:%d: %s", debug_filename, debug_lineno, msg); } /** @brief Log output that writes to @c stderr */ struct log_output log_stderr = { logfp, 0 }; /** @brief Log output that sends to syslog */ struct log_output log_syslog = { logsyslog, 0 }; /** @brief Format and log a message */ static void vlogger(int pri, const char *fmt, va_list ap) { char buffer[1024]; format(buffer, sizeof buffer, fmt, ap); log_default->fn(pri, buffer, log_default->user); } /** @brief Format and log a message */ static void logger(int pri, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vlogger(pri, fmt, ap); va_end(ap); } /** @brief Format and log a message * @param pri Message priority (as per syslog) * @param fmt Format string * @param errno_value Errno value to include as a string, or 0 * @param ap Argument list */ void elog(int pri, int errno_value, const char *fmt, va_list ap) { char buffer[1024]; if(errno_value == 0) vlogger(pri, fmt, ap); else { memset(buffer, 0, sizeof buffer); byte_vsnprintf(buffer, sizeof buffer, fmt, ap); buffer[sizeof buffer - 1] = 0; logger(pri, "%s: %s", buffer, strerror(errno_value)); } } #define disorder_fatal fatal #define disorder_error error #define disorder_info info /* shared implementation of vararg functions */ #include "log-impl.h" /** @brief Log a debug message */ void debug(const char *msg, ...) { va_list ap; va_start(ap, msg); vlogger(LOG_DEBUG, msg, ap); va_end(ap); } /** @brief Set the program name from @c argc */ void set_progname(char **argv) { if((progname = strrchr(argv[0], '/'))) ++progname; else progname = argv[0]; } /* Local Variables: c-basic-offset:2 comment-column:40 End: */