misc: Put git-version-gen into the tarball
[osmocom-bb.git] / src / logging.c
1 /* Debugging/Logging support code */
2
3 /* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4  * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
5  * All Rights Reserved
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License along
18  * with this program; if not, write to the Free Software Foundation, Inc.,
19  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  */
22
23 #include "../config.h"
24
25 #include <stdarg.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30
31 #ifdef HAVE_STRINGS_H
32 #include <strings.h>
33 #endif
34 #include <time.h>
35 #include <errno.h>
36
37 #include <osmocom/core/talloc.h>
38 #include <osmocom/core/utils.h>
39 #include <osmocom/core/logging.h>
40
41 #include <osmocom/vty/logging.h>        /* for LOGGING_STR. */
42
43 struct log_info *osmo_log_info;
44
45 static struct log_context log_context;
46 static void *tall_log_ctx = NULL;
47 LLIST_HEAD(osmo_log_target_list);
48
49 #define LOGLEVEL_DEFS   6       /* Number of loglevels.*/
50
51 static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
52         { 0,            "EVERYTHING" },
53         { LOGL_DEBUG,   "DEBUG" },
54         { LOGL_INFO,    "INFO" },
55         { LOGL_NOTICE,  "NOTICE" },
56         { LOGL_ERROR,   "ERROR" },
57         { LOGL_FATAL,   "FATAL" },
58         { 0, NULL },
59 };
60
61 #define INT2IDX(x)      (-1*(x)-1)
62 static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
63         [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
64                 .name = "DLGLOBAL",
65                 .description = "Library-internal global log family",
66                 .loglevel = LOGL_NOTICE,
67                 .enabled = 1,
68         },
69         [INT2IDX(DLLAPDM)] = {  /* -2 becomes 1 */
70                 .name = "DLLAPDM",
71                 .description = "LAPDm in libosmogsm",
72                 .loglevel = LOGL_NOTICE,
73                 .enabled = 1,
74         },
75 };
76
77 /* You have to keep this in sync with the structure loglevel_strs. */
78 const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
79         "Log simply everything",
80         "Log debug messages and higher levels",
81         "Log informational messages and higher levels",
82         "Log noticable messages and higher levels",
83         "Log error messages and higher levels",
84         "Log only fatal messages",
85         NULL,
86 };
87
88 /* special magic for negative (library-internal) log subsystem numbers */
89 static int subsys_lib2index(int subsys)
90 {
91         return (subsys * -1) + (osmo_log_info->num_cat_user-1);
92 }
93
94 int log_parse_level(const char *lvl)
95 {
96         return get_string_value(loglevel_strs, lvl);
97 }
98
99 const char *log_level_str(unsigned int lvl)
100 {
101         return get_value_string(loglevel_strs, lvl);
102 }
103
104 int log_parse_category(const char *category)
105 {
106         int i;
107
108         for (i = 0; i < osmo_log_info->num_cat; ++i) {
109                 if (osmo_log_info->cat[i].name == NULL)
110                         continue;
111                 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
112                         return i;
113         }
114
115         return -EINVAL;
116 }
117
118 /*
119  * Parse the category mask.
120  * The format can be this: category1:category2:category3
121  * or category1,2:category2,3:...
122  */
123 void log_parse_category_mask(struct log_target* target, const char *_mask)
124 {
125         int i = 0;
126         char *mask = strdup(_mask);
127         char *category_token = NULL;
128
129         /* Disable everything to enable it afterwards */
130         for (i = 0; i < osmo_log_info->num_cat; ++i)
131                 target->categories[i].enabled = 0;
132
133         category_token = strtok(mask, ":");
134         do {
135                 for (i = 0; i < osmo_log_info->num_cat; ++i) {
136                         char* colon = strstr(category_token, ",");
137                         int length = strlen(category_token);
138
139                         if (!osmo_log_info->cat[i].name)
140                                 continue;
141
142                         if (colon)
143                             length = colon - category_token;
144
145                         if (strncasecmp(osmo_log_info->cat[i].name,
146                                         category_token, length) == 0) {
147                                 int level = 0;
148
149                                 if (colon)
150                                         level = atoi(colon+1);
151
152                                 target->categories[i].enabled = 1;
153                                 target->categories[i].loglevel = level;
154                         }
155                 }
156         } while ((category_token = strtok(NULL, ":")));
157
158         free(mask);
159 }
160
161 static const char* color(int subsys)
162 {
163         if (subsys < osmo_log_info->num_cat)
164                 return osmo_log_info->cat[subsys].color;
165
166         return NULL;
167 }
168
169 static void _output(struct log_target *target, unsigned int subsys,
170                     unsigned int level, char *file, int line, int cont,
171                     const char *format, va_list ap)
172 {
173         char buf[4096];
174         int ret, len = 0, offset = 0, rem = sizeof(buf);
175
176         /* are we using color */
177         if (target->use_color) {
178                 const char *c = color(subsys);
179                 if (c) {
180                         ret = snprintf(buf + offset, rem, "%s", color(subsys));
181                         if (ret < 0)
182                                 goto err;
183                         OSMO_SNPRINTF_RET(ret, rem, offset, len);
184                 }
185         }
186         if (!cont) {
187                 if (target->print_timestamp) {
188                         char *timestr;
189                         time_t tm;
190                         tm = time(NULL);
191                         timestr = ctime(&tm);
192                         timestr[strlen(timestr)-1] = '\0';
193                         ret = snprintf(buf + offset, rem, "%s ", timestr);
194                         if (ret < 0)
195                                 goto err;
196                         OSMO_SNPRINTF_RET(ret, rem, offset, len);
197                 }
198                 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
199                                 subsys, file, line);
200                 if (ret < 0)
201                         goto err;
202                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
203         }
204         ret = vsnprintf(buf + offset, rem, format, ap);
205         if (ret < 0)
206                 goto err;
207         OSMO_SNPRINTF_RET(ret, rem, offset, len);
208
209         ret = snprintf(buf + offset, rem, "%s",
210                         target->use_color ? "\033[0;m" : "");
211         if (ret < 0)
212                 goto err;
213         OSMO_SNPRINTF_RET(ret, rem, offset, len);
214 err:
215         buf[sizeof(buf)-1] = '\0';
216         target->output(target, level, buf);
217 }
218
219 static void _logp(int subsys, int level, char *file, int line,
220                   int cont, const char *format, va_list ap)
221 {
222         struct log_target *tar;
223
224         if (subsys < 0)
225                 subsys = subsys_lib2index(subsys);
226
227         if (subsys > osmo_log_info->num_cat)
228                 subsys = DLGLOBAL;
229
230         llist_for_each_entry(tar, &osmo_log_target_list, entry) {
231                 struct log_category *category;
232                 int output = 0;
233                 va_list bp;
234
235                 category = &tar->categories[subsys];
236                 /* subsystem is not supposed to be logged */
237                 if (!category->enabled)
238                         continue;
239
240                 /* Check the global log level */
241                 if (tar->loglevel != 0 && level < tar->loglevel)
242                         continue;
243
244                 /* Check the category log level */
245                 if (tar->loglevel == 0 && category->loglevel != 0 &&
246                     level < category->loglevel)
247                         continue;
248
249                 /* Apply filters here... if that becomes messy we will
250                  * need to put filters in a list and each filter will
251                  * say stop, continue, output */
252                 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
253                         output = 1;
254                 else if (osmo_log_info->filter_fn)
255                         output = osmo_log_info->filter_fn(&log_context,
256                                                        tar);
257                 if (!output)
258                         continue;
259
260                 /* According to the manpage, vsnprintf leaves the value of ap
261                  * in undefined state. Since _output uses vsnprintf and it may
262                  * be called several times, we have to pass a copy of ap. */
263                 va_copy(bp, ap);
264                 _output(tar, subsys, level, file, line, cont, format, bp);
265                 va_end(bp);
266         }
267 }
268
269 void logp(int subsys, char *file, int line, int cont,
270           const char *format, ...)
271 {
272         va_list ap;
273
274         va_start(ap, format);
275         _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
276         va_end(ap);
277 }
278
279 void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
280 {
281         va_list ap;
282
283         va_start(ap, format);
284         _logp(subsys, level, file, line, cont, format, ap);
285         va_end(ap);
286 }
287
288 void log_add_target(struct log_target *target)
289 {
290         llist_add_tail(&target->entry, &osmo_log_target_list);
291 }
292
293 void log_del_target(struct log_target *target)
294 {
295         llist_del(&target->entry);
296 }
297
298 void log_reset_context(void)
299 {
300         memset(&log_context, 0, sizeof(log_context));
301 }
302
303 int log_set_context(uint8_t ctx_nr, void *value)
304 {
305         if (ctx_nr > LOG_MAX_CTX)
306                 return -EINVAL;
307
308         log_context.ctx[ctx_nr] = value;
309
310         return 0;
311 }
312
313 void log_set_all_filter(struct log_target *target, int all)
314 {
315         if (all)
316                 target->filter_map |= LOG_FILTER_ALL;
317         else
318                 target->filter_map &= ~LOG_FILTER_ALL;
319 }
320
321 void log_set_use_color(struct log_target *target, int use_color)
322 {
323         target->use_color = use_color;
324 }
325
326 void log_set_print_timestamp(struct log_target *target, int print_timestamp)
327 {
328         target->print_timestamp = print_timestamp;
329 }
330
331 void log_set_log_level(struct log_target *target, int log_level)
332 {
333         target->loglevel = log_level;
334 }
335
336 void log_set_category_filter(struct log_target *target, int category,
337                                int enable, int level)
338 {
339         if (category >= osmo_log_info->num_cat)
340                 return;
341         target->categories[category].enabled = !!enable;
342         target->categories[category].loglevel = level;
343 }
344
345 static void _file_output(struct log_target *target, unsigned int level,
346                          const char *log)
347 {
348         fprintf(target->tgt_file.out, "%s", log);
349         fflush(target->tgt_file.out);
350 }
351
352 struct log_target *log_target_create(void)
353 {
354         struct log_target *target;
355         unsigned int i;
356
357         target = talloc_zero(tall_log_ctx, struct log_target);
358         if (!target)
359                 return NULL;
360
361         target->categories = talloc_zero_array(target, 
362                                                 struct log_category,
363                                                 osmo_log_info->num_cat);
364         if (!target->categories) {
365                 talloc_free(target);
366                 return NULL;
367         }
368
369         INIT_LLIST_HEAD(&target->entry);
370
371         /* initialize the per-category enabled/loglevel from defaults */
372         for (i = 0; i < osmo_log_info->num_cat; i++) {
373                 struct log_category *cat = &target->categories[i];
374                 cat->enabled = osmo_log_info->cat[i].enabled;
375                 cat->loglevel = osmo_log_info->cat[i].loglevel;
376         }
377
378         /* global settings */
379         target->use_color = 1;
380         target->print_timestamp = 0;
381
382         /* global log level */
383         target->loglevel = 0;
384         return target;
385 }
386
387 struct log_target *log_target_create_stderr(void)
388 {
389 /* since C89/C99 says stderr is a macro, we can safely do this! */
390 #ifdef stderr
391         struct log_target *target;
392
393         target = log_target_create();
394         if (!target)
395                 return NULL;
396
397         target->type = LOG_TGT_TYPE_STDERR;
398         target->tgt_file.out = stderr;
399         target->output = _file_output;
400         return target;
401 #else
402         return NULL;
403 #endif /* stderr */
404 }
405
406 struct log_target *log_target_create_file(const char *fname)
407 {
408         struct log_target *target;
409
410         target = log_target_create();
411         if (!target)
412                 return NULL;
413
414         target->type = LOG_TGT_TYPE_FILE;
415         target->tgt_file.out = fopen(fname, "a");
416         if (!target->tgt_file.out)
417                 return NULL;
418
419         target->output = _file_output;
420
421         target->tgt_file.fname = talloc_strdup(target, fname);
422
423         return target;
424 }
425
426 struct log_target *log_target_find(int type, const char *fname)
427 {
428         struct log_target *tgt;
429
430         llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
431                 if (tgt->type != type)
432                         continue;
433                 if (tgt->type == LOG_TGT_TYPE_FILE) {
434                         if (!strcmp(fname, tgt->tgt_file.fname))
435                                 return tgt;
436                 } else
437                         return tgt;
438         }
439         return NULL;
440 }
441
442 void log_target_destroy(struct log_target *target)
443 {
444
445         /* just in case, to make sure we don't have any references */
446         log_del_target(target);
447
448         if (target->output == &_file_output) {
449 /* since C89/C99 says stderr is a macro, we can safely do this! */
450 #ifdef stderr
451                 /* don't close stderr */
452                 if (target->tgt_file.out != stderr)
453 #endif
454                 {
455                         fclose(target->tgt_file.out);
456                         target->tgt_file.out = NULL;
457                 }
458         }
459
460         talloc_free(target);
461 }
462
463 /* close and re-open a log file (for log file rotation) */
464 int log_target_file_reopen(struct log_target *target)
465 {
466         fclose(target->tgt_file.out);
467
468         target->tgt_file.out = fopen(target->tgt_file.fname, "a");
469         if (!target->tgt_file.out)
470                 return -errno;
471
472         /* we assume target->output already to be set */
473
474         return 0;
475 }
476
477 /* This generates the logging command string for VTY. */
478 const char *log_vty_command_string(const struct log_info *unused_info)
479 {
480         struct log_info *info = osmo_log_info;
481         int len = 0, offset = 0, ret, i, rem;
482         int size = strlen("logging level () ()") + 1;
483         char *str;
484
485         for (i = 0; i < info->num_cat; i++) {
486                 if (info->cat[i].name == NULL)
487                         continue;
488                 size += strlen(info->cat[i].name) + 1;
489         }
490
491         for (i = 0; i < LOGLEVEL_DEFS; i++)
492                 size += strlen(loglevel_strs[i].str) + 1;
493
494         rem = size;
495         str = talloc_zero_size(tall_log_ctx, size);
496         if (!str)
497                 return NULL;
498
499         ret = snprintf(str + offset, rem, "logging level (all|");
500         if (ret < 0)
501                 goto err;
502         OSMO_SNPRINTF_RET(ret, rem, offset, len);
503
504         for (i = 0; i < info->num_cat; i++) {
505                 if (info->cat[i].name) {
506                         int j, name_len = strlen(info->cat[i].name)+1;
507                         char name[name_len];
508
509                         for (j = 0; j < name_len; j++)
510                                 name[j] = tolower(info->cat[i].name[j]);
511
512                         name[name_len-1] = '\0';
513                         ret = snprintf(str + offset, rem, "%s|", name+1);
514                         if (ret < 0)
515                                 goto err;
516                         OSMO_SNPRINTF_RET(ret, rem, offset, len);
517                 }
518         }
519         offset--;       /* to remove the trailing | */
520         rem++;
521
522         ret = snprintf(str + offset, rem, ") (");
523         if (ret < 0)
524                 goto err;
525         OSMO_SNPRINTF_RET(ret, rem, offset, len);
526
527         for (i = 0; i < LOGLEVEL_DEFS; i++) {
528                 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
529                 char loglevel_str[loglevel_str_len];
530
531                 for (j = 0; j < loglevel_str_len; j++)
532                         loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
533
534                 loglevel_str[loglevel_str_len-1] = '\0';
535                 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
536                 if (ret < 0)
537                         goto err;
538                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
539         }
540         offset--;       /* to remove the trailing | */
541         rem++;
542
543         ret = snprintf(str + offset, rem, ")");
544         if (ret < 0)
545                 goto err;
546         OSMO_SNPRINTF_RET(ret, rem, offset, len);
547 err:
548         str[size-1] = '\0';
549         return str;
550 }
551
552 /* This generates the logging command description for VTY. */
553 const char *log_vty_command_description(const struct log_info *unused_info)
554 {
555         struct log_info *info = osmo_log_info;
556         char *str;
557         int i, ret, len = 0, offset = 0, rem;
558         unsigned int size =
559                 strlen(LOGGING_STR
560                        "Set the log level for a specified category\n") + 1;
561
562         for (i = 0; i < info->num_cat; i++) {
563                 if (info->cat[i].name == NULL)
564                         continue;
565                 size += strlen(info->cat[i].description) + 1;
566         }
567
568         for (i = 0; i < LOGLEVEL_DEFS; i++)
569                 size += strlen(loglevel_descriptions[i]) + 1;
570
571         size += strlen("Global setting for all subsystems") + 1;
572         rem = size;
573         str = talloc_zero_size(tall_log_ctx, size);
574         if (!str)
575                 return NULL;
576
577         ret = snprintf(str + offset, rem, LOGGING_STR
578                         "Set the log level for a specified category\n");
579         if (ret < 0)
580                 goto err;
581         OSMO_SNPRINTF_RET(ret, rem, offset, len);
582
583         ret = snprintf(str + offset, rem,
584                         "Global setting for all subsystems\n");
585         if (ret < 0)
586                 goto err;
587         OSMO_SNPRINTF_RET(ret, rem, offset, len);
588
589         for (i = 0; i < info->num_cat; i++) {
590                 if (info->cat[i].name == NULL)
591                         continue;
592                 ret = snprintf(str + offset, rem, "%s\n",
593                                 info->cat[i].description);
594                 if (ret < 0)
595                         goto err;
596                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
597         }
598         for (i = 0; i < LOGLEVEL_DEFS; i++) {
599                 ret = snprintf(str + offset, rem, "%s\n",
600                                 loglevel_descriptions[i]);
601                 if (ret < 0)
602                         goto err;
603                 OSMO_SNPRINTF_RET(ret, rem, offset, len);
604         }
605 err:
606         str[size-1] = '\0';
607         return str;
608 }
609
610 int log_init(const struct log_info *inf, void *ctx)
611 {
612         int i;
613
614         tall_log_ctx = talloc_named_const(ctx, 1, "logging");
615         if (!tall_log_ctx)
616                 return -ENOMEM;
617
618         osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
619         if (!osmo_log_info)
620                 return -ENOMEM;
621
622         osmo_log_info->num_cat_user = inf->num_cat;
623         /* total number = number of user cat + library cat */
624         osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
625
626         osmo_log_info->cat = talloc_zero_array(osmo_log_info,
627                                         struct log_info_cat,
628                                         osmo_log_info->num_cat);
629         if (!osmo_log_info->cat) {
630                 talloc_free(osmo_log_info);
631                 osmo_log_info = NULL;
632                 return -ENOMEM;
633         }
634
635         /* copy over the user part */
636         for (i = 0; i < inf->num_cat; i++) {
637                 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
638                         sizeof(struct log_info_cat));
639         }
640
641         /* copy over the library part */
642         for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
643                 unsigned int cn = osmo_log_info->num_cat_user + i;
644                 memcpy(&osmo_log_info->cat[cn],
645                         &internal_cat[i], sizeof(struct log_info_cat));
646         }
647
648         return 0;
649 }