5 Author: Giovanni Giacobbi <giovanni@giacobbi.net>
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; 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 /* Minimum time delay for log flushing calls (in seconds) */
25 #define SILC_LOG_FLUSH_MIN_DELAY 2
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. We initialize this struct with most
44 * of the fields set to NULL, because we'll fill in those values at runtime. */
45 static struct SilcLogStruct silclogs[SILC_LOG_MAX] = {
46 {"", NULL, 0, "Info", SILC_LOG_INFO, NULL, NULL},
47 {"", NULL, 0, "Warning", SILC_LOG_WARNING, NULL, NULL},
48 {"", NULL, 0, "Error", SILC_LOG_ERROR, NULL, NULL},
49 {"", NULL, 0, "Fatal", SILC_LOG_FATAL, NULL, NULL},
52 /* Causes logging output to contain timestamps */
53 bool silc_log_timestamp = TRUE;
55 /* If TRUE, log files will be flushed for each log input */
56 bool silc_log_quick = FALSE;
58 /* Set TRUE/FALSE to enable/disable debugging */
59 bool silc_debug = FALSE;
60 bool silc_debug_hexdump = FALSE;
62 /* Flush delay (in seconds) */
63 long silc_log_flushdelay = 300;
65 /* Regular pattern matching expression for the debug output */
66 char silc_log_debug_string[128];
68 /* Debug callbacks. If set, these are triggered for each specific output. */
69 static SilcLogDebugCb silc_log_debug_cb = NULL;
70 static void *silc_log_debug_context = NULL;
71 static SilcLogHexdumpCb silc_log_hexdump_cb = NULL;
72 static void *silc_log_hexdump_context = NULL;
74 /* Did we register already our functions to the scheduler? */
75 static bool silc_log_scheduled = FALSE;
76 static bool silc_log_no_init = FALSE;
78 /* This is only needed during starting up -- don't lose any logging message */
79 static bool silc_log_starting = TRUE;
81 /* The type wrapper utility. Translates a SilcLogType id to the corresponding
82 * logfile, or NULL if not found. */
83 static SilcLog silc_log_find_by_type(SilcLogType type)
85 /* this is not really needed, but i think it's more secure */
88 return &silclogs[SILC_LOG_INFO];
89 case SILC_LOG_WARNING:
90 return &silclogs[SILC_LOG_WARNING];
92 return &silclogs[SILC_LOG_ERROR];
94 return &silclogs[SILC_LOG_FATAL];
101 /* Given an open log file, checks the size and rotates it if there is a
102 * max size set lower then the current size */
103 static void silc_log_checksize(SilcLog log)
108 if (!log || !log->fp || !log->maxsize)
109 return; /* we are not interested */
111 if ((size = ftell(log->fp)) < 0) {
112 /* OMG, EBADF is here.. we'll try our best.. */
113 FILE *oldfp = log->fp;
114 fclose(oldfp); /* we can discard the error */
115 log->fp = NULL; /* make sure we don't get here recursively */
116 SILC_LOG_ERROR(("Error while checking size of the log file %s, fp=%p",
117 log->filename, oldfp));
120 if (size < log->maxsize)
124 fprintf(log->fp, "[%s] [%s] Cycling log file, over max "
125 "logsize (%lu kilobytes)\n",
126 silc_get_time(0), log->typename, (unsigned long)log->maxsize / 1024);
129 memset(newname, 0, sizeof(newname));
130 snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
133 /* I heard the following syscall may cause portability issues, but I don't
134 * have any other solution since SILC library doesn't provide any other
135 * function like this. -Johnny */
136 rename(log->filename, newname);
137 if (!(log->fp = fopen(log->filename, "w")))
138 SILC_LOG_WARNING(("Couldn't reopen logfile %s for type \"%s\": %s",
139 log->filename, log->typename, strerror(errno)));
141 chmod(log->filename, 0600);
142 #endif /* HAVE_CHMOD */
145 /* Reset a logging channel (close and reopen) */
147 static bool silc_log_reset(SilcLog log)
149 if (!log) return FALSE;
154 if (!log->filename[0]) return FALSE;
155 if (!(log->fp = fopen(log->filename, "a+"))) {
156 SILC_LOG_WARNING(("Couldn't reset logfile %s for type \"%s\": %s",
157 log->filename, log->typename, strerror(errno)));
164 /* Internal timeout callback to flush log channels and check file sizes */
166 SILC_TASK_CALLBACK(silc_log_fflush_callback)
169 if (!silc_log_quick) {
170 silc_log_flush_all();
172 silc_log_checksize(&silclogs[u]);
174 silc_log_starting = FALSE;
175 if (silc_log_flushdelay < SILC_LOG_FLUSH_MIN_DELAY)
176 silc_log_flushdelay = SILC_LOG_FLUSH_MIN_DELAY;
177 silc_schedule_task_add((SilcSchedule) context, 0, silc_log_fflush_callback,
178 context, silc_log_flushdelay, 0, SILC_TASK_TIMEOUT,
179 SILC_TASK_PRI_NORMAL);
182 /* Outputs the log message to the first available channel. Channels are
183 * ordered by importance (see SilcLogType documentation).
184 * More important channels can be printed on less important ones, but not
187 void silc_log_output(SilcLogType type, char *string)
189 const char *typename = NULL;
193 if ((type > SILC_LOG_MAX) || !(log = silc_log_find_by_type(type)))
196 /* Save the original typename, because even if we redirect the message
197 * to another channel we'll keep however the original channel name */
198 typename = log->typename;
200 /* If there is a custom callback set, use it and return. */
202 if ((*log->cb)(type, string, log->context))
206 if (!silc_log_scheduled) {
207 if (silc_log_no_init == FALSE) {
209 "Warning, trying to output without log files initialization, "
210 "log output is going to stderr\n");
211 silc_log_no_init = TRUE;
213 /* redirect output */
219 /* ok, now we have to find an open stream */
221 if (log && (fp = log->fp)) goto found;
222 if (type == 0) break;
223 log = silc_log_find_by_type(--type);
226 /* Couldn't find any open stream.. sorry :( */
227 SILC_LOG_DEBUG(("Warning! couldn't find any available log channel!"));
231 /* writes the logging string to the selected channel */
232 if (silc_log_timestamp)
233 fprintf(fp, "[%s] [%s] %s\n", silc_get_time(0), typename, string);
235 fprintf(fp, "[%s] %s\n", typename, string);
237 if (silc_log_quick || silc_log_starting) {
239 if (log) /* we may have been redirected to stderr */
240 silc_log_checksize(log);
244 /* If debugging, also output the logging message to the console */
245 if (typename && silc_debug) {
246 fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
252 /* returns an internally allocated pointer to a logging channel file */
253 char *silc_log_get_file(SilcLogType type)
257 if (!(log = silc_log_find_by_type(type)))
260 return log->filename;
264 /* Set and initialize the specified logging channel. See the API reference */
265 bool silc_log_set_file(SilcLogType type, char *filename, SilcUInt32 maxsize,
266 SilcSchedule scheduler)
271 log = silc_log_find_by_type(type);
275 SILC_LOG_DEBUG(("Setting \"%s\" file to %s (max size=%d)",
276 log->typename, filename, maxsize));
278 /* before assuming the new file, make sure we can open it */
280 if (!(fp = fopen(filename, "a+"))) {
281 fprintf(stderr, "warning: couldn't open log file %s: %s\n",
282 filename, strerror(errno));
286 chmod(filename, 0600);
287 #endif /* HAVE_CHMOD */
290 /* clean the logging channel */
291 if (strlen(log->filename)) {
294 memset(log->filename, 0, sizeof(log->filename));
299 memset(log->filename, 0, sizeof(log->filename));
300 strncpy(log->filename, filename,
301 strlen(filename) < sizeof(log->filename) ? strlen(filename) :
302 sizeof(log->filename) - 1);
304 log->maxsize = maxsize;
308 if (silc_log_scheduled)
311 /* Add schedule hook with a short delay to make sure we'll use
313 silc_schedule_task_add(scheduler, 0, silc_log_fflush_callback,
314 (void *) scheduler, 10, 0,
315 SILC_TASK_TIMEOUT, SILC_TASK_PRI_NORMAL);
316 silc_log_scheduled = TRUE;
322 /* Sets a log callback, set callback to NULL to return to default behaviour */
324 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
328 if (!(log = silc_log_find_by_type(type)))
332 log->context = context;
335 /* Resets log callbacks */
337 void silc_log_reset_callbacks()
340 SILC_FOREACH_LOG(u) {
341 silclogs[u].cb = NULL;
342 silclogs[u].context = NULL;
346 /* Flushes all opened logging channels */
348 void silc_log_flush_all() {
350 SILC_LOG_DEBUG(("Flushing all logs"));
351 SILC_FOREACH_LOG(u) {
353 fflush(silclogs[u].fp);
357 /* Resets all known logging channels */
359 void silc_log_reset_all() {
361 SILC_LOG_DEBUG(("Resetting all logs"));
363 silc_log_reset(&silclogs[u]);
364 /* Immediately flush any possible warning message */
365 silc_log_flush_all();
368 /* Outputs the debug message to stderr. */
370 void silc_log_output_debug(char *file, const char *function,
371 int line, char *string)
376 if (silc_log_debug_string &&
377 !silc_string_regex_match(silc_log_debug_string, file) &&
378 !silc_string_regex_match(silc_log_debug_string, function))
381 if (silc_log_debug_cb) {
382 if ((*silc_log_debug_cb)(file, (char *)function, line, string,
383 silc_log_debug_context))
387 fprintf(stderr, "%s:%d: %s\n", function, line, string);
394 /* Hexdumps a message */
396 void silc_log_output_hexdump(char *file, const char *function,
397 int line, void *data_in,
398 SilcUInt32 len, char *string)
402 unsigned char *data = (unsigned char *)data_in;
404 if (!silc_debug_hexdump)
407 if (silc_log_debug_string &&
408 !silc_string_regex_match(silc_log_debug_string, file) &&
409 !silc_string_regex_match(silc_log_debug_string, function))
412 if (silc_log_hexdump_cb) {
413 if ((*silc_log_hexdump_cb)(file, (char *)function, line,
414 data_in, len, string,
415 silc_log_hexdump_context))
419 fprintf(stderr, "%s:%d: %s\n", function, line, string);
427 if ((len - pos) < 16 && (len - pos <= len - off))
437 fprintf(stderr, "%08X ", k++ * 16);
439 for (i = 0; i < count; i++) {
440 fprintf(stderr, "%02X ", data[pos + i]);
442 if ((i + 1) % 4 == 0)
443 fprintf(stderr, " ");
446 if (count && count < 16) {
449 for (j = 0; j < 16 - count; j++) {
450 fprintf(stderr, " ");
452 if ((j + count + 1) % 4 == 0)
453 fprintf(stderr, " ");
457 for (i = 0; i < count; i++) {
460 if (data[pos] < 32 || data[pos] >= 127)
465 fprintf(stderr, "%c", ch);
470 fprintf(stderr, "\n");
480 /* Sets debug callbacks */
482 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
484 SilcLogHexdumpCb hexdump_cb,
485 void *hexdump_context)
487 silc_log_debug_cb = debug_cb;
488 silc_log_debug_context = debug_context;
489 silc_log_hexdump_cb = hexdump_cb;
490 silc_log_hexdump_context = hexdump_context;
493 /* Resets debug callbacks */
495 void silc_log_reset_debug_callbacks()
497 silc_log_debug_cb = NULL;
498 silc_log_debug_context = NULL;
499 silc_log_hexdump_cb = NULL;
500 silc_log_hexdump_context = NULL;
503 /* Set current debug string */
505 void silc_log_set_debug_string(const char *debug_string)
509 if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
510 strchr(debug_string, '$'))
511 string = strdup(debug_string);
513 string = silc_string_regexify(debug_string);
514 len = strlen(string);
515 if (len >= sizeof(silc_log_debug_string))
516 len = sizeof(silc_log_debug_string) - 1;
517 memset(silc_log_debug_string, 0, sizeof(silc_log_debug_string));
518 strncpy(silc_log_debug_string, string, len);