5 Author: Pekka Riikonen <priikone@silcnet.org>
7 Copyright (C) 1997 - 2005 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.
21 #include "silcincludes.h"
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_get_time(0), log->typename, (unsigned long)log->maxsize / 1024);
113 memset(newname, 0, sizeof(newname));
114 snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
116 rename(log->filename, newname);
118 log->fp = fopen(log->filename, "w");
120 SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
121 log->filename, log->typename, strerror(errno)));
123 chmod(log->filename, 0600);
124 #endif /* HAVE_CHMOD */
127 /* Internal timeout callback to flush log channels and check file sizes */
129 SILC_TASK_CALLBACK(silc_log_fflush_callback)
133 if (!silclog.quick) {
134 silc_log_flush_all();
135 log = silc_log_get_context(SILC_LOG_INFO);
136 silc_log_checksize(log);
137 log = silc_log_get_context(SILC_LOG_WARNING);
138 silc_log_checksize(log);
139 log = silc_log_get_context(SILC_LOG_ERROR);
140 silc_log_checksize(log);
141 log = silc_log_get_context(SILC_LOG_FATAL);
142 silc_log_checksize(log);
145 silclog.starting = FALSE;
147 if (silclog.flushdelay < 2)
148 silclog.flushdelay = 2;
149 silc_schedule_task_add_timeout(context, silc_log_fflush_callback, context,
150 silclog.flushdelay, 0);
153 /* Output log message to log file */
155 void silc_log_output(SilcLogType type, char *string)
157 const char *typename = NULL;
158 SilcLog log = silc_log_get_context(type);
164 /* Forward to callback if set */
166 if ((*log->cb)(type, string, log->context))
169 typename = log->typename;
171 if (!silclog.scheduled) {
172 if (silclog.no_init == FALSE) {
174 "Warning, trying to output without log files initialization, "
175 "log output is going to stderr\n");
176 silclog.no_init = TRUE;
184 /* Find open log file */
191 log = silc_log_get_context(--type);
193 if (!log || !log->fp)
197 if (silclog.timestamp)
198 fprintf(fp, "[%s] [%s] %s\n", silc_get_time(0), typename, string);
200 fprintf(fp, "[%s] %s\n", typename, string);
202 if (silclog.quick || silclog.starting) {
205 silc_log_checksize(log);
209 /* Output log to stderr if debugging */
210 if (typename && silclog.debug) {
211 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
217 /* Set and initialize the specified log file. */
219 bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
220 SilcSchedule scheduler)
225 log = silc_log_get_context(type);
229 SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
230 log->typename, filename, maxsize));
234 fp = fopen(filename, "a+");
236 fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
237 filename, strerror(errno));
241 chmod(filename, 0600);
242 #endif /* HAVE_CHMOD */
245 /* Close previous log file if it exists */
246 if (strlen(log->filename)) {
249 memset(log->filename, 0, sizeof(log->filename));
253 /* Set new log file */
256 log->maxsize = maxsize;
258 memset(log->filename, 0, sizeof(log->filename));
259 silc_strncat(log->filename, sizeof(log->filename), filename,
263 /* Add flush timeout */
265 silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
266 silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
268 silclog.scheduled = TRUE;
274 /* Return log filename */
276 char *silc_log_get_file(SilcLogType type)
278 SilcLog log = silc_log_get_context(type);
279 return log && log->fp ? log->filename : NULL;
282 /* Sets a log callback, set callback to NULL to return to default behaviour */
284 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
286 SilcLog log = silc_log_get_context(type);
289 log->context = context;
293 /* Reset log callbacks */
295 void silc_log_reset_callbacks(void)
298 log = silc_log_get_context(SILC_LOG_INFO);
299 log->cb = log->context = NULL;
300 log = silc_log_get_context(SILC_LOG_WARNING);
301 log->cb = log->context = NULL;
302 log = silc_log_get_context(SILC_LOG_ERROR);
303 log->cb = log->context = NULL;
304 log = silc_log_get_context(SILC_LOG_FATAL);
305 log->cb = log->context = NULL;
308 /* Flush all log files */
310 void silc_log_flush_all(void)
313 log = silc_log_get_context(SILC_LOG_INFO);
316 log = silc_log_get_context(SILC_LOG_WARNING);
319 log = silc_log_get_context(SILC_LOG_ERROR);
322 log = silc_log_get_context(SILC_LOG_FATAL);
327 /* Reset a log file */
329 static void silc_log_reset(SilcLog log)
336 if (!strlen(log->filename))
339 log->fp = fopen(log->filename, "a+");
341 SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
342 log->filename, log->typename, strerror(errno)));
345 /* Reset all log files */
347 void silc_log_reset_all(void)
350 log = silc_log_get_context(SILC_LOG_INFO);
353 log = silc_log_get_context(SILC_LOG_WARNING);
356 log = silc_log_get_context(SILC_LOG_ERROR);
359 log = silc_log_get_context(SILC_LOG_FATAL);
362 silc_log_flush_all();
365 /* Sets debug callbacks */
367 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
369 SilcLogHexdumpCb hexdump_cb,
370 void *hexdump_context)
372 silclog.debug_cb = debug_cb;
373 silclog.debug_context = debug_context;
374 silclog.hexdump_cb = hexdump_cb;
375 silclog.hexdump_context = hexdump_context;
378 /* Resets debug callbacks */
380 void silc_log_reset_debug_callbacks()
382 silclog.debug_cb = NULL;
383 silclog.debug_context = NULL;
384 silclog.hexdump_cb = NULL;
385 silclog.hexdump_context = NULL;
388 /* Set current debug string */
390 void silc_log_set_debug_string(const char *debug_string)
394 if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
395 strchr(debug_string, '$'))
396 string = strdup(debug_string);
398 string = silc_string_regexify(debug_string);
399 len = strlen(string);
400 if (len >= sizeof(silclog.debug_string))
401 len = sizeof(silclog.debug_string) - 1;
402 memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
403 strncpy(silclog.debug_string, string, len);
409 void silc_log_timestamp(bool enable)
411 silclog.timestamp = enable;
416 void silc_log_flushdelay(SilcUInt32 flushdelay)
418 silclog.flushdelay = flushdelay;
421 /* Set quick logging */
423 void silc_log_quick(bool enable)
425 silclog.quick = enable;
430 void silc_log_debug(bool enable)
432 silclog.debug = enable;
435 /* Set debug hexdump */
437 void silc_log_debug_hexdump(bool enable)
439 silclog.debug_hexdump = enable;
442 /* Outputs the debug message to stderr. */
444 void silc_log_output_debug(char *file, const char *function,
445 int line, char *string)
450 if (!silc_string_regex_match(silclog.debug_string, file) &&
451 !silc_string_regex_match(silclog.debug_string, function))
454 if (silclog.debug_cb) {
455 if ((*silclog.debug_cb)(file, (char *)function, line, string,
456 silclog.debug_context))
460 fprintf(stderr, "%s:%d: %s\n", function, line, string);
467 /* Hexdumps a message */
469 void silc_log_output_hexdump(char *file, const char *function,
470 int line, void *data_in,
471 SilcUInt32 len, char *string)
475 unsigned char *data = (unsigned char *)data_in;
477 if (!silclog.debug_hexdump)
480 if (!silc_string_regex_match(silclog.debug_string, file) &&
481 !silc_string_regex_match(silclog.debug_string, function))
484 if (silclog.hexdump_cb) {
485 if ((*silclog.hexdump_cb)(file, (char *)function, line,
486 data_in, len, string, silclog.hexdump_context))
490 fprintf(stderr, "%s:%d: %s\n", function, line, string);
498 if ((len - pos) < 16 && (len - pos <= len - off))
508 fprintf(stderr, "%08X ", k++ * 16);
510 for (i = 0; i < count; i++) {
511 fprintf(stderr, "%02X ", data[pos + i]);
513 if ((i + 1) % 4 == 0)
514 fprintf(stderr, " ");
517 if (count && count < 16) {
520 for (j = 0; j < 16 - count; j++) {
521 fprintf(stderr, " ");
523 if ((j + count + 1) % 4 == 0)
524 fprintf(stderr, " ");
528 for (i = 0; i < count; i++) {
531 if (data[pos] < 32 || data[pos] >= 127)
536 fprintf(stderr, "%c", ch);
541 fprintf(stderr, "\n");