X-Git-Url: http://git.silcnet.org/gitweb/?p=runtime.git;a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilclog.c;h=e857bac29789cf85c96cd5fe7e794b18c5913faa;hp=a0c9181a7f72a93a8f2b80931aa3ea11b05f057c;hb=607af8ce2c5dced2b5ca5c7c1d815fc80fbb2603;hpb=ff91a22e073349fc5339ec55d65392c23fdcb6db diff --git a/lib/silcutil/silclog.c b/lib/silcutil/silclog.c index a0c9181a..e857bac2 100644 --- a/lib/silcutil/silclog.c +++ b/lib/silcutil/silclog.c @@ -2,293 +2,554 @@ silclog.c - Author: Pekka Riikonen + Author: Pekka Riikonen - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2008 Pekka Riikonen 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. - + the Free Software Foundation; version 2 of the License. + 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. */ -/* $Id$ */ -#include "silcincludes.h" +#include "silcruntime.h" + +/* SilcLogSettings context */ +typedef struct { + SilcUInt32 flushdelay; + + char debug_string[128]; + SilcLogDebugCb debug_cb; + void *debug_context; + SilcLogHexdumpCb hexdump_cb; + void *hexdump_context; + + unsigned int timestamp : 1; + unsigned int quick : 1; + unsigned int debug : 1; + unsigned int debug_hexdump : 1; + unsigned int scheduled : 1; + unsigned int no_init : 1; + unsigned int starting : 1; +} *SilcLogSettings, SilcLogSettingsStruct; + +/* SilcLog context */ +typedef struct { + char filename[256]; + FILE *fp; + SilcUInt64 maxsize; + const char *typename; + SilcLogType type; + SilcLogCb cb; + void *context; +} *SilcLog, SilcLogStruct; -/* Set TRUE/FALSE to enable/disable debugging */ -int silc_debug = FALSE; +#ifndef SILC_SYMBIAN -/* SILC Log name strings. These strings are printed to the log file. */ -const SilcLogTypeName silc_log_types[] = +/* Default settings */ +static SilcLogSettingsStruct silclog = { - { "Info", SILC_LOG_INFO }, - { "Warning", SILC_LOG_WARNING }, - { "Error", SILC_LOG_ERROR }, - { "Fatal", SILC_LOG_FATAL }, + 300, + { 0 }, + NULL, NULL, + NULL, NULL, + TRUE, + FALSE, + FALSE, + FALSE, + FALSE, + FALSE, + TRUE, +}; + +#endif /* !SILC_SYMBIAN */ - { NULL, -1 }, +/* Default log contexts */ +#ifndef SILC_SYMBIAN +static SilcLogStruct silclogs[4] = +#else +const SilcLogStruct silclogs[4] = +#endif /* !SILC_SYMBIAN */ +{ + {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL}, + {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL}, + {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL}, + {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL}, }; -char *log_info_file; -char *log_warning_file; -char *log_error_file; -char *log_fatal_file; -uint32 log_info_size; -uint32 log_warning_size; -uint32 log_error_size; -uint32 log_fatal_size; - -/* Log callbacks. If these are set by the application these are used - instead of the default functions in this file. */ -static SilcLogCb info_cb = NULL; -static SilcLogCb warning_cb = NULL; -static SilcLogCb error_cb = NULL; -static SilcLogCb fatal_cb = NULL; - -/* Debug callbacks. If set these are used instead of default ones. */ -static SilcDebugCb debug_cb = NULL; -static SilcDebugHexdumpCb debug_hexdump_cb = NULL; - -/* Outputs the log message to what ever log file selected. */ - -void silc_log_output(const char *filename, uint32 maxsize, - SilcLogType type, char *string) +/* Return log context by type */ + +static SilcLog silc_log_get_context(SilcLogType type) { - FILE *fp; - const SilcLogTypeName *np; - - switch(type) - { - case SILC_LOG_INFO: - if (info_cb) { - (*info_cb)(string); - silc_free(string); - return; - } - break; - case SILC_LOG_WARNING: - if (warning_cb) { - (*warning_cb)(string); - silc_free(string); - return; - } - break; - case SILC_LOG_ERROR: - if (error_cb) { - (*error_cb)(string); - silc_free(string); - return; - } - break; - case SILC_LOG_FATAL: - if (fatal_cb) { - (*fatal_cb)(string); - silc_free(string); - return; - } - break; - } + if (type < 1 || type > 4) + return NULL; + return (SilcLog)&silclogs[(int)type - 1]; +} - if (!filename) - filename = " "; - - /* Purge the log file if the max size is defined. */ - if (maxsize) { - fp = fopen(filename, "r"); - if (fp) { - int filelen; - - filelen = fseek(fp, (off_t)0L, SEEK_END); - fseek(fp, (off_t)0L, SEEK_SET); - - /* Purge? */ - if (filelen >= maxsize) - unlink(filename); - - fclose(fp); - } - } +/* Check log file site and cycle log file if it is over max size. */ - /* Open the log file */ - if ((fp = fopen(filename, "a+")) == NULL) { - fprintf(stderr, "warning: could not open log file " - "%s: %s\n", filename, strerror(errno)); - fprintf(stderr, "warning: log messages will be displayed on the screen\n"); - fp = stderr; - } - - /* Get the log type name */ - for(np = silc_log_types; np->name; np++) { - if (np->type == type) - break; +static void silc_log_checksize(SilcLog log) +{ + char newname[256]; + SilcUInt64 size; + + if (!log || !log->fp || !log->maxsize) + return; + + size = silc_file_size(log->filename); + if (!size) { + fclose(log->fp); + log->fp = NULL; } - fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), np->name, string); - fflush(fp); - if (fp != stderr) - fclose(fp); - silc_free(string); + if (size < log->maxsize) + return; + + /* Cycle log file */ + fprintf(log->fp, + "[%s] [%s] Cycling log file, over max log size (%lu kilobytes)\n", + silc_time_string(0), log->typename, + (unsigned long)log->maxsize / 1024); + fflush(log->fp); + fclose(log->fp); + + memset(newname, 0, sizeof(newname)); + silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename); + unlink(newname); + rename(log->filename, newname); + + log->fp = fopen(log->filename, "w"); + if (!log->fp) + SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s", + log->filename, log->typename, strerror(errno))); +#ifdef HAVE_CHMOD + chmod(log->filename, 0600); +#endif /* HAVE_CHMOD */ } -/* Outputs the debug message to stderr. */ +/* Internal timeout callback to flush log channels and check file sizes */ -void silc_log_output_debug(char *file, char *function, - int line, char *string) +SILC_TASK_CALLBACK(silc_log_fflush_callback) { - if (!silc_debug) { - silc_free(string); - return; +#ifndef SILC_SYMBIAN + SilcLog log; + + if (!silclog.quick) { + silc_log_flush_all(); + log = silc_log_get_context(SILC_LOG_INFO); + silc_log_checksize(log); + log = silc_log_get_context(SILC_LOG_WARNING); + silc_log_checksize(log); + log = silc_log_get_context(SILC_LOG_ERROR); + silc_log_checksize(log); + log = silc_log_get_context(SILC_LOG_FATAL); + silc_log_checksize(log); } - if (debug_cb) - { - (*debug_cb)(file, function, line, string); - silc_free(string); - return; - } + silclog.starting = FALSE; - fprintf(stderr, "%s:%d: %s\n", function, line, string); - fflush(stderr); - silc_free(string); + if (silclog.flushdelay < 2) + silclog.flushdelay = 2; + silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context, + silclog.flushdelay, 0); +#endif /* !SILC_SYMBIAN */ } -/* Hexdumps a message */ +/* Output log message to log file */ -void silc_log_output_hexdump(char *file, char *function, - int line, void *data_in, - uint32 len, char *string) +void silc_log_output(SilcLogType type, char *string) { - int i, k; - int off, pos, count; - unsigned char *data = (unsigned char *)data_in; + const char *typename = NULL; + SilcLog log = silc_log_get_context(type); + FILE *fp; - if (!silc_debug) { - silc_free(string); - return; + if (!log) + goto end; + + /* Forward to callback if set */ + if (log->cb) + if ((*log->cb)(type, string, log->context)) + goto end; + + typename = log->typename; + +#ifndef SILC_SYMBIAN + if (!silclog.scheduled) { + if (silclog.no_init == FALSE) { + fprintf(stderr, + "Warning, log files not initialized, " + "log output is going to stderr\n"); + silclog.no_init = TRUE; + } + + fp = stderr; + log = NULL; + goto found; } +#endif /* !SILC_SYMBIAN */ - if (debug_hexdump_cb) - { - (*debug_hexdump_cb)(file, function, line, data_in, len, string); - silc_free(string); - return; + /* Find open log file */ + while (log) { + if (log->fp) { + fp = log->fp; + break; } - fprintf(stderr, "%s:%d: %s\n", function, line, string); + log = silc_log_get_context(--type); + } + if (!log || !log->fp) + goto end; + +#ifndef SILC_SYMBIAN + found: + if (silclog.timestamp) + fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string); + else + fprintf(fp, "[%s] %s\n", typename, string); + + if (silclog.quick || silclog.starting) { + fflush(fp); + if (log) + silc_log_checksize(log); + } +#endif /* !SILC_SYMBIAN */ + + end: +#ifndef SILC_SYMBIAN + /* Output log to stderr if debugging */ + if (typename && silclog.debug) { + fprintf(stderr, "[Logging] [%s] %s\n", typename, string); + fflush(stderr); + } +#else + fprintf(stderr, "[Logging] [%s] %s\n", typename, string); +#endif /* !SILC_SYMBIAN */ + silc_free(string); +} - k = 0; - off = len % 16; - pos = 0; - count = 16; - while (1) { - - if (off) { - if ((len - pos) < 16 && (len - pos <= len - off)) - count = off; - } else { - if (pos == len) - count = 0; +/* Set and initialize the specified log file. */ + +SilcBool silc_log_set_file(SilcLogType type, char *filename, + SilcUInt32 maxsize, SilcSchedule scheduler) +{ +#ifndef SILC_SYMBIAN + FILE *fp = NULL; + SilcLog log; + + if (!scheduler) + scheduler = silc_schedule_get_global(); + + log = silc_log_get_context(type); + if (!log) + return FALSE; + + SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)", + log->typename, filename, maxsize)); + + /* Open log file */ + if (filename) { + fp = fopen(filename, "a+"); + if (!fp) { + fprintf(stderr, "warning: couldn't open log file '%s': %s\n", + filename, strerror(errno)); + return FALSE; } - if (off == len) - count = len; +#ifdef HAVE_CHMOD + chmod(filename, 0600); +#endif /* HAVE_CHMOD */ + } - if (count) - fprintf(stderr, "%08X ", k++ * 16); + /* Close previous log file if it exists */ + if (strlen(log->filename)) { + if (log->fp) + fclose(log->fp); + memset(log->filename, 0, sizeof(log->filename)); + log->fp = NULL; + } - for (i = 0; i < count; i++) { - fprintf(stderr, "%02X ", data[pos + i]); - - if ((i + 1) % 4 == 0) - fprintf(stderr, " "); - } + /* Set new log file */ + if (fp) { + log->fp = fp; + log->maxsize = maxsize; - if (count && count < 16) { - int j; - - for (j = 0; j < 16 - count; j++) { - fprintf(stderr, " "); + memset(log->filename, 0, sizeof(log->filename)); + silc_strncat(log->filename, sizeof(log->filename), filename, + strlen(filename)); + } - if ((j + count + 1) % 4 == 0) - fprintf(stderr, " "); - } - } - - for (i = 0; i < count; i++) { - char ch; - - if (data[pos] < 32 || data[pos] >= 127) - ch = '.'; - else - ch = data[pos]; - - fprintf(stderr, "%c", ch); - pos++; - } + /* Add flush timeout */ + if (scheduler) { + silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback); + silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback, + scheduler, 10, 0); + silclog.scheduled = TRUE; + } - if (count) - fprintf(stderr, "\n"); +#endif /* !SILC_SYMBIAN */ + return TRUE; +} - if (count < 16) - break; +/* Return log filename */ + +char *silc_log_get_file(SilcLogType type) +{ + SilcLog log = silc_log_get_context(type); + return log && log->fp ? log->filename : NULL; +} + +/* Sets a log callback, set callback to NULL to return to default behaviour */ + +void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context) +{ +#ifndef SILC_SYMBIAN + SilcLog log = silc_log_get_context(type); + if (log) { + log->cb = cb; + log->context = context; } +#endif /* !SILC_SYMBIAN */ +} + +/* Reset log callbacks */ + +void silc_log_reset_callbacks(void) +{ +#ifndef SILC_SYMBIAN + SilcLog log; + log = silc_log_get_context(SILC_LOG_INFO); + log->cb = log->context = NULL; + log = silc_log_get_context(SILC_LOG_WARNING); + log->cb = log->context = NULL; + log = silc_log_get_context(SILC_LOG_ERROR); + log->cb = log->context = NULL; + log = silc_log_get_context(SILC_LOG_FATAL); + log->cb = log->context = NULL; +#endif /* !SILC_SYMBIAN */ } -/* Sets log files */ +/* Flush all log files */ -void silc_log_set_files(char *info, uint32 info_size, - char *warning, uint32 warning_size, - char *error, uint32 error_size, - char *fatal, uint32 fatal_size) +void silc_log_flush_all(void) { - log_info_file = info; - log_warning_file = warning; - log_error_file = error; - log_fatal_file = fatal; - - log_info_size = info_size; - log_warning_size = warning_size; - log_error_size = error_size; - log_fatal_size = fatal_size; + SilcLog log; + log = silc_log_get_context(SILC_LOG_INFO); + if (log->fp) + fflush(log->fp); + log = silc_log_get_context(SILC_LOG_WARNING); + if (log->fp) + fflush(log->fp); + log = silc_log_get_context(SILC_LOG_ERROR); + if (log->fp) + fflush(log->fp); + log = silc_log_get_context(SILC_LOG_FATAL); + if (log->fp) + fflush(log->fp); } -/* Sets log callbacks */ +/* Reset a log file */ -void silc_log_set_callbacks(SilcLogCb info, SilcLogCb warning, - SilcLogCb error, SilcLogCb fatal) +static void silc_log_reset(SilcLog log) { - info_cb = info; - warning_cb = warning; - error_cb = error; - fatal_cb = fatal; + if (log->fp) { + fflush(log->fp); + fclose(log->fp); + } + + if (!strlen(log->filename)) + return; + + log->fp = fopen(log->filename, "a+"); + if (!log->fp) + SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s", + log->filename, log->typename, strerror(errno))); } -/* Resets log callbacks */ +/* Reset all log files */ -void silc_log_reset_callbacks() +void silc_log_reset_all(void) { - info_cb = warning_cb = error_cb = fatal_cb = NULL; + SilcLog log; + log = silc_log_get_context(SILC_LOG_INFO); + if (log->fp) + silc_log_reset(log); + log = silc_log_get_context(SILC_LOG_WARNING); + if (log->fp) + silc_log_reset(log); + log = silc_log_get_context(SILC_LOG_ERROR); + if (log->fp) + silc_log_reset(log); + log = silc_log_get_context(SILC_LOG_FATAL); + if (log->fp) + silc_log_reset(log); + silc_log_flush_all(); } /* Sets debug callbacks */ -void silc_log_set_debug_callbacks(SilcDebugCb debug, - SilcDebugHexdumpCb debug_hexdump) +void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb, + void *debug_context, + SilcLogHexdumpCb hexdump_cb, + void *hexdump_context) { - debug_cb = debug; - debug_hexdump_cb = debug_hexdump; +#ifndef SILC_SYMBIAN + silclog.debug_cb = debug_cb; + silclog.debug_context = debug_context; + silclog.hexdump_cb = hexdump_cb; + silclog.hexdump_context = hexdump_context; +#endif /* !SILC_SYMBIAN */ } /* Resets debug callbacks */ void silc_log_reset_debug_callbacks() { - debug_cb = NULL; - debug_hexdump_cb = NULL; +#ifndef SILC_SYMBIAN + silclog.debug_cb = NULL; + silclog.debug_context = NULL; + silclog.hexdump_cb = NULL; + silclog.hexdump_context = NULL; +#endif /* !SILC_SYMBIAN */ +} + +/* Set current debug string */ + +void silc_log_set_debug_string(const char *debug_string) +{ +#ifndef SILC_SYMBIAN + char *string; + int len; + if ((strchr(debug_string, '(') && strchr(debug_string, ')')) || + strchr(debug_string, '$')) + string = silc_strdup(debug_string); + else + string = silc_string_regexify(debug_string); + len = strlen(string); + if (len >= sizeof(silclog.debug_string)) + len = sizeof(silclog.debug_string) - 1; + memset(silclog.debug_string, 0, sizeof(silclog.debug_string)); + strncpy(silclog.debug_string, string, len); + silc_free(string); +#endif /* !SILC_SYMBIAN */ +} + +/* Set timestamp */ + +void silc_log_timestamp(SilcBool enable) +{ +#ifndef SILC_SYMBIAN + silclog.timestamp = enable; +#endif /* !SILC_SYMBIAN */ +} + +/* Set flushdelay */ + +void silc_log_flushdelay(SilcUInt32 flushdelay) +{ +#ifndef SILC_SYMBIAN + silclog.flushdelay = flushdelay; +#endif /* !SILC_SYMBIAN */ +} + +/* Set quick logging */ + +void silc_log_quick(SilcBool enable) +{ +#ifndef SILC_SYMBIAN + silclog.quick = enable; +#endif /* !SILC_SYMBIAN */ +} + +/* Set debugging */ + +void silc_log_debug(SilcBool enable) +{ +#ifndef SILC_SYMBIAN + silclog.debug = enable; +#endif /* !SILC_SYMBIAN */ +} + +/* Set debug hexdump */ + +void silc_log_debug_hexdump(SilcBool enable) +{ +#ifndef SILC_SYMBIAN + silclog.debug_hexdump = enable; +#endif /* !SILC_SYMBIAN */ +} + +/* Outputs the debug message to stderr. */ + +void silc_log_output_debug(char *file, const char *function, + int line, char *string) +{ + SilcTimeStruct curtime; + +#ifndef SILC_SYMBIAN + if (!silclog.debug) + goto end; + + if (!silc_string_regex_match(silclog.debug_string, file) && + !silc_string_regex_match(silclog.debug_string, function)) + goto end; + + if (silclog.debug_cb) { + if ((*silclog.debug_cb)(file, (char *)function, line, string, + silclog.debug_context)) + goto end; + } +#endif /* !SILC_SYMBIAN */ + + silc_time_value(0, &curtime); + +#ifdef SILC_WIN32 + if (strrchr(function, '\\')) + fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string); + else +#endif /* SILC_WIN32 */ +#ifdef SILC_SYMBIAN + silc_symbian_debug(function, line, string); +#else + fprintf(stderr, "%02d:%02d:%02d.%03d %s:%d: %s\n", curtime.hour, + curtime.minute, curtime.second, curtime.msecond, function, line, + string); + fflush(stderr); +#endif /* SILC_SYMBIAN */ + + end: + silc_free(string); +} + +/* Hexdumps a message */ + +void silc_log_output_hexdump(char *file, const char *function, + int line, void *data_in, + SilcUInt32 len, char *string) +{ +#ifndef SILC_SYMBIAN + if (!silclog.debug_hexdump) + goto end; + + if (!silc_string_regex_match(silclog.debug_string, file) && + !silc_string_regex_match(silclog.debug_string, function)) + goto end; + + if (silclog.hexdump_cb) { + if ((*silclog.hexdump_cb)(file, (char *)function, line, + data_in, len, string, silclog.hexdump_context)) + goto end; + } +#endif /* !SILC_SYMBIAN */ + + fprintf(stderr, "%s:%d: %s\n", function, line, string); + + silc_hexdump(data_in, len, stderr); + + end: + silc_free(string); }