blob: 89ca6ced032880dcd7e174dcee7723cb50341eaf [file] [log] [blame]
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001/* 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
Harald Welte01fd5cb2010-03-26 23:51:31 +080023#include "../config.h"
24
Harald Welte4a2bb9e2010-03-26 09:33:40 +080025#include <stdarg.h>
26#include <stdlib.h>
27#include <stdio.h>
28#include <string.h>
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010029#include <ctype.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080030
31#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080032#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080033#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +080034#include <time.h>
35#include <errno.h>
36
37#include <osmocore/talloc.h>
38#include <osmocore/utils.h>
Harald Welte3ae27582010-03-26 21:24:24 +080039#include <osmocore/logging.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080040
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010041#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
42
Harald Welte4ebdf742010-05-19 19:54:00 +020043const struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080044
Harald Welte3ae27582010-03-26 21:24:24 +080045static struct log_context log_context;
46static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010047LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080048
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010049#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
50
51static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte4a2bb9e2010-03-26 09:33:40 +080052 { 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
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010061/* You have to keep this in sync with the structure loglevel_strs. */
62const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
63 "Log simply everything",
64 "Log debug messages and higher levels",
65 "Log informational messages and higher levels",
66 "Log noticable messages and higher levels",
67 "Log error messages and higher levels",
68 "Log only fatal messages",
69 NULL,
70};
71
Harald Welte3ae27582010-03-26 21:24:24 +080072int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +080073{
74 return get_string_value(loglevel_strs, lvl);
75}
76
Harald Welte9ac22252010-05-11 11:19:40 +020077const char *log_level_str(unsigned int lvl)
78{
79 return get_value_string(loglevel_strs, lvl);
80}
81
Harald Welte3ae27582010-03-26 21:24:24 +080082int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +080083{
84 int i;
85
Harald Welte4ebdf742010-05-19 19:54:00 +020086 for (i = 0; i < osmo_log_info->num_cat; ++i) {
87 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +080088 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080089 }
90
91 return -EINVAL;
92}
93
94/*
95 * Parse the category mask.
96 * The format can be this: category1:category2:category3
97 * or category1,2:category2,3:...
98 */
Harald Welte3ae27582010-03-26 21:24:24 +080099void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800100{
101 int i = 0;
102 char *mask = strdup(_mask);
103 char *category_token = NULL;
104
105 /* Disable everything to enable it afterwards */
106 for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
107 target->categories[i].enabled = 0;
108
109 category_token = strtok(mask, ":");
110 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200111 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800112 char* colon = strstr(category_token, ",");
113 int length = strlen(category_token);
114
115 if (colon)
116 length = colon - category_token;
117
Harald Welte4ebdf742010-05-19 19:54:00 +0200118 if (strncasecmp(osmo_log_info->cat[i].name,
119 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800120 int level = 0;
121
122 if (colon)
123 level = atoi(colon+1);
124
Harald Weltefaadfe22010-03-26 21:05:43 +0800125 target->categories[i].enabled = 1;
126 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800127 }
128 }
129 } while ((category_token = strtok(NULL, ":")));
130
131 free(mask);
132}
133
134static const char* color(int subsys)
135{
Harald Welte4ebdf742010-05-19 19:54:00 +0200136 if (subsys < osmo_log_info->num_cat)
137 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800138
Harald Welted788f662010-03-26 09:45:03 +0800139 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800140}
141
Harald Welte3ae27582010-03-26 21:24:24 +0800142static void _output(struct log_target *target, unsigned int subsys,
Harald Welte76e72ab2011-02-17 15:52:39 +0100143 unsigned int level, char *file, int line, int cont,
144 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800145{
146 char col[30];
147 char sub[30];
148 char tim[30];
149 char buf[4096];
150 char final[4096];
151
152 /* prepare the data */
153 col[0] = '\0';
154 sub[0] = '\0';
155 tim[0] = '\0';
156 buf[0] = '\0';
157
158 /* are we using color */
159 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800160 const char *c = color(subsys);
161 if (c) {
162 snprintf(col, sizeof(col), "%s", color(subsys));
163 col[sizeof(col)-1] = '\0';
164 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800165 }
166 vsnprintf(buf, sizeof(buf), format, ap);
167 buf[sizeof(buf)-1] = '\0';
168
169 if (!cont) {
170 if (target->print_timestamp) {
171 char *timestr;
172 time_t tm;
173 tm = time(NULL);
174 timestr = ctime(&tm);
175 timestr[strlen(timestr)-1] = '\0';
176 snprintf(tim, sizeof(tim), "%s ", timestr);
177 tim[sizeof(tim)-1] = '\0';
178 }
179 snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
180 sub[sizeof(sub)-1] = '\0';
181 }
182
Harald Weltea439a3a2010-07-30 21:01:54 +0200183 snprintf(final, sizeof(final), "%s%s%s%s%s", col, tim, sub, buf,
184 target->use_color ? "\033[0;m" : "");
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185 final[sizeof(final)-1] = '\0';
Harald Welte76e72ab2011-02-17 15:52:39 +0100186 target->output(target, level, final);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800187}
188
189
Harald Welte3ae27582010-03-26 21:24:24 +0800190static void _logp(unsigned int subsys, int level, char *file, int line,
191 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800192{
Harald Welte3ae27582010-03-26 21:24:24 +0800193 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800194
Harald Welte28222962011-02-18 20:37:04 +0100195 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800196 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800197 int output = 0;
198
199 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800200 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800201 if (!category->enabled)
202 continue;
203
204 /* Check the global log level */
205 if (tar->loglevel != 0 && level < tar->loglevel)
206 continue;
207
208 /* Check the category log level */
209 if (tar->loglevel == 0 && category->loglevel != 0 &&
210 level < category->loglevel)
211 continue;
212
213 /* Apply filters here... if that becomes messy we will
214 * need to put filters in a list and each filter will
215 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800216 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800217 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200218 else if (osmo_log_info->filter_fn)
219 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800220 tar);
221
222 if (output) {
223 /* FIXME: copying the va_list is an ugly
224 * workaround against a bug hidden somewhere in
225 * _output. If we do not copy here, the first
226 * call to _output() will corrupt the va_list
227 * contents, and any further _output() calls
228 * with the same va_list will segfault */
229 va_list bp;
230 va_copy(bp, ap);
Harald Welte76e72ab2011-02-17 15:52:39 +0100231 _output(tar, subsys, level, file, line, cont, format, bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800232 va_end(bp);
233 }
234 }
235}
236
Harald Welte3ae27582010-03-26 21:24:24 +0800237void logp(unsigned int subsys, char *file, int line, int cont,
238 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800239{
240 va_list ap;
241
242 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800243 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244 va_end(ap);
245}
246
Harald Welte3ae27582010-03-26 21:24:24 +0800247void logp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800248{
249 va_list ap;
250
251 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800252 _logp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800253 va_end(ap);
254}
255
Harald Welte3ae27582010-03-26 21:24:24 +0800256void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800257{
Harald Welte28222962011-02-18 20:37:04 +0100258 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800259}
260
Harald Welte3ae27582010-03-26 21:24:24 +0800261void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800262{
263 llist_del(&target->entry);
264}
265
Harald Welte3ae27582010-03-26 21:24:24 +0800266void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800267{
Harald Welte3ae27582010-03-26 21:24:24 +0800268 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800269}
270
Harald Welte3ae27582010-03-26 21:24:24 +0800271int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800272{
Harald Welte3ae27582010-03-26 21:24:24 +0800273 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800274 return -EINVAL;
275
Harald Welte3ae27582010-03-26 21:24:24 +0800276 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800277
278 return 0;
279}
280
Harald Welte3ae27582010-03-26 21:24:24 +0800281void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800282{
283 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800284 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800285 else
Harald Welte3ae27582010-03-26 21:24:24 +0800286 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800287}
288
Harald Welte3ae27582010-03-26 21:24:24 +0800289void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800290{
291 target->use_color = use_color;
292}
293
Harald Welte3ae27582010-03-26 21:24:24 +0800294void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800295{
296 target->print_timestamp = print_timestamp;
297}
298
Harald Welte3ae27582010-03-26 21:24:24 +0800299void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800300{
301 target->loglevel = log_level;
302}
303
Harald Welte3ae27582010-03-26 21:24:24 +0800304void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800305 int enable, int level)
306{
Harald Welte4ebdf742010-05-19 19:54:00 +0200307 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800308 return;
309 target->categories[category].enabled = !!enable;
310 target->categories[category].loglevel = level;
311}
312
Harald Welte76e72ab2011-02-17 15:52:39 +0100313static void _file_output(struct log_target *target, unsigned int level,
314 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800315{
Harald Welte0083cd32010-08-25 14:55:44 +0200316 fprintf(target->tgt_file.out, "%s", log);
317 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800318}
319
Harald Welte3ae27582010-03-26 21:24:24 +0800320struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800321{
Harald Welte3ae27582010-03-26 21:24:24 +0800322 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800323 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324
Harald Welte3ae27582010-03-26 21:24:24 +0800325 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800326 if (!target)
327 return NULL;
328
329 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800330
331 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200332 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800333 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200334 cat->enabled = osmo_log_info->cat[i].enabled;
335 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800336 }
337
338 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800339 target->use_color = 1;
340 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800341
342 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800343 target->loglevel = 0;
344 return target;
345}
346
Harald Welte3ae27582010-03-26 21:24:24 +0800347struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800348{
Harald Weltea3b844c2010-03-27 00:04:40 +0800349/* since C89/C99 says stderr is a macro, we can safely do this! */
350#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800351 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800352
Harald Welte3ae27582010-03-26 21:24:24 +0800353 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800354 if (!target)
355 return NULL;
356
Harald Welte28222962011-02-18 20:37:04 +0100357 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200358 target->tgt_file.out = stderr;
359 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800360 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800361#else
362 return NULL;
363#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800364}
365
Harald Welte3086c392010-08-25 19:10:50 +0200366struct log_target *log_target_create_file(const char *fname)
367{
368 struct log_target *target;
369
370 target = log_target_create();
371 if (!target)
372 return NULL;
373
Harald Welte28222962011-02-18 20:37:04 +0100374 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200375 target->tgt_file.out = fopen(fname, "a");
376 if (!target->tgt_file.out)
377 return NULL;
378
379 target->output = _file_output;
380
381 target->tgt_file.fname = talloc_strdup(target, fname);
382
383 return target;
384}
385
Harald Welte28222962011-02-18 20:37:04 +0100386struct log_target *log_target_find(int type, const char *fname)
387{
388 struct log_target *tgt;
389
390 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
391 if (tgt->type != type)
392 continue;
393 if (tgt->type == LOG_TGT_TYPE_FILE) {
394 if (!strcmp(fname, tgt->tgt_file.fname))
395 return tgt;
396 } else
397 return tgt;
398 }
399 return NULL;
400}
401
Harald Welte3086c392010-08-25 19:10:50 +0200402void log_target_destroy(struct log_target *target)
403{
404
405 /* just in case, to make sure we don't have any references */
406 log_del_target(target);
407
408 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200409/* since C89/C99 says stderr is a macro, we can safely do this! */
410#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200411 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200412 if (target->tgt_file.out != stderr)
413#endif
414 {
Harald Welte3086c392010-08-25 19:10:50 +0200415 fclose(target->tgt_file.out);
416 target->tgt_file.out = NULL;
417 }
418 }
419
420 talloc_free(target);
421}
422
423/* close and re-open a log file (for log file rotation) */
424int log_target_file_reopen(struct log_target *target)
425{
426 fclose(target->tgt_file.out);
427
428 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
429 if (!target->tgt_file.out)
430 return -errno;
431
432 /* we assume target->output already to be set */
433
434 return 0;
435}
436
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100437/* This can go into some header file so others can benefit from it. */
438#define SNPRINTF_FAILURE(ret, rem, offset, len) \
439do { \
440 len += ret; \
441 if (ret > rem) \
442 ret = rem; \
443 offset += ret; \
444 rem -= ret; \
445} while (0)
446
447/* This generates the logging command string for VTY. */
448const char *log_vty_command_string(const struct log_info *info)
Harald Welte7638af92010-05-11 16:39:22 +0200449{
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100450 int len = 0, offset = 0, ret, i, rem;
451 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200452 char *str;
453
454 for (i = 0; i < info->num_cat; i++)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100455 size += strlen(info->cat[i].name) + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200456
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100457 for (i = 0; i < LOGLEVEL_DEFS; i++)
458 size += strlen(loglevel_strs[i].str) + 1;
459
460 rem = size;
461 str = talloc_zero_size(NULL, size);
Harald Welte7638af92010-05-11 16:39:22 +0200462 if (!str)
463 return NULL;
464
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100465 ret = snprintf(str + offset, rem, "logging level (");
466 if (ret < 0)
467 goto err;
468 SNPRINTF_FAILURE(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200469
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100470 for (i = 0; i < info->num_cat; i++) {
471 int j, name_len = strlen(info->cat[i].name)+1;
472 char name[name_len];
473
474 for (j = 0; j < name_len; j++)
475 name[j] = tolower(info->cat[i].name[j]);
476
477 name[name_len-1] = '\0';
478 ret = snprintf(str + offset, rem, "%s|", name+1);
479 if (ret < 0)
480 goto err;
481 SNPRINTF_FAILURE(ret, rem, offset, len);
482 }
483 offset--; /* to remove the trailing | */
484 rem++;
485
486 ret = snprintf(str + offset, rem, ") (");
487 if (ret < 0)
488 goto err;
489 SNPRINTF_FAILURE(ret, rem, offset, len);
490
491 for (i = 0; i < LOGLEVEL_DEFS; i++) {
492 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
493 char loglevel_str[loglevel_str_len];
494
495 for (j = 0; j < loglevel_str_len; j++)
496 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
497
498 loglevel_str[loglevel_str_len-1] = '\0';
499 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
500 if (ret < 0)
501 goto err;
502 SNPRINTF_FAILURE(ret, rem, offset, len);
503 }
504 offset--; /* to remove the trailing | */
505 rem++;
506
507 ret = snprintf(str + offset, rem, ")");
508 if (ret < 0)
509 goto err;
510 SNPRINTF_FAILURE(ret, rem, offset, len);
511err:
512 return str;
513}
514
515/* This generates the logging command description for VTY. */
516const char *log_vty_command_description(const struct log_info *info)
517{
518 char *str;
519 int i, ret, len = 0, offset = 0, rem;
520 unsigned int size =
521 strlen(LOGGING_STR
522 "Set the log level for a specified category\n") + 1;
523
524 for (i = 0; i < info->num_cat; i++)
525 size += strlen(info->cat[i].description) + 1;
526
527 for (i = 0; i < LOGLEVEL_DEFS; i++)
528 size += strlen(loglevel_descriptions[i]) + 1;
529
530 rem = size;
531 str = talloc_zero_size(NULL, size);
532 if (!str)
533 return NULL;
534
535 ret = snprintf(str + offset, rem, LOGGING_STR
536 "Set the log level for a specified category\n");
537 if (ret < 0)
538 goto err;
539 SNPRINTF_FAILURE(ret, rem, offset, len);
540
541 for (i = 0; i < info->num_cat; i++) {
542 ret = snprintf(str + offset, rem, "%s\n",
543 info->cat[i].description);
544 if (ret < 0)
545 goto err;
546 SNPRINTF_FAILURE(ret, rem, offset, len);
547 }
548 for (i = 0; i < LOGLEVEL_DEFS; i++) {
549 ret = snprintf(str + offset, rem, "%s\n",
550 loglevel_descriptions[i]);
551 if (ret < 0)
552 goto err;
553 SNPRINTF_FAILURE(ret, rem, offset, len);
554 }
555err:
Harald Welte7638af92010-05-11 16:39:22 +0200556 return str;
557}
558
Harald Welte3ae27582010-03-26 21:24:24 +0800559void log_init(const struct log_info *cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800560{
Harald Welte3ae27582010-03-26 21:24:24 +0800561 tall_log_ctx = talloc_named_const(NULL, 1, "logging");
Harald Welte4ebdf742010-05-19 19:54:00 +0200562 osmo_log_info = cat;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800563}