5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2008 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.
20 #include "silcruntime.h"
22 /* SilcLogSettings context */
24 SilcUInt32 flushdelay;
26 char debug_string[128];
27 SilcLogDebugCb debug_cb;
29 SilcLogHexdumpCb hexdump_cb;
30 void *hexdump_context;
32 unsigned int timestamp : 1;
33 unsigned int quick : 1;
34 unsigned int debug : 1;
35 unsigned int debug_hexdump : 1;
36 unsigned int scheduled : 1;
37 unsigned int no_init : 1;
38 unsigned int starting : 1;
39 } *SilcLogSettings, SilcLogSettingsStruct;
50 } *SilcLog, SilcLogStruct;
54 /* Default settings */
55 static SilcLogSettingsStruct silclog =
70 #endif /* !SILC_SYMBIAN */
72 /* Default log contexts */
74 static SilcLogStruct silclogs[4] =
76 const SilcLogStruct silclogs[4] =
77 #endif /* !SILC_SYMBIAN */
79 {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
80 {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
81 {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
82 {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
85 /* Return log context by type */
87 static SilcLog silc_log_get_context(SilcLogType type)
89 if (type < 1 || type > 4)
91 return (SilcLog)&silclogs[(int)type - 1];
94 /* Check log file site and cycle log file if it is over max size. */
96 static void silc_log_checksize(SilcLog log)
101 if (!log || !log->fp || !log->maxsize)
104 size = silc_file_size(log->filename);
110 if (size < log->maxsize)
115 "[%s] [%s] Cycling log file, over max log size (%lu kilobytes)\n",
116 silc_time_string(0), log->typename,
117 (unsigned long)log->maxsize / 1024);
121 memset(newname, 0, sizeof(newname));
122 silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
124 rename(log->filename, newname);
126 log->fp = fopen(log->filename, "w");
128 SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
129 log->filename, log->typename, strerror(errno)));
131 chmod(log->filename, 0600);
132 #endif /* HAVE_CHMOD */
135 /* Internal timeout callback to flush log channels and check file sizes */
137 SILC_TASK_CALLBACK(silc_log_fflush_callback)
142 if (!silclog.quick) {
143 silc_log_flush_all();
144 log = silc_log_get_context(SILC_LOG_INFO);
145 silc_log_checksize(log);
146 log = silc_log_get_context(SILC_LOG_WARNING);
147 silc_log_checksize(log);
148 log = silc_log_get_context(SILC_LOG_ERROR);
149 silc_log_checksize(log);
150 log = silc_log_get_context(SILC_LOG_FATAL);
151 silc_log_checksize(log);
154 silclog.starting = FALSE;
156 if (silclog.flushdelay < 2)
157 silclog.flushdelay = 2;
158 silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
159 silclog.flushdelay, 0);
160 #endif /* !SILC_SYMBIAN */
163 /* Output log message to log file */
165 void silc_log_output(SilcLogType type, char *string)
167 const char *typename = NULL;
168 SilcLog log = silc_log_get_context(type);
174 /* Forward to callback if set */
176 if ((*log->cb)(type, string, log->context))
179 typename = log->typename;
182 if (!silclog.scheduled) {
183 if (silclog.no_init == FALSE) {
185 "Warning, log files not initialized, "
186 "log output is going to stderr\n");
187 silclog.no_init = TRUE;
194 #endif /* !SILC_SYMBIAN */
196 /* Find open log file */
203 log = silc_log_get_context(--type);
205 if (!log || !log->fp)
210 if (silclog.timestamp)
211 fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string);
213 fprintf(fp, "[%s] %s\n", typename, string);
215 if (silclog.quick || silclog.starting) {
218 silc_log_checksize(log);
220 #endif /* !SILC_SYMBIAN */
224 /* Output log to stderr if debugging */
225 if (typename && silclog.debug) {
226 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
230 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
231 #endif /* !SILC_SYMBIAN */
236 /* Set and initialize the specified log file. */
238 SilcBool silc_log_set_file(SilcLogType type, char *filename,
239 SilcUInt32 maxsize, SilcSchedule scheduler)
246 scheduler = silc_schedule_get_global();
248 log = silc_log_get_context(type);
252 SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
253 log->typename, filename, maxsize));
257 fp = fopen(filename, "a+");
259 fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
260 filename, strerror(errno));
264 chmod(filename, 0600);
265 #endif /* HAVE_CHMOD */
268 /* Close previous log file if it exists */
269 if (strlen(log->filename)) {
272 memset(log->filename, 0, sizeof(log->filename));
276 /* Set new log file */
279 log->maxsize = maxsize;
281 memset(log->filename, 0, sizeof(log->filename));
282 silc_strncat(log->filename, sizeof(log->filename), filename,
286 /* Add flush timeout */
288 silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
289 silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
291 silclog.scheduled = TRUE;
294 #endif /* !SILC_SYMBIAN */
298 /* Return log filename */
300 char *silc_log_get_file(SilcLogType type)
302 SilcLog log = silc_log_get_context(type);
303 return log && log->fp ? log->filename : NULL;
306 /* Sets a log callback, set callback to NULL to return to default behaviour */
308 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
311 SilcLog log = silc_log_get_context(type);
314 log->context = context;
316 #endif /* !SILC_SYMBIAN */
319 /* Reset log callbacks */
321 void silc_log_reset_callbacks(void)
325 log = silc_log_get_context(SILC_LOG_INFO);
326 log->cb = log->context = NULL;
327 log = silc_log_get_context(SILC_LOG_WARNING);
328 log->cb = log->context = NULL;
329 log = silc_log_get_context(SILC_LOG_ERROR);
330 log->cb = log->context = NULL;
331 log = silc_log_get_context(SILC_LOG_FATAL);
332 log->cb = log->context = NULL;
333 #endif /* !SILC_SYMBIAN */
336 /* Flush all log files */
338 void silc_log_flush_all(void)
341 log = silc_log_get_context(SILC_LOG_INFO);
344 log = silc_log_get_context(SILC_LOG_WARNING);
347 log = silc_log_get_context(SILC_LOG_ERROR);
350 log = silc_log_get_context(SILC_LOG_FATAL);
355 /* Reset a log file */
357 static void silc_log_reset(SilcLog log)
364 if (!strlen(log->filename))
367 log->fp = fopen(log->filename, "a+");
369 SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
370 log->filename, log->typename, strerror(errno)));
373 /* Reset all log files */
375 void silc_log_reset_all(void)
378 log = silc_log_get_context(SILC_LOG_INFO);
381 log = silc_log_get_context(SILC_LOG_WARNING);
384 log = silc_log_get_context(SILC_LOG_ERROR);
387 log = silc_log_get_context(SILC_LOG_FATAL);
390 silc_log_flush_all();
393 /* Sets debug callbacks */
395 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
397 SilcLogHexdumpCb hexdump_cb,
398 void *hexdump_context)
401 silclog.debug_cb = debug_cb;
402 silclog.debug_context = debug_context;
403 silclog.hexdump_cb = hexdump_cb;
404 silclog.hexdump_context = hexdump_context;
405 #endif /* !SILC_SYMBIAN */
408 /* Resets debug callbacks */
410 void silc_log_reset_debug_callbacks()
413 silclog.debug_cb = NULL;
414 silclog.debug_context = NULL;
415 silclog.hexdump_cb = NULL;
416 silclog.hexdump_context = NULL;
417 #endif /* !SILC_SYMBIAN */
420 /* Set current debug string */
422 void silc_log_set_debug_string(const char *debug_string)
427 if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
428 strchr(debug_string, '$'))
429 string = silc_strdup(debug_string);
431 string = silc_string_regexify(debug_string);
432 len = strlen(string);
433 if (len >= sizeof(silclog.debug_string))
434 len = sizeof(silclog.debug_string) - 1;
435 memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
436 strncpy(silclog.debug_string, string, len);
438 #endif /* !SILC_SYMBIAN */
443 void silc_log_timestamp(SilcBool enable)
446 silclog.timestamp = enable;
447 #endif /* !SILC_SYMBIAN */
452 void silc_log_flushdelay(SilcUInt32 flushdelay)
455 silclog.flushdelay = flushdelay;
456 #endif /* !SILC_SYMBIAN */
459 /* Set quick logging */
461 void silc_log_quick(SilcBool enable)
464 silclog.quick = enable;
465 #endif /* !SILC_SYMBIAN */
470 void silc_log_debug(SilcBool enable)
473 silclog.debug = enable;
474 #endif /* !SILC_SYMBIAN */
477 /* Set debug hexdump */
479 void silc_log_debug_hexdump(SilcBool enable)
482 silclog.debug_hexdump = enable;
483 #endif /* !SILC_SYMBIAN */
486 /* Outputs the debug message to stderr. */
488 void silc_log_output_debug(char *file, const char *function,
489 int line, char *string)
491 SilcTimeStruct curtime;
497 if (!silc_string_regex_match(silclog.debug_string, file) &&
498 !silc_string_regex_match(silclog.debug_string, function))
501 if (silclog.debug_cb) {
502 if ((*silclog.debug_cb)(file, (char *)function, line, string,
503 silclog.debug_context))
506 #endif /* !SILC_SYMBIAN */
508 silc_time_value(0, &curtime);
511 if (strrchr(function, '\\'))
512 fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
514 #endif /* SILC_WIN32 */
516 silc_symbian_debug(function, line, string);
518 fprintf(stderr, "%02d:%02d:%02d %s:%d: %s\n", curtime.hour,
519 curtime.minute, curtime.second, function, line,
522 #endif /* SILC_SYMBIAN */
528 /* Hexdumps a message */
530 void silc_log_output_hexdump(char *file, const char *function,
531 int line, void *data_in,
532 SilcUInt32 len, char *string)
535 if (!silclog.debug_hexdump)
538 if (!silc_string_regex_match(silclog.debug_string, file) &&
539 !silc_string_regex_match(silclog.debug_string, function))
542 if (silclog.hexdump_cb) {
543 if ((*silclog.hexdump_cb)(file, (char *)function, line,
544 data_in, len, string, silclog.hexdump_context))
547 #endif /* !SILC_SYMBIAN */
549 fprintf(stderr, "%s:%d: %s\n", function, line, string);
551 silc_hexdump(data_in, len, stderr);