/* tinyproxy - A fast light-weight HTTP proxy * Copyright (C) 1998 Steven Young * Copyright (C) 1999 Robert James Kaes * Copyright (C) 2009 Michael Adam * * 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 2 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* Logs the various messages which tinyproxy produces to either a log file * or the syslog daemon. Not much to it... */ #include "main.h" #include "heap.h" #include "log.h" #include "utils.h" #include "sblist.h" #include "conf.h" #include static const char *syslog_level[] = { NULL, NULL, "CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG", "CONNECT" }; #define TIME_LENGTH 16 #define STRING_LENGTH 800 static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; /* * Global file descriptor for the log file */ int log_file_fd = -1; /* * Store the log level setting. */ static int log_level = LOG_INFO; /* * Hold a listing of log messages which need to be sent once the log * file has been established. * The key is the actual messages (already filled in full), and the value * is the log level. */ static sblist *log_message_storage; static unsigned int logging_initialized = FALSE; /* boolean */ /* * Open the log file and store the file descriptor in a global location. */ int open_log_file (const char *log_file_name) { if (log_file_name == NULL) { log_file_fd = fileno(stdout); } else { log_file_fd = create_file_safely (log_file_name, FALSE); } return log_file_fd; } /* * Close the log file */ void close_log_file (void) { if (log_file_fd < 0 || log_file_fd == fileno(stdout)) { return; } close (log_file_fd); log_file_fd = -1; } /* * Set the log level for writing to the log file. */ void set_log_level (int level) { log_level = level; } /* * This routine logs messages to either the log file or the syslog function. */ void log_message (int level, const char *fmt, ...) { va_list args; struct timespec nowtime; struct tm tm_buf; char time_string[TIME_LENGTH]; char str[STRING_LENGTH]; ssize_t ret; #ifdef NDEBUG /* * Figure out if we should write the message or not. */ if (log_level == LOG_CONN) { if (level == LOG_INFO) return; } else if (log_level == LOG_INFO) { if (level > LOG_INFO && level != LOG_CONN) return; } else if (level > log_level) return; #endif if (config && config->syslog && level == LOG_CONN) level = LOG_INFO; va_start (args, fmt); /* * If the config file hasn't been processed, then we need to store * the messages for later processing. */ if (!logging_initialized) { char *entry_buffer; if (!log_message_storage) { log_message_storage = sblist_new (sizeof(char*), 64); if (!log_message_storage) goto out; } vsnprintf (str, STRING_LENGTH, fmt, args); entry_buffer = (char *) safemalloc (strlen (str) + 6); if (!entry_buffer) goto out; sprintf (entry_buffer, "%d %s", level, str); if(!sblist_add (log_message_storage, &entry_buffer)) safefree (entry_buffer); goto out; } if(!config->syslog && log_file_fd == -1) goto out; if (config->syslog) { pthread_mutex_lock(&log_mutex); #ifdef HAVE_VSYSLOG_H vsyslog (level, fmt, args); #else vsnprintf (str, STRING_LENGTH, fmt, args); syslog (level, "%s", str); #endif pthread_mutex_unlock(&log_mutex); } else { char *p; clock_gettime(CLOCK_REALTIME, &nowtime); /* Format is month day hour:minute:second (24 time) */ strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S", localtime_r (&nowtime.tv_sec, &tm_buf)); snprintf (str, STRING_LENGTH, "%-9s %s.%03lu [%ld]: ", syslog_level[level], time_string, (unsigned long) nowtime.tv_nsec/1000000ul, (long int) getpid ()); /* * Overwrite the '\0' and leave room for a trailing '\n' * be added next. */ p = str + strlen(str); vsnprintf (p, STRING_LENGTH - strlen(str) - 1, fmt, args); p = str + strlen(str); *p = '\n'; *(p+1) = '\0'; assert (log_file_fd >= 0); pthread_mutex_lock(&log_mutex); ret = write (log_file_fd, str, strlen (str)); pthread_mutex_unlock(&log_mutex); if (ret == -1) { config->syslog = TRUE; log_message(LOG_CRIT, "ERROR: Could not write to log " "file %s: %s.", config->logf_name, strerror(errno)); log_message(LOG_CRIT, "Falling back to syslog logging"); } pthread_mutex_lock(&log_mutex); fsync (log_file_fd); pthread_mutex_unlock(&log_mutex); } out: va_end (args); } /* * This needs to send any stored log messages. */ static void send_stored_logs (void) { char **string; char *ptr; int level; size_t i; if (log_message_storage == NULL) return; log_message(LOG_DEBUG, "sending stored logs"); for (i = 0; i < sblist_getsize (log_message_storage); ++i) { string = sblist_get (log_message_storage, i); if (!string || !*string) continue; ptr = strchr (*string, ' ') + 1; level = atoi (*string); #ifdef NDEBUG if (log_level == LOG_CONN && level == LOG_INFO) continue; else if (log_level == LOG_INFO) { if (level > LOG_INFO && level != LOG_CONN) continue; } else if (level > log_level) continue; #endif log_message (level, "%s", ptr); safefree(*string); } sblist_free (log_message_storage); log_message_storage = NULL; log_message(LOG_DEBUG, "done sending stored logs"); } /** * Initialize the logging subsystem, based on the configuration. * Returns 0 upon success, -1 upon failure. * * This function uses fprintf() instead of log_message(), since * the logging is not yet set up... */ int setup_logging (void) { if (!config->syslog) { if (open_log_file (config->logf_name) < 0) { /* * If opening the log file fails, we try * to fall back to syslog logging... */ config->syslog = TRUE; log_message (LOG_CRIT, "ERROR: Could not create log " "file %s: %s.", config->logf_name, strerror (errno)); log_message (LOG_CRIT, "Falling back to syslog logging."); } } if (config->syslog) { openlog ("tinyproxy", LOG_PID, LOG_USER); } logging_initialized = TRUE; send_stored_logs (); return 0; } /** * Stop the logging subsystem. */ void shutdown_logging (void) { if (!logging_initialized) { return; } if (config->syslog) { closelog (); } else { close_log_file (); } logging_initialized = FALSE; }