X-Git-Url: http://git.silcnet.org/gitweb/?a=blobdiff_plain;f=lib%2Fsilcutil%2Fsilclog.c;h=b415c664d1eecc3ed45b0b6829da2895f859c206;hb=a818c5b5411bbc4436d1c5f011236985c96bb787;hp=243ce20e8792902746ebbb0622c39f59c86a278e;hpb=4d35af3be05cacf69ca4bd634973cdcd25118e98;p=silc.git diff --git a/lib/silcutil/silclog.c b/lib/silcutil/silclog.c index 243ce20e..b415c664 100644 --- a/lib/silcutil/silclog.c +++ b/lib/silcutil/silclog.c @@ -2,15 +2,15 @@ silclog.c - Author: Pekka Riikonen + Author: Johnny Mnemonic - Copyright (C) 1997 - 2000 Pekka Riikonen + Copyright (C) 1997 - 2002 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. - + 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 @@ -21,170 +21,379 @@ #include "silcincludes.h" -/* Set TRUE/FALSE to enable/disable debugging */ -int silc_debug = FALSE; +/* Minimum time delay for log flushing calls (in seconds) */ +#define SILC_LOG_FLUSH_MIN_DELAY 2 -/* SILC Log name strings. These strings are printed to the log file. */ -const SilcLogTypeName silc_log_types[] = -{ - { "Info", SILC_LOG_INFO }, - { "Warning", SILC_LOG_WARNING }, - { "Error", SILC_LOG_ERROR }, - { "Fatal", SILC_LOG_FATAL }, +/* nice macro for looping through all logs -- makes the code more readable */ +#define SILC_FOREACH_LOG(__x__) for (__x__ = 0; __x__ < SILC_LOG_MAX; __x__++) - { NULL, -1 }, +/* Our working struct -- at the moment we keep it private, but this could + * change in the future */ +struct SilcLogStruct { + char *filename; + FILE *fp; + SilcUInt32 maxsize; + char *typename; + SilcLogType type; + SilcLogCb cb; + void *context; }; +typedef struct SilcLogStruct *SilcLog; + +/* These are the known logging channels */ +static struct SilcLogStruct silclogs[SILC_LOG_MAX] = { + {NULL, NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL}, + {NULL, NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL}, + {NULL, NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL}, + {NULL, NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL}, +}; + +/* If TRUE, log files will be flushed for each log input */ +bool silc_log_quick = FALSE; + +/* Set TRUE/FALSE to enable/disable debugging */ +bool silc_debug = FALSE; +bool silc_debug_hexdump = FALSE; -char *log_info_file; -char *log_warning_file; -char *log_error_file; -char *log_fatal_file; -unsigned int log_info_size; -unsigned int log_warning_size; -unsigned int log_error_size; -unsigned int 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; +/* Flush delay */ +long silc_log_flushdelay = 300; + +/* Regular pattern matching expression for the debug output */ +char *silc_log_debug_string = NULL; /* Debug callbacks. If set these are used instead of default ones. */ -static SilcDebugCb debug_cb = NULL; -static SilcDebugHexdumpCb debug_hexdump_cb = NULL; +static SilcLogDebugCb silc_log_debug_cb = NULL; +static void *silc_log_debug_context = NULL; +static SilcLogHexdumpCb silc_log_hexdump_cb = NULL; +static void *silc_log_hexdump_context = NULL; -/* Outputs the log message to what ever log file selected. */ +/* Did we register already our functions to the scheduler? */ +static bool silc_log_scheduled = FALSE; +static bool silc_log_no_init = FALSE; -void silc_log_output(const char *filename, unsigned int maxsize, - SilcLogType type, char *string) -{ - FILE *fp; - const SilcLogTypeName *np; +/* This is only needed during starting up -- don't lose any logging */ +static bool silc_log_starting = TRUE; - switch(type) - { +/* The type wrapper utility. Translates a SilcLogType id to the corresponding + * logfile, or NULL if not found. */ +static SilcLog silc_log_find_by_type(SilcLogType type) +{ + /* this is not really needed, but i think it's more secure */ + switch (type) { case SILC_LOG_INFO: - if (info_cb) { - (*info_cb)(string); - silc_free(string); - return; - } - break; + return &silclogs[SILC_LOG_INFO]; case SILC_LOG_WARNING: - if (warning_cb) { - (*warning_cb)(string); - silc_free(string); - return; - } - break; + return &silclogs[SILC_LOG_WARNING]; case SILC_LOG_ERROR: - if (error_cb) { - (*error_cb)(string); - silc_free(string); - return; - } - break; + return &silclogs[SILC_LOG_ERROR]; case SILC_LOG_FATAL: - if (fatal_cb) { - (*fatal_cb)(string); - silc_free(string); - return; - } - break; - } + return &silclogs[SILC_LOG_FATAL]; + default: + return NULL; + } + return NULL; +} - /* 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); - } +/* Given an open log file, checks the size and rotates it if there is a + * max size set less then the current size */ +static void silc_log_checksize(SilcLog log) +{ + char newname[127]; + long size; + + if (!log || !log->fp || !log->maxsize) + return; /* we are not interested */ + if ((size = ftell(log->fp)) < 0) { + /* OMG, EBADF is here.. we'll try our best.. */ + FILE *oldfp = log->fp; + fclose(oldfp); /* we can discard the error */ + log->fp = NULL; /* make sure we don't get here recursively */ + SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%d", + log->filename, oldfp)); + return; + } + if (size < log->maxsize) return; + + /* It's too big */ + fprintf(log->fp, "[%s] [%s] Cycling log file, over max " + "logsize (%lu kilobytes)\n", + silc_get_time(), log->typename, log->maxsize / 1024); + fflush(log->fp); + fclose(log->fp); + snprintf(newname, sizeof(newname), "%s.old", log->filename); + unlink(newname); + + /* I heard the following syscall may cause portability issues, but I don't + * have any other solution since SILC library doesn't provide any other + * function like this. -Johnny */ + rename(log->filename, newname); + if (!(log->fp = fopen(log->filename, "w"))) + SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s", + log->filename, log->typename, strerror(errno))); +} + +/* Reset a logging channel (close and reopen) */ + +static bool silc_log_reset(SilcLog log) +{ + if (!log) return FALSE; + if (log->fp) { + fflush(log->fp); + fclose(log->fp); } + if (!log->filename) return FALSE; + if (!(log->fp = fopen(log->filename, "a+"))) { + SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s", + log->filename, log->typename, strerror(errno))); + return FALSE; + } + return TRUE; +} + +/* Internal timeout callback to flush log channels and check file sizes */ + +SILC_TASK_CALLBACK(silc_log_fflush_callback) +{ + unsigned int u; + if (!silc_log_quick) { + silc_log_flush_all(); + SILC_FOREACH_LOG(u) + silc_log_checksize(&silclogs[u]); + } + silc_log_starting = FALSE; + if (silc_log_flushdelay < SILC_LOG_FLUSH_MIN_DELAY) + silc_log_flushdelay = SILC_LOG_FLUSH_MIN_DELAY; + silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback, + context, silc_log_flushdelay, 0, SILC_TASK_TIMEOUT, + SILC_TASK_PRI_NORMAL); +} + +/* Outputs the log message to the first available channel. Channels are + * ordered by importance (see SilcLogType documentation). + * More importants channels can be printed on less important ones, but not + * vice-versa. */ - /* 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"); +void silc_log_output(SilcLogType type, char *string) +{ + char *typename; + FILE *fp; + SilcLog log; + + if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type))) + goto end; + + /* If there is a custom callback set, use it and return. */ + if (log->cb) { + if ((*log->cb)(type, string, log->context)) + goto end; + } + + /* save the original typename, because if we redirect the channel we + * keep however the original destination channel name */ + typename = log->typename; + + if (!silc_log_scheduled) { + if (silc_log_no_init == FALSE) { + fprintf(stderr, + "Warning, trying to output without log files initialization, " + "log output is going to stderr\n"); + silc_log_no_init = TRUE; + } + /* redirect output */ fp = stderr; + log = NULL; + goto found; } - - /* Get the log type name */ - for(np = silc_log_types; np->name; np++) { - if (np->type == type) - break; + + /* ok, now we have to find an open stream */ + while (TRUE) { + if (log && (fp = log->fp)) goto found; + if (type == 0) break; + log = silc_log_find_by_type(--type); + } + + /* Couldn't find any open stream.. sorry :( */ + SILC_LOG_DEBUG(("Warning! couldn't find any available log channel!")); + goto end; + + found: + fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), typename, string); + if (silc_log_quick || silc_log_starting) { + fflush(fp); + if (log) + silc_log_checksize(log); } - fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), np->name, string); - fflush(fp); - fclose(fp); + end: silc_free(string); } -/* Outputs the debug message to stderr. */ +/* returns an internally allocated pointer to a logging channel file */ +char *silc_log_get_file(SilcLogType type) +{ + SilcLog log; -void silc_log_output_debug(char *file, char *function, - int line, char *string) + if (!(log = silc_log_find_by_type(type))) + return NULL; + if (log->fp) + return log->filename; + return NULL; +} + +/* Set and initialize the specified logging channel. See the API reference */ +bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize, + SilcSchedule scheduler) { - if (!silc_debug) { - silc_free(string); - return; + FILE *fp = NULL; + SilcLog log; + + log = silc_log_find_by_type(type); + if (!log || !scheduler) + return FALSE; + + SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)", + log->typename, filename, maxsize)); + + /* before assuming the new file, make sure we can open it */ + if (filename) { + if (!(fp = fopen(filename, "a+"))) { + fprintf(stderr, "warning: couldn't open log file %s: %s\n", + filename, strerror(errno)); + return FALSE; + } } - if (debug_cb) - { - (*debug_cb)(file, function, line, string); - silc_free(string); - return; + /* remove old file */ + if (log->filename) { + if (log->fp) { + fflush(fp); + fclose(fp); } + silc_free(log->filename); + log->filename = NULL; + log->fp = NULL; + } + + if (fp) { + log->filename = strdup(filename); + log->fp = fp; + log->maxsize = maxsize; + } + + if (silc_log_scheduled) + return TRUE; + + /* add schedule hook with a short delay to make sure we'll use right delay */ + silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback, + (void *) scheduler, 10, 0, + SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL); + + silc_log_scheduled = TRUE; + + return TRUE; +} + +/* Sets a log callback, set callback to NULL to return to default behaviour */ + +void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context) +{ + SilcLog log; + + if (!(log = silc_log_find_by_type(type))) + return; + + log->cb = cb; + log->context = context; +} + +/* Resets log callbacks */ + +void silc_log_reset_callbacks() +{ + unsigned int u; + SILC_FOREACH_LOG(u) { + silclogs[u].cb = NULL; + silclogs[u].context = NULL; + } +} + +/* Flushes all opened logging channels */ + +void silc_log_flush_all() { + unsigned int u; + SILC_LOG_DEBUG(("Flushing all logs")); + SILC_FOREACH_LOG(u) { + if (silclogs[u].fp) + fflush(silclogs[u].fp); + } +} + +/* Resets all known logging channels */ + +void silc_log_reset_all() { + unsigned int u; + SILC_LOG_DEBUG(("Resetting all logs")); + SILC_FOREACH_LOG(u) + silc_log_reset(&silclogs[u]); +} + +/* Outputs the debug message to stderr. */ + +void silc_log_output_debug(char *file, char *function, + int line, char *string) +{ + if (!silc_debug) + goto end; + + if (silc_log_debug_string && + !silc_string_regex_match(silc_log_debug_string, file) && + !silc_string_regex_match(silc_log_debug_string, function)) + goto end; + + if (silc_log_debug_cb) { + if ((*silc_log_debug_cb)(file, function, line, string, + silc_log_debug_context)) + goto end; + } - /* fprintf(stderr, "%s:%s:%d: %s\n", file, function, line, string); */ fprintf(stderr, "%s:%d: %s\n", function, line, string); fflush(stderr); + + end: silc_free(string); } /* Hexdumps a message */ -void silc_log_output_hexdump(char *file, char *function, +void silc_log_output_hexdump(char *file, char *function, int line, void *data_in, - unsigned int len, char *string) + SilcUInt32 len, char *string) { int i, k; int off, pos, count; unsigned char *data = (unsigned char *)data_in; - if (!silc_debug) { - silc_free(string); - return; - } + if (!silc_debug_hexdump) + goto end; - if (debug_hexdump_cb) - { - (*debug_hexdump_cb)(file, function, line, data_in, len, string); - silc_free(string); - return; - } + if (silc_log_debug_string && + !silc_string_regex_match(silc_log_debug_string, file) && + !silc_string_regex_match(silc_log_debug_string, function)) + goto end; + + if (silc_log_hexdump_cb) { + if ((*silc_log_hexdump_cb)(file, function, line, data_in, len, string, + silc_log_hexdump_context)) + goto end; + } - /* fprintf(stderr, "%s:%s:%d: %s\n", file, function, line, string); */ fprintf(stderr, "%s:%d: %s\n", function, line, string); - silc_free(string); k = 0; - off = len % 16; pos = 0; count = 16; + off = len % 16; while (1) { - if (off) { if ((len - pos) < 16 && (len - pos <= len - off)) count = off; @@ -200,14 +409,14 @@ void silc_log_output_hexdump(char *file, char *function, for (i = 0; i < count; i++) { fprintf(stderr, "%02X ", data[pos + i]); - + if ((i + 1) % 4 == 0) fprintf(stderr, " "); } if (count && count < 16) { int j; - + for (j = 0; j < 16 - count; j++) { fprintf(stderr, " "); @@ -215,10 +424,10 @@ void silc_log_output_hexdump(char *file, char *function, fprintf(stderr, " "); } } - + for (i = 0; i < count; i++) { char ch; - + if (data[pos] < 32 || data[pos] >= 127) ch = '.'; else @@ -234,57 +443,43 @@ void silc_log_output_hexdump(char *file, char *function, if (count < 16) break; } -} -/* Sets log files */ - -void silc_log_set_files(char *info, unsigned int info_size, - char *warning, unsigned int warning_size, - char *error, unsigned int error_size, - char *fatal, unsigned int fatal_size) -{ - 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; -} - -/* Sets log callbacks */ - -void silc_log_set_callbacks(SilcLogCb info, SilcLogCb warning, - SilcLogCb error, SilcLogCb fatal) -{ - info_cb = info; - warning_cb = warning; - error_cb = error; - fatal_cb = fatal; + end: + silc_free(string); } -/* Resets log callbacks */ +/* Sets debug callbacks */ -void silc_log_reset_callbacks() +void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb, + void *debug_context, + SilcLogHexdumpCb hexdump_cb, + void *hexdump_context) { - info_cb = warning_cb = error_cb = fatal_cb = NULL; + silc_log_debug_cb = debug_cb; + silc_log_debug_context = debug_context; + silc_log_hexdump_cb = hexdump_cb; + silc_log_hexdump_context = hexdump_context; } -/* Sets debug callbacks */ +/* Resets debug callbacks */ -void silc_log_set_debug_callbacks(SilcDebugCb debug, - SilcDebugHexdumpCb debug_hexdump) +void silc_log_reset_debug_callbacks() { - debug_cb = debug; - debug_hexdump_cb = debug_hexdump; + silc_log_debug_cb = NULL; + silc_log_debug_context = NULL; + silc_log_hexdump_cb = NULL; + silc_log_hexdump_context = NULL; } -/* Resets debug callbacks */ +/* Set current debug string */ -void silc_log_reset_debug_callbacks() +void silc_log_set_debug_string(const char *debug_string) { - debug_cb = NULL; - debug_hexdump_cb = NULL; + silc_free(silc_log_debug_string); + if ((strchr(debug_string, '(') && + strchr(debug_string, ')')) || + strchr(debug_string, '$')) + silc_log_debug_string = strdup(debug_string); + else + silc_log_debug_string = silc_string_regexify(debug_string); }