5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2006 Pekka Riikonen
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; version 2 of the License.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
23 /* SilcLogSettings context */
25 SilcUInt32 flushdelay;
27 char debug_string[128];
28 SilcLogDebugCb debug_cb;
30 SilcLogHexdumpCb hexdump_cb;
31 void *hexdump_context;
33 unsigned int timestamp : 1;
34 unsigned int quick : 1;
35 unsigned int debug : 1;
36 unsigned int debug_hexdump : 1;
37 unsigned int scheduled : 1;
38 unsigned int no_init : 1;
39 unsigned int starting : 1;
40 } *SilcLogSettings, SilcLogSettingsStruct;
51 } *SilcLog, SilcLogStruct;
53 /* Default settings */
54 static SilcLogSettingsStruct silclog =
69 /* Default log contexts */
70 static SilcLogStruct silclogs[4] =
72 {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
73 {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
74 {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
75 {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
78 /* Return log context by type */
80 static SilcLog silc_log_get_context(SilcLogType type)
82 if (type < 1 || type > 4)
84 return &silclogs[(int)type - 1];
87 /* Check log file site and cycle log file if it is over max size. */
89 static void silc_log_checksize(SilcLog log)
94 if (!log || !log->fp || !log->maxsize)
97 size = silc_file_size(log->filename);
103 if (size < log->maxsize)
108 "[%s] [%s] Cycling log file, over max log size (%lu kilobytes)\n",
109 silc_time_string(0), log->typename,
110 (unsigned long)log->maxsize / 1024);
114 memset(newname, 0, sizeof(newname));
115 silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
117 rename(log->filename, newname);
119 log->fp = fopen(log->filename, "w");
121 SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
122 log->filename, log->typename, strerror(errno)));
124 chmod(log->filename, 0600);
125 #endif /* HAVE_CHMOD */
128 /* Internal timeout callback to flush log channels and check file sizes */
130 SILC_TASK_CALLBACK(silc_log_fflush_callback)
134 if (!silclog.quick) {
135 silc_log_flush_all();
136 log = silc_log_get_context(SILC_LOG_INFO);
137 silc_log_checksize(log);
138 log = silc_log_get_context(SILC_LOG_WARNING);
139 silc_log_checksize(log);
140 log = silc_log_get_context(SILC_LOG_ERROR);
141 silc_log_checksize(log);
142 log = silc_log_get_context(SILC_LOG_FATAL);
143 silc_log_checksize(log);
146 silclog.starting = FALSE;
148 if (silclog.flushdelay < 2)
149 silclog.flushdelay = 2;
150 silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
151 silclog.flushdelay, 0);
154 /* Output log message to log file */
156 void silc_log_output(SilcLogType type, char *string)
158 const char *typename = NULL;
159 SilcLog log = silc_log_get_context(type);
165 /* Forward to callback if set */
167 if ((*log->cb)(type, string, log->context))
170 typename = log->typename;
172 if (!silclog.scheduled) {
173 if (silclog.no_init == FALSE) {
175 "Warning, trying to output without log files initialization, "
176 "log output is going to stderr\n");
177 silclog.no_init = TRUE;
185 /* Find open log file */
192 log = silc_log_get_context(--type);
194 if (!log || !log->fp)
198 if (silclog.timestamp)
199 fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string);
201 fprintf(fp, "[%s] %s\n", typename, string);
203 if (silclog.quick || silclog.starting) {
206 silc_log_checksize(log);
210 /* Output log to stderr if debugging */
211 if (typename && silclog.debug) {
212 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
218 /* Set and initialize the specified log file. */
220 SilcBool silc_log_set_file(SilcLogType type, char *filename,
221 SilcUInt32 maxsize, SilcSchedule scheduler)
226 log = silc_log_get_context(type);
230 SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
231 log->typename, filename, maxsize));
235 fp = fopen(filename, "a+");
237 fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
238 filename, strerror(errno));
242 chmod(filename, 0600);
243 #endif /* HAVE_CHMOD */
246 /* Close previous log file if it exists */
247 if (strlen(log->filename)) {
250 memset(log->filename, 0, sizeof(log->filename));
254 /* Set new log file */
257 log->maxsize = maxsize;
259 memset(log->filename, 0, sizeof(log->filename));
260 silc_strncat(log->filename, sizeof(log->filename), filename,
264 /* Add flush timeout */
266 silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
267 silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
269 silclog.scheduled = TRUE;
275 /* Return log filename */
277 char *silc_log_get_file(SilcLogType type)
279 SilcLog log = silc_log_get_context(type);
280 return log && log->fp ? log->filename : NULL;
283 /* Sets a log callback, set callback to NULL to return to default behaviour */
285 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
287 SilcLog log = silc_log_get_context(type);
290 log->context = context;
294 /* Reset log callbacks */
296 void silc_log_reset_callbacks(void)
299 log = silc_log_get_context(SILC_LOG_INFO);
300 log->cb = log->context = NULL;
301 log = silc_log_get_context(SILC_LOG_WARNING);
302 log->cb = log->context = NULL;
303 log = silc_log_get_context(SILC_LOG_ERROR);
304 log->cb = log->context = NULL;
305 log = silc_log_get_context(SILC_LOG_FATAL);
306 log->cb = log->context = NULL;
309 /* Flush all log files */
311 void silc_log_flush_all(void)
314 log = silc_log_get_context(SILC_LOG_INFO);
317 log = silc_log_get_context(SILC_LOG_WARNING);
320 log = silc_log_get_context(SILC_LOG_ERROR);
323 log = silc_log_get_context(SILC_LOG_FATAL);
328 /* Reset a log file */
330 static void silc_log_reset(SilcLog log)
337 if (!strlen(log->filename))
340 log->fp = fopen(log->filename, "a+");
342 SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
343 log->filename, log->typename, strerror(errno)));
346 /* Reset all log files */
348 void silc_log_reset_all(void)
351 log = silc_log_get_context(SILC_LOG_INFO);
354 log = silc_log_get_context(SILC_LOG_WARNING);
357 log = silc_log_get_context(SILC_LOG_ERROR);
360 log = silc_log_get_context(SILC_LOG_FATAL);
363 silc_log_flush_all();
366 /* Sets debug callbacks */
368 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
370 SilcLogHexdumpCb hexdump_cb,
371 void *hexdump_context)
373 silclog.debug_cb = debug_cb;
374 silclog.debug_context = debug_context;
375 silclog.hexdump_cb = hexdump_cb;
376 silclog.hexdump_context = hexdump_context;
379 /* Resets debug callbacks */
381 void silc_log_reset_debug_callbacks()
383 silclog.debug_cb = NULL;
384 silclog.debug_context = NULL;
385 silclog.hexdump_cb = NULL;
386 silclog.hexdump_context = NULL;
389 /* Set current debug string */
391 void silc_log_set_debug_string(const char *debug_string)
395 if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
396 strchr(debug_string, '$'))
397 string = strdup(debug_string);
399 string = silc_string_regexify(debug_string);
400 len = strlen(string);
401 if (len >= sizeof(silclog.debug_string))
402 len = sizeof(silclog.debug_string) - 1;
403 memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
404 strncpy(silclog.debug_string, string, len);
410 void silc_log_timestamp(SilcBool enable)
412 silclog.timestamp = enable;
417 void silc_log_flushdelay(SilcUInt32 flushdelay)
419 silclog.flushdelay = flushdelay;
422 /* Set quick logging */
424 void silc_log_quick(SilcBool enable)
426 silclog.quick = enable;
431 void silc_log_debug(SilcBool enable)
433 silclog.debug = enable;
436 /* Set debug hexdump */
438 void silc_log_debug_hexdump(SilcBool enable)
440 silclog.debug_hexdump = enable;
443 /* Outputs the debug message to stderr. */
445 void silc_log_output_debug(char *file, const char *function,
446 int line, char *string)
451 if (!silc_string_regex_match(silclog.debug_string, file) &&
452 !silc_string_regex_match(silclog.debug_string, function))
455 if (silclog.debug_cb) {
456 if ((*silclog.debug_cb)(file, (char *)function, line, string,
457 silclog.debug_context))
462 if (strrchr(function, '\\'))
463 fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
465 #endif /* SILC_WIN32 */
467 fprintf(stderr, "%s:%d: %s\n", function, line, string);
474 /* Hexdumps a message */
476 void silc_log_output_hexdump(char *file, const char *function,
477 int line, void *data_in,
478 SilcUInt32 len, char *string)
482 unsigned char *data = (unsigned char *)data_in;
484 if (!silclog.debug_hexdump)
487 if (!silc_string_regex_match(silclog.debug_string, file) &&
488 !silc_string_regex_match(silclog.debug_string, function))
491 if (silclog.hexdump_cb) {
492 if ((*silclog.hexdump_cb)(file, (char *)function, line,
493 data_in, len, string, silclog.hexdump_context))
497 fprintf(stderr, "%s:%d: %s\n", function, line, string);
505 if ((len - pos) < 16 && (len - pos <= len - off))
515 fprintf(stderr, "%08X ", k++ * 16);
517 for (i = 0; i < count; i++) {
518 fprintf(stderr, "%02X ", data[pos + i]);
520 if ((i + 1) % 4 == 0)
521 fprintf(stderr, " ");
524 if (count && count < 16) {
527 for (j = 0; j < 16 - count; j++) {
528 fprintf(stderr, " ");
530 if ((j + count + 1) % 4 == 0)
531 fprintf(stderr, " ");
535 for (i = 0; i < count; i++) {
538 if (data[pos] < 32 || data[pos] >= 127)
543 fprintf(stderr, "%c", ch);
548 fprintf(stderr, "\n");