5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2007 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;
55 /* Default settings */
56 static SilcLogSettingsStruct silclog =
71 #endif /* !SILC_SYMBIAN */
73 /* Default log contexts */
75 static SilcLogStruct silclogs[4] =
77 const SilcLogStruct silclogs[4] =
78 #endif /* !SILC_SYMBIAN */
80 {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
81 {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
82 {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
83 {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
86 /* Return log context by type */
88 static SilcLog silc_log_get_context(SilcLogType type)
90 if (type < 1 || type > 4)
92 return (SilcLog)&silclogs[(int)type - 1];
95 /* Check log file site and cycle log file if it is over max size. */
97 static void silc_log_checksize(SilcLog log)
102 if (!log || !log->fp || !log->maxsize)
105 size = silc_file_size(log->filename);
111 if (size < log->maxsize)
116 "[%s] [%s] Cycling log file, over max log size (%lu kilobytes)\n",
117 silc_time_string(0), log->typename,
118 (unsigned long)log->maxsize / 1024);
122 memset(newname, 0, sizeof(newname));
123 silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
125 rename(log->filename, newname);
127 log->fp = fopen(log->filename, "w");
129 SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
130 log->filename, log->typename, strerror(errno)));
132 chmod(log->filename, 0600);
133 #endif /* HAVE_CHMOD */
136 /* Internal timeout callback to flush log channels and check file sizes */
138 SILC_TASK_CALLBACK(silc_log_fflush_callback)
143 if (!silclog.quick) {
144 silc_log_flush_all();
145 log = silc_log_get_context(SILC_LOG_INFO);
146 silc_log_checksize(log);
147 log = silc_log_get_context(SILC_LOG_WARNING);
148 silc_log_checksize(log);
149 log = silc_log_get_context(SILC_LOG_ERROR);
150 silc_log_checksize(log);
151 log = silc_log_get_context(SILC_LOG_FATAL);
152 silc_log_checksize(log);
155 silclog.starting = FALSE;
157 if (silclog.flushdelay < 2)
158 silclog.flushdelay = 2;
159 silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
160 silclog.flushdelay, 0);
161 #endif /* !SILC_SYMBIAN */
164 /* Output log message to log file */
166 void silc_log_output(SilcLogType type, char *string)
168 const char *typename = NULL;
169 SilcLog log = silc_log_get_context(type);
175 /* Forward to callback if set */
177 if ((*log->cb)(type, string, log->context))
180 typename = log->typename;
183 if (!silclog.scheduled) {
184 if (silclog.no_init == FALSE) {
186 "Warning, log files not initialized, "
187 "log output is going to stderr\n");
188 silclog.no_init = TRUE;
195 #endif /* !SILC_SYMBIAN */
197 /* Find open log file */
204 log = silc_log_get_context(--type);
206 if (!log || !log->fp)
211 if (silclog.timestamp)
212 fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string);
214 fprintf(fp, "[%s] %s\n", typename, string);
216 if (silclog.quick || silclog.starting) {
219 silc_log_checksize(log);
221 #endif /* !SILC_SYMBIAN */
225 /* Output log to stderr if debugging */
226 if (typename && silclog.debug) {
227 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
231 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
232 #endif /* !SILC_SYMBIAN */
237 /* Set and initialize the specified log file. */
239 SilcBool silc_log_set_file(SilcLogType type, char *filename,
240 SilcUInt32 maxsize, SilcSchedule scheduler)
247 scheduler = silc_schedule_get_global();
249 log = silc_log_get_context(type);
253 SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
254 log->typename, filename, maxsize));
258 fp = fopen(filename, "a+");
260 fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
261 filename, strerror(errno));
265 chmod(filename, 0600);
266 #endif /* HAVE_CHMOD */
269 /* Close previous log file if it exists */
270 if (strlen(log->filename)) {
273 memset(log->filename, 0, sizeof(log->filename));
277 /* Set new log file */
280 log->maxsize = maxsize;
282 memset(log->filename, 0, sizeof(log->filename));
283 silc_strncat(log->filename, sizeof(log->filename), filename,
287 /* Add flush timeout */
289 silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
290 silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
292 silclog.scheduled = TRUE;
295 #endif /* !SILC_SYMBIAN */
299 /* Return log filename */
301 char *silc_log_get_file(SilcLogType type)
303 SilcLog log = silc_log_get_context(type);
304 return log && log->fp ? log->filename : NULL;
307 /* Sets a log callback, set callback to NULL to return to default behaviour */
309 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
312 SilcLog log = silc_log_get_context(type);
315 log->context = context;
317 #endif /* !SILC_SYMBIAN */
320 /* Reset log callbacks */
322 void silc_log_reset_callbacks(void)
326 log = silc_log_get_context(SILC_LOG_INFO);
327 log->cb = log->context = NULL;
328 log = silc_log_get_context(SILC_LOG_WARNING);
329 log->cb = log->context = NULL;
330 log = silc_log_get_context(SILC_LOG_ERROR);
331 log->cb = log->context = NULL;
332 log = silc_log_get_context(SILC_LOG_FATAL);
333 log->cb = log->context = NULL;
334 #endif /* !SILC_SYMBIAN */
337 /* Flush all log files */
339 void silc_log_flush_all(void)
342 log = silc_log_get_context(SILC_LOG_INFO);
345 log = silc_log_get_context(SILC_LOG_WARNING);
348 log = silc_log_get_context(SILC_LOG_ERROR);
351 log = silc_log_get_context(SILC_LOG_FATAL);
356 /* Reset a log file */
358 static void silc_log_reset(SilcLog log)
365 if (!strlen(log->filename))
368 log->fp = fopen(log->filename, "a+");
370 SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
371 log->filename, log->typename, strerror(errno)));
374 /* Reset all log files */
376 void silc_log_reset_all(void)
379 log = silc_log_get_context(SILC_LOG_INFO);
382 log = silc_log_get_context(SILC_LOG_WARNING);
385 log = silc_log_get_context(SILC_LOG_ERROR);
388 log = silc_log_get_context(SILC_LOG_FATAL);
391 silc_log_flush_all();
394 /* Sets debug callbacks */
396 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
398 SilcLogHexdumpCb hexdump_cb,
399 void *hexdump_context)
402 silclog.debug_cb = debug_cb;
403 silclog.debug_context = debug_context;
404 silclog.hexdump_cb = hexdump_cb;
405 silclog.hexdump_context = hexdump_context;
406 #endif /* !SILC_SYMBIAN */
409 /* Resets debug callbacks */
411 void silc_log_reset_debug_callbacks()
414 silclog.debug_cb = NULL;
415 silclog.debug_context = NULL;
416 silclog.hexdump_cb = NULL;
417 silclog.hexdump_context = NULL;
418 #endif /* !SILC_SYMBIAN */
421 /* Set current debug string */
423 void silc_log_set_debug_string(const char *debug_string)
428 if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
429 strchr(debug_string, '$'))
430 string = silc_strdup(debug_string);
432 string = silc_string_regexify(debug_string);
433 len = strlen(string);
434 if (len >= sizeof(silclog.debug_string))
435 len = sizeof(silclog.debug_string) - 1;
436 memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
437 strncpy(silclog.debug_string, string, len);
439 #endif /* !SILC_SYMBIAN */
444 void silc_log_timestamp(SilcBool enable)
447 silclog.timestamp = enable;
448 #endif /* !SILC_SYMBIAN */
453 void silc_log_flushdelay(SilcUInt32 flushdelay)
456 silclog.flushdelay = flushdelay;
457 #endif /* !SILC_SYMBIAN */
460 /* Set quick logging */
462 void silc_log_quick(SilcBool enable)
465 silclog.quick = enable;
466 #endif /* !SILC_SYMBIAN */
471 void silc_log_debug(SilcBool enable)
474 silclog.debug = enable;
475 #endif /* !SILC_SYMBIAN */
478 /* Set debug hexdump */
480 void silc_log_debug_hexdump(SilcBool enable)
483 silclog.debug_hexdump = enable;
484 #endif /* !SILC_SYMBIAN */
487 /* Outputs the debug message to stderr. */
489 void silc_log_output_debug(char *file, const char *function,
490 int line, char *string)
492 SilcTimeStruct curtime;
498 if (!silc_string_regex_match(silclog.debug_string, file) &&
499 !silc_string_regex_match(silclog.debug_string, function))
502 if (silclog.debug_cb) {
503 if ((*silclog.debug_cb)(file, (char *)function, line, string,
504 silclog.debug_context))
507 #endif /* !SILC_SYMBIAN */
509 silc_time_value(0, &curtime);
512 if (strrchr(function, '\\'))
513 fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
515 #endif /* SILC_WIN32 */
517 silc_symbian_debug(function, line, string);
519 fprintf(stderr, "%02d:%02d:%02d %s:%d: %s\n", curtime.hour,
520 curtime.minute, curtime.second, function, line,
523 #endif /* SILC_SYMBIAN */
529 /* Hexdumps a message */
531 void silc_log_output_hexdump(char *file, const char *function,
532 int line, void *data_in,
533 SilcUInt32 len, char *string)
536 if (!silclog.debug_hexdump)
539 if (!silc_string_regex_match(silclog.debug_string, file) &&
540 !silc_string_regex_match(silclog.debug_string, function))
543 if (silclog.hexdump_cb) {
544 if ((*silclog.hexdump_cb)(file, (char *)function, line,
545 data_in, len, string, silclog.hexdump_context))
548 #endif /* !SILC_SYMBIAN */
550 fprintf(stderr, "%s:%d: %s\n", function, line, string);
552 silc_hexdump(data_in, len, stderr);