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