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