Added SILC Thread Queue API
[silc.git] / lib / silcutil / silclog.c
1 /*
2
3   silclog.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8
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.
12
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.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 /* SilcLogSettings context */
24 typedef struct {
25   SilcUInt32 flushdelay;
26
27   char debug_string[128];
28   SilcLogDebugCb debug_cb;
29   void *debug_context;
30   SilcLogHexdumpCb hexdump_cb;
31   void *hexdump_context;
32
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;
41
42 /* SilcLog context */
43 typedef struct {
44   char filename[256];
45   FILE *fp;
46   SilcUInt64 maxsize;
47   const char *typename;
48   SilcLogType type;
49   SilcLogCb cb;
50   void *context;
51 } *SilcLog, SilcLogStruct;
52
53 #ifndef SILC_SYMBIAN
54
55 /* Default settings */
56 static SilcLogSettingsStruct silclog =
57 {
58   300,
59   { 0 },
60   NULL, NULL,
61   NULL, NULL,
62   TRUE,
63   FALSE,
64   FALSE,
65   FALSE,
66   FALSE,
67   FALSE,
68   TRUE,
69 };
70
71 #endif /* !SILC_SYMBIAN */
72
73 /* Default log contexts */
74 #ifndef SILC_SYMBIAN
75 static SilcLogStruct silclogs[4] =
76 #else
77 const SilcLogStruct silclogs[4] =
78 #endif /* !SILC_SYMBIAN */
79 {
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},
84 };
85
86 /* Return log context by type */
87
88 static SilcLog silc_log_get_context(SilcLogType type)
89 {
90   if (type < 1 || type > 4)
91     return NULL;
92   return (SilcLog)&silclogs[(int)type - 1];
93 }
94
95 /* Check log file site and cycle log file if it is over max size. */
96
97 static void silc_log_checksize(SilcLog log)
98 {
99   char newname[256];
100   SilcUInt64 size;
101
102   if (!log || !log->fp || !log->maxsize)
103     return;
104
105   size = silc_file_size(log->filename);
106   if (!size) {
107     fclose(log->fp);
108     log->fp = NULL;
109   }
110
111   if (size < log->maxsize)
112     return;
113
114   /* Cycle log file */
115   fprintf(log->fp,
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);
119   fflush(log->fp);
120   fclose(log->fp);
121
122   memset(newname, 0, sizeof(newname));
123   silc_snprintf(newname, sizeof(newname) - 1, "%s.old", log->filename);
124   unlink(newname);
125   rename(log->filename, newname);
126
127   log->fp = fopen(log->filename, "w");
128   if (!log->fp)
129     SILC_LOG_WARNING(("Couldn't reopen log file '%s' for type '%s': %s",
130                       log->filename, log->typename, strerror(errno)));
131 #ifdef HAVE_CHMOD
132   chmod(log->filename, 0600);
133 #endif /* HAVE_CHMOD */
134 }
135
136 /* Internal timeout callback to flush log channels and check file sizes */
137
138 SILC_TASK_CALLBACK(silc_log_fflush_callback)
139 {
140 #ifndef SILC_SYMBIAN
141   SilcLog log;
142
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);
153   }
154
155   silclog.starting = FALSE;
156
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 */
162 }
163
164 /* Output log message to log file */
165
166 void silc_log_output(SilcLogType type, char *string)
167 {
168   const char *typename = NULL;
169   SilcLog log = silc_log_get_context(type);
170   FILE *fp;
171
172   if (!log)
173     goto end;
174
175   /* Forward to callback if set */
176   if (log->cb)
177     if ((*log->cb)(type, string, log->context))
178       goto end;
179
180   typename = log->typename;
181
182 #ifndef SILC_SYMBIAN
183   if (!silclog.scheduled) {
184     if (silclog.no_init == FALSE) {
185       fprintf(stderr,
186               "Warning, log files not initialized, "
187               "log output is going to stderr\n");
188       silclog.no_init = TRUE;
189     }
190
191     fp = stderr;
192     log = NULL;
193     goto found;
194   }
195 #endif /* !SILC_SYMBIAN */
196
197   /* Find open log file */
198   while (log) {
199     if (log->fp) {
200       fp = log->fp;
201       break;
202     }
203
204     log = silc_log_get_context(--type);
205   }
206   if (!log || !log->fp)
207     goto end;
208
209 #ifndef SILC_SYMBIAN
210  found:
211   if (silclog.timestamp)
212     fprintf(fp, "[%s] [%s] %s\n", silc_time_string(0), typename, string);
213   else
214     fprintf(fp, "[%s] %s\n", typename, string);
215
216   if (silclog.quick || silclog.starting) {
217     fflush(fp);
218     if (log)
219       silc_log_checksize(log);
220   }
221 #endif /* !SILC_SYMBIAN */
222
223  end:
224 #ifndef SILC_SYMBIAN
225   /* Output log to stderr if debugging */
226   if (typename && silclog.debug) {
227     fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
228     fflush(stderr);
229   }
230 #else
231   fprintf(stderr, "[Logging] [%s] %s\n", typename, string);
232 #endif /* !SILC_SYMBIAN */
233
234   silc_free(string);
235 }
236
237 /* Set and initialize the specified log file. */
238
239 SilcBool silc_log_set_file(SilcLogType type, char *filename,
240                            SilcUInt32 maxsize, SilcSchedule scheduler)
241 {
242 #ifndef SILC_SYMBIAN
243   FILE *fp = NULL;
244   SilcLog log;
245
246   if (!scheduler)
247     scheduler = silc_schedule_get_global();
248
249   log = silc_log_get_context(type);
250   if (!log)
251     return FALSE;
252
253   SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
254                   log->typename, filename, maxsize));
255
256   /* Open log file */
257   if (filename) {
258     fp = fopen(filename, "a+");
259     if (!fp) {
260       fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
261               filename, strerror(errno));
262       return FALSE;
263     }
264 #ifdef HAVE_CHMOD
265     chmod(filename, 0600);
266 #endif /* HAVE_CHMOD */
267   }
268
269   /* Close previous log file if it exists */
270   if (strlen(log->filename)) {
271     if (log->fp)
272       fclose(log->fp);
273     memset(log->filename, 0, sizeof(log->filename));
274     log->fp = NULL;
275   }
276
277   /* Set new log file */
278   if (fp) {
279     log->fp = fp;
280     log->maxsize = maxsize;
281
282     memset(log->filename, 0, sizeof(log->filename));
283     silc_strncat(log->filename, sizeof(log->filename), filename,
284                  strlen(filename));
285   }
286
287   /* Add flush timeout */
288   if (scheduler) {
289     silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
290     silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
291                                    scheduler, 10, 0);
292     silclog.scheduled = TRUE;
293   }
294
295 #endif /* !SILC_SYMBIAN */
296   return TRUE;
297 }
298
299 /* Return log filename */
300
301 char *silc_log_get_file(SilcLogType type)
302 {
303   SilcLog log = silc_log_get_context(type);
304   return log && log->fp ? log->filename : NULL;
305 }
306
307 /* Sets a log callback, set callback to NULL to return to default behaviour */
308
309 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
310 {
311 #ifndef SILC_SYMBIAN
312   SilcLog log = silc_log_get_context(type);
313   if (log) {
314     log->cb = cb;
315     log->context = context;
316   }
317 #endif /* !SILC_SYMBIAN */
318 }
319
320 /* Reset log callbacks */
321
322 void silc_log_reset_callbacks(void)
323 {
324 #ifndef SILC_SYMBIAN
325   SilcLog log;
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 */
335 }
336
337 /* Flush all log files */
338
339 void silc_log_flush_all(void)
340 {
341   SilcLog log;
342   log = silc_log_get_context(SILC_LOG_INFO);
343   if (log->fp)
344     fflush(log->fp);
345   log = silc_log_get_context(SILC_LOG_WARNING);
346   if (log->fp)
347     fflush(log->fp);
348   log = silc_log_get_context(SILC_LOG_ERROR);
349   if (log->fp)
350     fflush(log->fp);
351   log = silc_log_get_context(SILC_LOG_FATAL);
352   if (log->fp)
353     fflush(log->fp);
354 }
355
356 /* Reset a log file */
357
358 static void silc_log_reset(SilcLog log)
359 {
360   if (log->fp) {
361     fflush(log->fp);
362     fclose(log->fp);
363   }
364
365   if (!strlen(log->filename))
366     return;
367
368   log->fp = fopen(log->filename, "a+");
369   if (!log->fp)
370     SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
371                       log->filename, log->typename, strerror(errno)));
372 }
373
374 /* Reset all log files */
375
376 void silc_log_reset_all(void)
377 {
378   SilcLog log;
379   log = silc_log_get_context(SILC_LOG_INFO);
380   if (log->fp)
381     silc_log_reset(log);
382   log = silc_log_get_context(SILC_LOG_WARNING);
383   if (log->fp)
384     silc_log_reset(log);
385   log = silc_log_get_context(SILC_LOG_ERROR);
386   if (log->fp)
387     silc_log_reset(log);
388   log = silc_log_get_context(SILC_LOG_FATAL);
389   if (log->fp)
390     silc_log_reset(log);
391   silc_log_flush_all();
392 }
393
394 /* Sets debug callbacks */
395
396 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
397                                   void *debug_context,
398                                   SilcLogHexdumpCb hexdump_cb,
399                                   void *hexdump_context)
400 {
401 #ifndef SILC_SYMBIAN
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 */
407 }
408
409 /* Resets debug callbacks */
410
411 void silc_log_reset_debug_callbacks()
412 {
413 #ifndef SILC_SYMBIAN
414   silclog.debug_cb = NULL;
415   silclog.debug_context = NULL;
416   silclog.hexdump_cb = NULL;
417   silclog.hexdump_context = NULL;
418 #endif /* !SILC_SYMBIAN */
419 }
420
421 /* Set current debug string */
422
423 void silc_log_set_debug_string(const char *debug_string)
424 {
425 #ifndef SILC_SYMBIAN
426   char *string;
427   int len;
428   if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
429       strchr(debug_string, '$'))
430     string = silc_strdup(debug_string);
431   else
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);
438   silc_free(string);
439 #endif /* !SILC_SYMBIAN */
440 }
441
442 /* Set timestamp */
443
444 void silc_log_timestamp(SilcBool enable)
445 {
446 #ifndef SILC_SYMBIAN
447   silclog.timestamp = enable;
448 #endif /* !SILC_SYMBIAN */
449 }
450
451 /* Set flushdelay */
452
453 void silc_log_flushdelay(SilcUInt32 flushdelay)
454 {
455 #ifndef SILC_SYMBIAN
456   silclog.flushdelay = flushdelay;
457 #endif /* !SILC_SYMBIAN */
458 }
459
460 /* Set quick logging */
461
462 void silc_log_quick(SilcBool enable)
463 {
464 #ifndef SILC_SYMBIAN
465   silclog.quick = enable;
466 #endif /* !SILC_SYMBIAN */
467 }
468
469 /* Set debugging */
470
471 void silc_log_debug(SilcBool enable)
472 {
473 #ifndef SILC_SYMBIAN
474   silclog.debug = enable;
475 #endif /* !SILC_SYMBIAN */
476 }
477
478 /* Set debug hexdump */
479
480 void silc_log_debug_hexdump(SilcBool enable)
481 {
482 #ifndef SILC_SYMBIAN
483   silclog.debug_hexdump = enable;
484 #endif /* !SILC_SYMBIAN */
485 }
486
487 /* Outputs the debug message to stderr. */
488
489 void silc_log_output_debug(char *file, const char *function,
490                            int line, char *string)
491 {
492   SilcTimeStruct curtime;
493
494 #ifndef SILC_SYMBIAN
495   if (!silclog.debug)
496     goto end;
497
498   if (!silc_string_regex_match(silclog.debug_string, file) &&
499       !silc_string_regex_match(silclog.debug_string, function))
500     goto end;
501
502   if (silclog.debug_cb) {
503     if ((*silclog.debug_cb)(file, (char *)function, line, string,
504                             silclog.debug_context))
505       goto end;
506   }
507 #endif /* !SILC_SYMBIAN */
508
509   silc_time_value(0, &curtime);
510
511 #ifdef SILC_WIN32
512   if (strrchr(function, '\\'))
513     fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
514   else
515 #endif /* SILC_WIN32 */
516 #ifdef SILC_SYMBIAN
517   silc_symbian_debug(function, line, string);
518 #else
519   fprintf(stderr, "%02d:%02d:%02d %s:%d: %s\n", curtime.hour,
520           curtime.minute, curtime.second, function, line,
521           string);
522   fflush(stderr);
523 #endif /* SILC_SYMBIAN */
524
525  end:
526   silc_free(string);
527 }
528
529 /* Hexdumps a message */
530
531 void silc_log_output_hexdump(char *file, const char *function,
532                              int line, void *data_in,
533                              SilcUInt32 len, char *string)
534 {
535 #ifndef SILC_SYMBIAN
536   if (!silclog.debug_hexdump)
537     goto end;
538
539   if (!silc_string_regex_match(silclog.debug_string, file) &&
540       !silc_string_regex_match(silclog.debug_string, function))
541     goto end;
542
543   if (silclog.hexdump_cb) {
544     if ((*silclog.hexdump_cb)(file, (char *)function, line,
545                               data_in, len, string, silclog.hexdump_context))
546       goto end;
547   }
548 #endif /* !SILC_SYMBIAN */
549
550   fprintf(stderr, "%s:%d: %s\n", function, line, string);
551
552   silc_hexdump(data_in, len, stderr);
553
554  end:
555   silc_free(string);
556 }