silclog.c
- Author: Pekka Riikonen <priikone@poseidon.pspt.fi>
+ Author: Pekka Riikonen <priikone@silcnet.org>
- Copyright (C) 1997 - 2000 Pekka Riikonen
+ Copyright (C) 1997 - 2007 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
*/
/* $Id$ */
-#include "silcincludes.h"
+#include "silc.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 */
-bool silc_debug = FALSE;
-bool silc_debug_hexdump = FALSE;
-char *silc_debug_string = NULL;
+#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,
+};
- { NULL, -1 },
+#endif /* !SILC_SYMBIAN */
+
+/* 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)
+{
+ if (type < 1 || type > 4)
+ return NULL;
+ return (SilcLog)&silclogs[(int)type - 1];
+}
+
+/* Check log file site and cycle log file if it is over max size. */
+
+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;
+ }
+
+ 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 */
+}
+
+/* Internal timeout callback to flush log channels and check file sizes */
+
+SILC_TASK_CALLBACK(silc_log_fflush_callback)
{
+#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);
+ }
+
+ silclog.starting = FALSE;
+
+ if (silclog.flushdelay < 2)
+ silclog.flushdelay = 2;
+ silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
+ silclog.flushdelay, 0);
+#endif /* !SILC_SYMBIAN */
+}
+
+/* Output log message to log file */
+
+void silc_log_output(SilcLogType type, char *string)
+{
+ const char *typename = NULL;
+ SilcLog log = silc_log_get_context(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 (!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, trying to output without log files initialization, "
+ "log output is going to stderr\n");
+ silclog.no_init = TRUE;
}
- if (!filename)
fp = stderr;
- else {
- /* 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);
- }
- }
-
- /* 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;
- }
+ log = NULL;
+ goto found;
}
+#endif /* !SILC_SYMBIAN */
- /* Get the log type name */
- for (np = silc_log_types; np->name; np++) {
- if (np->type == type)
+ /* Find open log file */
+ while (log) {
+ if (log->fp) {
+ fp = log->fp;
break;
+ }
+
+ 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 */
- fprintf(fp, "[%s] [%s] %s\n", silc_get_time(), np->name, string);
- fflush(fp);
- if (fp != stderr)
- fclose(fp);
silc_free(string);
}
-/* Outputs the debug message to stderr. */
+/* Set and initialize the specified log file. */
-void silc_log_output_debug(char *file, char *function,
- int line, char *string)
+SilcBool silc_log_set_file(SilcLogType type, char *filename,
+ SilcUInt32 maxsize, SilcSchedule scheduler)
{
- if (!silc_debug) {
- silc_free(string);
- return;
+#ifndef SILC_SYMBIAN
+ FILE *fp = NULL;
+ SilcLog log;
+
+ 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;
+ }
+#ifdef HAVE_CHMOD
+ chmod(filename, 0600);
+#endif /* HAVE_CHMOD */
}
- if (silc_debug_string &&
- (!silc_string_regex_match(silc_debug_string, file) &&
- !silc_string_regex_match(silc_debug_string, function))) {
- silc_free(string);
- return;
+ /* 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;
+ }
+
+ /* Set new log file */
+ if (fp) {
+ log->fp = fp;
+ log->maxsize = maxsize;
+
+ memset(log->filename, 0, sizeof(log->filename));
+ silc_strncat(log->filename, sizeof(log->filename), filename,
+ strlen(filename));
+ }
+
+ /* 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;
+ }
+
+#endif /* !SILC_SYMBIAN */
+ return TRUE;
+}
+
+/* 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 */
+}
+
+/* Flush all log files */
- if (debug_cb) {
- (*debug_cb)(file, function, line, string);
- silc_free(string);
+void silc_log_flush_all(void)
+{
+ 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);
+}
+
+/* Reset a log file */
+
+static void silc_log_reset(SilcLog log)
+{
+ 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)));
+}
+
+/* Reset all log files */
+
+void silc_log_reset_all(void)
+{
+ 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(SilcLogDebugCb debug_cb,
+ void *debug_context,
+ SilcLogHexdumpCb hexdump_cb,
+ void *hexdump_context)
+{
+#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()
+{
+#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 = 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 */
- fprintf(stderr, "%s:%d: %s\n", function, line, string);
+ 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 %s:%d: %s\n", curtime.hour,
+ curtime.minute, curtime.second, function, line,
+ string);
fflush(stderr);
+#endif /* SILC_SYMBIAN */
+
+ end:
silc_free(string);
}
/* Hexdumps a message */
-void silc_log_output_hexdump(char *file, char *function,
+void silc_log_output_hexdump(char *file, const char *function,
int line, void *data_in,
- uint32 len, char *string)
+ SilcUInt32 len, char *string)
{
int i, k;
int off, pos, count;
unsigned char *data = (unsigned char *)data_in;
- if (!silc_debug_hexdump) {
- silc_free(string);
- return;
- }
+#ifndef SILC_SYMBIAN
+ if (!silclog.debug_hexdump)
+ goto end;
- if (silc_debug_string &&
- (!silc_string_regex_match(silc_debug_string, file) &&
- !silc_string_regex_match(silc_debug_string, function))) {
- silc_free(string);
- return;
- }
+ if (!silc_string_regex_match(silclog.debug_string, file) &&
+ !silc_string_regex_match(silclog.debug_string, function))
+ goto end;
- if (debug_hexdump_cb) {
- (*debug_hexdump_cb)(file, function, line, data_in, len, string);
- silc_free(string);
- return;
+ 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_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;
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, " ");
fprintf(stderr, " ");
}
}
-
+
for (i = 0; i < count; i++) {
char ch;
-
+
if (data[pos] < 32 || data[pos] >= 127)
ch = '.';
else
if (count < 16)
break;
}
-}
-
-/* Sets 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)
-{
- 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;
-}
-
-/* Resets log callbacks */
-
-void silc_log_reset_callbacks()
-{
- info_cb = warning_cb = error_cb = fatal_cb = NULL;
-}
-
-/* Sets debug callbacks */
-
-void silc_log_set_debug_callbacks(SilcDebugCb debug,
- SilcDebugHexdumpCb debug_hexdump)
-{
- debug_cb = debug;
- debug_hexdump_cb = debug_hexdump;
-}
-
-/* Resets debug callbacks */
-void silc_log_reset_debug_callbacks()
-{
- debug_cb = NULL;
- debug_hexdump_cb = NULL;
-}
-
-/* Set current debug string */
-
-void silc_log_set_debug_string(const char *debug_string)
-{
- silc_free(silc_debug_string);
- if ((strchr(debug_string, '(') &&
- strchr(debug_string, ')')) ||
- strchr(debug_string, '$'))
- silc_debug_string = strdup(debug_string);
- else
- silc_debug_string = silc_string_regexify(debug_string);
+ end:
+ silc_free(string);
}