1 /*
2 
3   silclog.c
4 
5   Author: Pekka Riikonen <priikone@silcnet.org>
6 
7   Copyright (C) 1997 - 2007 Pekka Riikonen
8 
9   The contents of this file are subject to one of the Licenses specified
10   in the COPYING file;  You may not use this file except in compliance
11   with the License.
12 
13   The software distributed under the License is distributed on an "AS IS"
14   basis, in the hope that it will be useful, but WITHOUT WARRANTY OF ANY
15   KIND, either expressed or implied.  See the COPYING file for more
16   information.
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 
silc_log_get_context(SilcLogType type)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 
silc_log_checksize(SilcLog log)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 
SILC_TASK_CALLBACK(silc_log_fflush_callback)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 
silc_log_output(SilcLogType type,char * string)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, trying to output without log files initialization, "
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 
silc_log_set_file(SilcLogType type,char * filename,SilcUInt32 maxsize,SilcSchedule scheduler)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   log = silc_log_get_context(type);
247   if (!log)
248     return FALSE;
249 
250   SILC_LOG_DEBUG(("Setting '%s' file to %s (max size=%d)",
251 		  log->typename, filename, maxsize));
252 
253   /* Open log file */
254   if (filename) {
255     fp = fopen(filename, "a+");
256     if (!fp) {
257       fprintf(stderr, "warning: couldn't open log file '%s': %s\n",
258 	      filename, strerror(errno));
259       return FALSE;
260     }
261 #ifdef HAVE_CHMOD
262     chmod(filename, 0600);
263 #endif /* HAVE_CHMOD */
264   }
265 
266   /* Close previous log file if it exists */
267   if (strlen(log->filename)) {
268     if (log->fp)
269       fclose(log->fp);
270     memset(log->filename, 0, sizeof(log->filename));
271     log->fp = NULL;
272   }
273 
274   /* Set new log file */
275   if (fp) {
276     log->fp = fp;
277     log->maxsize = maxsize;
278 
279     memset(log->filename, 0, sizeof(log->filename));
280     silc_strncat(log->filename, sizeof(log->filename), filename,
281 		 strlen(filename));
282   }
283 
284   /* Add flush timeout */
285   if (scheduler) {
286     silc_schedule_task_del_by_callback(scheduler, silc_log_fflush_callback);
287     silc_schedule_task_add_timeout(scheduler, silc_log_fflush_callback,
288 				   scheduler, 10, 0);
289     silclog.scheduled = TRUE;
290   }
291 
292 #endif /* !SILC_SYMBIAN */
293   return TRUE;
294 }
295 
296 /* Return log filename */
297 
silc_log_get_file(SilcLogType type)298 char *silc_log_get_file(SilcLogType type)
299 {
300   SilcLog log = silc_log_get_context(type);
301   return log && log->fp ? log->filename : NULL;
302 }
303 
304 /* Sets a log callback, set callback to NULL to return to default behaviour */
305 
silc_log_set_callback(SilcLogType type,SilcLogCb cb,void * context)306 void silc_log_set_callback(SilcLogType type, SilcLogCb cb, void *context)
307 {
308 #ifndef SILC_SYMBIAN
309   SilcLog log = silc_log_get_context(type);
310   if (log) {
311     log->cb = cb;
312     log->context = context;
313   }
314 #endif /* !SILC_SYMBIAN */
315 }
316 
317 /* Reset log callbacks */
318 
silc_log_reset_callbacks(void)319 void silc_log_reset_callbacks(void)
320 {
321 #ifndef SILC_SYMBIAN
322   SilcLog log;
323   log = silc_log_get_context(SILC_LOG_INFO);
324   log->cb = log->context = NULL;
325   log = silc_log_get_context(SILC_LOG_WARNING);
326   log->cb = log->context = NULL;
327   log = silc_log_get_context(SILC_LOG_ERROR);
328   log->cb = log->context = NULL;
329   log = silc_log_get_context(SILC_LOG_FATAL);
330   log->cb = log->context = NULL;
331 #endif /* !SILC_SYMBIAN */
332 }
333 
334 /* Flush all log files */
335 
silc_log_flush_all(void)336 void silc_log_flush_all(void)
337 {
338   SilcLog log;
339   log = silc_log_get_context(SILC_LOG_INFO);
340   if (log->fp)
341     fflush(log->fp);
342   log = silc_log_get_context(SILC_LOG_WARNING);
343   if (log->fp)
344     fflush(log->fp);
345   log = silc_log_get_context(SILC_LOG_ERROR);
346   if (log->fp)
347     fflush(log->fp);
348   log = silc_log_get_context(SILC_LOG_FATAL);
349   if (log->fp)
350     fflush(log->fp);
351 }
352 
353 /* Reset a log file */
354 
silc_log_reset(SilcLog log)355 static void silc_log_reset(SilcLog log)
356 {
357   if (log->fp) {
358     fflush(log->fp);
359     fclose(log->fp);
360   }
361 
362   if (!strlen(log->filename))
363     return;
364 
365   log->fp = fopen(log->filename, "a+");
366   if (!log->fp)
367     SILC_LOG_WARNING(("Couldn't reset log file '%s' for type '%s': %s",
368 		      log->filename, log->typename, strerror(errno)));
369 }
370 
371 /* Reset all log files */
372 
silc_log_reset_all(void)373 void silc_log_reset_all(void)
374 {
375   SilcLog log;
376   log = silc_log_get_context(SILC_LOG_INFO);
377   if (log->fp)
378     silc_log_reset(log);
379   log = silc_log_get_context(SILC_LOG_WARNING);
380   if (log->fp)
381     silc_log_reset(log);
382   log = silc_log_get_context(SILC_LOG_ERROR);
383   if (log->fp)
384     silc_log_reset(log);
385   log = silc_log_get_context(SILC_LOG_FATAL);
386   if (log->fp)
387     silc_log_reset(log);
388   silc_log_flush_all();
389 }
390 
391 /* Sets debug callbacks */
392 
silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,void * debug_context,SilcLogHexdumpCb hexdump_cb,void * hexdump_context)393 void silc_log_set_debug_callbacks(SilcLogDebugCb debug_cb,
394 				  void *debug_context,
395 				  SilcLogHexdumpCb hexdump_cb,
396 				  void *hexdump_context)
397 {
398 #ifndef SILC_SYMBIAN
399   silclog.debug_cb = debug_cb;
400   silclog.debug_context = debug_context;
401   silclog.hexdump_cb = hexdump_cb;
402   silclog.hexdump_context = hexdump_context;
403 #endif /* !SILC_SYMBIAN */
404 }
405 
406 /* Resets debug callbacks */
407 
silc_log_reset_debug_callbacks()408 void silc_log_reset_debug_callbacks()
409 {
410 #ifndef SILC_SYMBIAN
411   silclog.debug_cb = NULL;
412   silclog.debug_context = NULL;
413   silclog.hexdump_cb = NULL;
414   silclog.hexdump_context = NULL;
415 #endif /* !SILC_SYMBIAN */
416 }
417 
418 /* Set current debug string */
419 
silc_log_set_debug_string(const char * debug_string)420 void silc_log_set_debug_string(const char *debug_string)
421 {
422 #ifndef SILC_SYMBIAN
423   char *string;
424   int len;
425   if ((strchr(debug_string, '(') && strchr(debug_string, ')')) ||
426       strchr(debug_string, '$'))
427     string = strdup(debug_string);
428   else
429     string = silc_string_regexify(debug_string);
430   len = strlen(string);
431   if (len >= sizeof(silclog.debug_string))
432     len = sizeof(silclog.debug_string) - 1;
433   memset(silclog.debug_string, 0, sizeof(silclog.debug_string));
434   strncpy(silclog.debug_string, string, len);
435   silc_free(string);
436 #endif /* !SILC_SYMBIAN */
437 }
438 
439 /* Set timestamp */
440 
silc_log_timestamp(SilcBool enable)441 void silc_log_timestamp(SilcBool enable)
442 {
443 #ifndef SILC_SYMBIAN
444   silclog.timestamp = enable;
445 #endif /* !SILC_SYMBIAN */
446 }
447 
448 /* Set flushdelay */
449 
silc_log_flushdelay(SilcUInt32 flushdelay)450 void silc_log_flushdelay(SilcUInt32 flushdelay)
451 {
452 #ifndef SILC_SYMBIAN
453   silclog.flushdelay = flushdelay;
454 #endif /* !SILC_SYMBIAN */
455 }
456 
457 /* Set quick logging */
458 
silc_log_quick(SilcBool enable)459 void silc_log_quick(SilcBool enable)
460 {
461 #ifndef SILC_SYMBIAN
462   silclog.quick = enable;
463 #endif /* !SILC_SYMBIAN */
464 }
465 
466 /* Set debugging */
467 
silc_log_debug(SilcBool enable)468 void silc_log_debug(SilcBool enable)
469 {
470 #ifndef SILC_SYMBIAN
471   silclog.debug = enable;
472 #endif /* !SILC_SYMBIAN */
473 }
474 
475 /* Set debug hexdump */
476 
silc_log_debug_hexdump(SilcBool enable)477 void silc_log_debug_hexdump(SilcBool enable)
478 {
479 #ifndef SILC_SYMBIAN
480   silclog.debug_hexdump = enable;
481 #endif /* !SILC_SYMBIAN */
482 }
483 
484 /* Outputs the debug message to stderr. */
485 
silc_log_output_debug(char * file,const char * function,int line,char * string)486 void silc_log_output_debug(char *file, const char *function,
487 			   int line, char *string)
488 {
489   SilcTimeStruct curtime;
490 
491 #ifndef SILC_SYMBIAN
492   if (!silclog.debug)
493     goto end;
494 
495   if (!silc_string_regex_match(silclog.debug_string, file) &&
496       !silc_string_regex_match(silclog.debug_string, function))
497     goto end;
498 
499   if (silclog.debug_cb) {
500     if ((*silclog.debug_cb)(file, (char *)function, line, string,
501 			    silclog.debug_context))
502       goto end;
503   }
504 #endif /* !SILC_SYMBIAN */
505 
506   silc_time_value(0, &curtime);
507 
508 #ifdef SILC_WIN32
509   if (strrchr(function, '\\'))
510     fprintf(stderr, "%s:%d: %s\n", strrchr(function, '\\') + 1, line, string);
511   else
512 #endif /* SILC_WIN32 */
513 #ifdef SILC_SYMBIAN
514   silc_symbian_debug(function, line, string);
515 #else
516   fprintf(stderr, "%02d:%02d:%02d %s:%d: %s\n", curtime.hour,
517 	  curtime.minute, curtime.second, function, line,
518 	  string);
519   fflush(stderr);
520 #endif /* SILC_SYMBIAN */
521 
522  end:
523   silc_free(string);
524 }
525 
526 /* Hexdumps a message */
527 
silc_log_output_hexdump(char * file,const char * function,int line,void * data_in,SilcUInt32 len,char * string)528 void silc_log_output_hexdump(char *file, const char *function,
529 			     int line, void *data_in,
530 			     SilcUInt32 len, char *string)
531 {
532   int i, k;
533   int off, pos, count;
534   unsigned char *data = (unsigned char *)data_in;
535 
536 #ifndef SILC_SYMBIAN
537   if (!silclog.debug_hexdump)
538     goto end;
539 
540   if (!silc_string_regex_match(silclog.debug_string, file) &&
541       !silc_string_regex_match(silclog.debug_string, function))
542     goto end;
543 
544   if (silclog.hexdump_cb) {
545     if ((*silclog.hexdump_cb)(file, (char *)function, line,
546 			      data_in, len, string, silclog.hexdump_context))
547       goto end;
548   }
549 #endif /* !SILC_SYMBIAN */
550 
551   fprintf(stderr, "%s:%d: %s\n", function, line, string);
552 
553   k = 0;
554   pos = 0;
555   count = 16;
556   off = len % 16;
557   while (1) {
558     if (off) {
559       if ((len - pos) < 16 && (len - pos <= len - off))
560 	count = off;
561     } else {
562       if (pos == len)
563 	count = 0;
564     }
565     if (off == len)
566       count = len;
567 
568     if (count)
569       fprintf(stderr, "%08X  ", k++ * 16);
570 
571     for (i = 0; i < count; i++) {
572       fprintf(stderr, "%02X ", data[pos + i]);
573 
574       if ((i + 1) % 4 == 0)
575 	fprintf(stderr, " ");
576     }
577 
578     if (count && count < 16) {
579       int j;
580 
581       for (j = 0; j < 16 - count; j++) {
582 	fprintf(stderr, "   ");
583 
584 	if ((j + count + 1) % 4 == 0)
585 	  fprintf(stderr, " ");
586       }
587     }
588 
589     for (i = 0; i < count; i++) {
590       char ch;
591 
592       if (data[pos] < 32 || data[pos] >= 127)
593 	ch = '.';
594       else
595 	ch = data[pos];
596 
597       fprintf(stderr, "%c", ch);
598       pos++;
599     }
600 
601     if (count)
602       fprintf(stderr, "\n");
603 
604     if (count < 16)
605       break;
606   }
607 
608  end:
609   silc_free(string);
610 }
611