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