5 Author: Johnny Mnemonic <johnny@themnemonic.org>
7 Copyright (C) 1997 - 2002 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; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
22 #include "silcincludes.h"
24 /* default flush time (5 minutes) */
25 #define SILC_LOG_TIMEOUT 300
27 /* nice macro for looping through all logs -- makes the code more readable */
28 #define SILC_FOREACH_LOG(__x__) for (__x__ = 0; __x__ < SILC_LOG_MAX; __x__++)
30 /* Our working struct -- at the moment we keep it private, but this could
31 * change in the future */
32 struct SilcLogStruct {
41 typedef struct SilcLogStruct *SilcLog;
43 /* These are the known logging channels */
44 static struct SilcLogStruct silclogs[SILC_LOG_MAX] = {
45 {NULL, NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
46 {NULL, NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
47 {NULL, NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
48 {NULL, NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
51 /* If TRUE, log files will be flushed for each log input */
52 bool silc_log_quick = FALSE;
54 /* Set TRUE/FALSE to enable/disable debugging */
55 bool silc_debug = FALSE;
56 bool silc_debug_hexdump = FALSE;
58 /* Regular pattern matching expression for the debug output */
59 static char *silc_log_debug_string = NULL;
61 /* Debug callbacks. If set these are used instead of default ones. */
62 static SilcLogDebugCb silc_log_debug_cb = NULL;
63 static void *silc_log_debug_context = NULL;
64 static SilcLogHexdumpCb silc_log_hexdump_cb = NULL;
65 static void *silc_log_hexdump_context = NULL;
67 /* Did we register already our functions to the scheduler? */
68 static bool silc_log_scheduled = FALSE;
69 static bool silc_log_no_init = FALSE;
71 /* The type wrapper utility. Translates a SilcLogType id to the corresponding
72 * logfile, or NULL if not found. */
73 static SilcLog silc_log_find_by_type(SilcLogType type)
75 /* this is not really needed, but i think it's more secure */
78 return &silclogs[SILC_LOG_INFO];
79 case SILC_LOG_WARNING:
80 return &silclogs[SILC_LOG_WARNING];
82 return &silclogs[SILC_LOG_ERROR];
84 return &silclogs[SILC_LOG_FATAL];
91 /* Given an open log file, checks the size and rotates it if there is a
92 * max size set less then the current size */
93 static void silc_log_checksize(SilcLog log)
98 if (!log || !log->fp || !log->maxsize)
99 return; /* we are not interested */
100 if ((size = ftell(log->fp)) < 0) {
101 /* OMG, EBADF is here.. we'll try our best.. */
102 FILE *oldfp = log->fp;
103 fclose(oldfp); /* we can discard the error */
104 log->fp = NULL; /* make sure we don't get here recursively */
105 SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%d",
106 log->filename, oldfp));
109 if (size < log->maxsize) return;
112 fprintf(log->fp, "[%s] [%s] Cycling log file, over max "
113 "logsize (%lu kilobytes)\n",
114 silc_get_time(), log->typename, log->maxsize / 1024);
117 snprintf(newname, sizeof(newname), "%s.old", log->filename);
120 /* I heard the following syscall may cause portability issues, but I don't
121 * have any other solution since SILC library doesn't provide any other
122 * function like this. -Johnny */
123 rename(log->filename, newname);
124 if (!(log->fp = fopen(log->filename, "w")))
125 SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s",
126 log->filename, log->typename, strerror(errno)));
129 /* Reset a logging channel (close and reopen) */
131 static bool silc_log_reset(SilcLog log)
133 if (!log) return FALSE;
138 if (!(log->fp = fopen(log->filename, "a+"))) {
139 SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s",
140 log->filename, log->typename, strerror(errno)));
146 /* Internal timeout callback to flush log channels and check file sizes */
148 SILC_TASK_CALLBACK(silc_log_fflush_callback)
151 if (!silc_log_quick) {
152 silc_log_flush_all();
154 silc_log_checksize(&silclogs[u]);
156 silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback,
157 context, SILC_LOG_TIMEOUT, 0, SILC_TASK_TIMEOUT,
158 SILC_TASK_PRI_NORMAL);
161 /* Outputs the log message to the first available channel. Channels are
162 * ordered by importance (see SilcLogType documentation).
163 * More importants channels can be printed on less important ones, but not
166 void silc_log_output(SilcLogType type, char *string)
171 if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type)))
174 /* If there is a custom callback set, use it and return. */
176 if ((*log->cb)(type, string, log->context))
180 if (!silc_log_scheduled) {
181 if (silc_log_no_init == FALSE) {
183 "Warning, trying to output without log files initialization, "
184 "log output is going to stderr\n");
185 silc_log_no_init = TRUE;
188 fprintf(stderr, "%s\n", string);
192 /* save the original typename, because if we redirect the channel we
193 * keep however the original destination channel name */
194 typename = log->typename;
196 /* ok, now we have to find an open stream */
198 if (log && log->fp) goto found;
199 if (type == 0) break;
200 log = silc_log_find_by_type(--type);
203 /* Couldn't find any open stream.. sorry :( */
204 SILC_LOG_DEBUG(("Warning! couldn't find any available log channel!"));
208 fprintf(log->fp, "[%s] [%s] %s\n", silc_get_time(), typename, string);
209 if (silc_log_quick) {
211 silc_log_checksize(log);
218 /* returns an internally allocated pointer to a logging channel file */
219 char *silc_log_get_file(SilcLogType type)
223 if (!(log = silc_log_find_by_type(type)))
226 return log->filename;
230 /* Set and initialize the specified logging channel. See the API reference */
231 bool silc_log_set_file(SilcLogType type, char *filename, uint32 maxsize,
232 SilcSchedule scheduler)
237 log = silc_log_find_by_type(type);
238 if (!log || !scheduler)
241 SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)",
242 log->typename, filename, maxsize));
244 /* before assuming the new file, make sure we can open it */
246 if (!(fp = fopen(filename, "a+"))) {
247 fprintf(stderr, "warning: couldn't open log file %s: %s\n",
248 filename, strerror(errno));
253 /* remove old file */
259 silc_free(log->filename);
260 log->filename = NULL;
265 log->filename = strdup(filename);
267 log->maxsize = maxsize;
270 if (silc_log_scheduled)
273 /* make sure we write to the disk sometimes */
274 silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
275 (void *) scheduler, SILC_LOG_TIMEOUT, 0,
276 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
278 silc_log_scheduled = TRUE;
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)
289 if (!(log = silc_log_find_by_type(type)))
293 log->context = context;
296 /* Resets log callbacks */
298 void silc_log_reset_callbacks()
301 SILC_FOREACH_LOG(u) {
302 silclogs[u].cb = NULL;
303 silclogs[u].context = NULL;
307 /* Flushes all opened logging channels */
309 void silc_log_flush_all() {
311 SILC_LOG_DEBUG(("Flushing all logs"));
312 SILC_FOREACH_LOG(u) {
314 fflush(silclogs[u].fp);
318 /* Resets all known logging channels */
320 void silc_log_reset_all() {
322 SILC_LOG_DEBUG(("Resetting all logs"));
324 silc_log_reset(&silclogs[u]);
327 /* Outputs the debug message to stderr. */
329 void silc_log_output_debug(char *file, char *function,
330 int line, char *string)
334 if (silc_log_debug_string &&
335 !silc_string_regex_match(silc_log_debug_string, file) &&
336 !silc_string_regex_match(silc_log_debug_string, function))
338 if (silc_log_debug_cb) {
339 if ((*silc_log_debug_cb)(file, function, line, string,
340 silc_log_debug_context))
343 fprintf(stderr, "%s:%d: %s\n", function, line, string);
349 /* Hexdumps a message */
351 void silc_log_output_hexdump(char *file, char *function,
352 int line, void *data_in,
353 uint32 len, char *string)
357 unsigned char *data = (unsigned char *)data_in;
359 if (!silc_debug_hexdump)
361 if (silc_log_debug_string &&
362 !silc_string_regex_match(silc_log_debug_string, file) &&
363 !silc_string_regex_match(silc_log_debug_string, function))
365 if (silc_log_hexdump_cb) {
366 if ((*silc_log_hexdump_cb)(file, function, line, data_in, len, string,
367 silc_log_hexdump_context))
371 fprintf(stderr, "%s:%d: %s\n", function, line, string);
380 if ((len - pos) < 16 && (len - pos <= len - off))
390 fprintf(stderr, "%08X ", k++ * 16);
392 for (i = 0; i < count; i++) {
393 fprintf(stderr, "%02X ", data[pos + i]);
395 if ((i + 1) % 4 == 0)
396 fprintf(stderr, " ");
399 if (count && count < 16) {
402 for (j = 0; j < 16 - count; j++) {
403 fprintf(stderr, " ");
405 if ((j + count + 1) % 4 == 0)
406 fprintf(stderr, " ");
410 for (i = 0; i < count; i++) {
413 if (data[pos] < 32 || data[pos] >= 127)
418 fprintf(stderr, "%c", ch);
423 fprintf(stderr, "\n");
434 /* Sets debug callbacks */
436 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
438 SilcLogHexdumpCb hexdump_cb,
439 void *hexdump_context)
441 silc_log_debug_cb = debug_cb;
442 silc_log_debug_context = debug_context;
443 silc_log_hexdump_cb = hexdump_cb;
444 silc_log_hexdump_context = hexdump_context;
447 /* Resets debug callbacks */
449 void silc_log_reset_debug_callbacks()
451 silc_log_debug_cb = NULL;
452 silc_log_debug_context = NULL;
453 silc_log_hexdump_cb = NULL;
454 silc_log_hexdump_context = NULL;
457 /* Set current debug string */
459 void silc_log_set_debug_string(const char *debug_string)
461 silc_free(silc_log_debug_string);
462 if ((strchr(debug_string, '(') &&
463 strchr(debug_string, ')')) ||
464 strchr(debug_string, '$'))
465 silc_log_debug_string = strdup(debug_string);
467 silc_log_debug_string = silc_string_regexify(debug_string);