blob: 730fc37c6f356e0c6842500fd386c241097e275a [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
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010037#include <osmocom/core/talloc.h>
38#include <osmocom/core/utils.h>
39#include <osmocom/core/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 Welteb43bc042011-06-27 10:29:17 +020043struct 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
Harald Welteb43bc042011-06-27 10:29:17 +020061#define INT2IDX(x) (-1*(x)-1)
62static 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 },
Harald Welte1f0b8c22011-06-27 10:51:37 +020069 [INT2IDX(DLLAPDM)] = { /* -2 becomes 1 */
70 .name = "DLLAPDM",
71 .description = "LAPDm in libosmogsm",
72 .loglevel = LOGL_NOTICE,
73 .enabled = 1,
74 },
Harald Welteb43bc042011-06-27 10:29:17 +020075};
76
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010077/* You have to keep this in sync with the structure loglevel_strs. */
78const 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
Harald Welteb43bc042011-06-27 10:29:17 +020088/* special magic for negative (library-internal) log subsystem numbers */
89static int subsys_lib2index(int subsys)
90{
91 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
92}
93
Harald Welte3ae27582010-03-26 21:24:24 +080094int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +080095{
96 return get_string_value(loglevel_strs, lvl);
97}
98
Harald Welte9ac22252010-05-11 11:19:40 +020099const char *log_level_str(unsigned int lvl)
100{
101 return get_value_string(loglevel_strs, lvl);
102}
103
Harald Welte3ae27582010-03-26 21:24:24 +0800104int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800105{
106 int i;
107
Harald Welte4ebdf742010-05-19 19:54:00 +0200108 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200109 if (osmo_log_info->cat[i].name == NULL)
110 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200111 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800112 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800113 }
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 */
Harald Welte3ae27582010-03-26 21:24:24 +0800123void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800124{
125 int i = 0;
126 char *mask = strdup(_mask);
127 char *category_token = NULL;
128
129 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200130 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800131 target->categories[i].enabled = 0;
132
133 category_token = strtok(mask, ":");
134 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200135 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800136 char* colon = strstr(category_token, ",");
137 int length = strlen(category_token);
138
Harald Welteb43bc042011-06-27 10:29:17 +0200139 if (!osmo_log_info->cat[i].name)
140 continue;
141
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800142 if (colon)
143 length = colon - category_token;
144
Harald Welte4ebdf742010-05-19 19:54:00 +0200145 if (strncasecmp(osmo_log_info->cat[i].name,
146 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800147 int level = 0;
148
149 if (colon)
150 level = atoi(colon+1);
151
Harald Weltefaadfe22010-03-26 21:05:43 +0800152 target->categories[i].enabled = 1;
153 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800154 }
155 }
156 } while ((category_token = strtok(NULL, ":")));
157
158 free(mask);
159}
160
161static const char* color(int subsys)
162{
Harald Welte4ebdf742010-05-19 19:54:00 +0200163 if (subsys < osmo_log_info->num_cat)
164 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800165
Harald Welted788f662010-03-26 09:45:03 +0800166 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800167}
168
Harald Welte3ae27582010-03-26 21:24:24 +0800169static void _output(struct log_target *target, unsigned int subsys,
Harald Welte76e72ab2011-02-17 15:52:39 +0100170 unsigned int level, char *file, int line, int cont,
171 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800172{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800173 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200174 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800175
176 /* are we using color */
177 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800178 const char *c = color(subsys);
179 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200180 ret = snprintf(buf + offset, rem, "%s", color(subsys));
181 if (ret < 0)
182 goto err;
183 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800184 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800186 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';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200193 ret = snprintf(buf + offset, rem, "%s ", timestr);
194 if (ret < 0)
195 goto err;
196 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800197 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200198 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);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800203 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200204 ret = vsnprintf(buf + offset, rem, format, ap);
205 if (ret < 0)
206 goto err;
207 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800208
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200209 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);
214err:
215 buf[sizeof(buf)-1] = '\0';
216 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800217}
218
Harald Welteb43bc042011-06-27 10:29:17 +0200219static void _logp(int subsys, int level, char *file, int line,
Harald Welte3ae27582010-03-26 21:24:24 +0800220 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800221{
Harald Welte3ae27582010-03-26 21:24:24 +0800222 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800223
Harald Welteb43bc042011-06-27 10:29:17 +0200224 if (subsys < 0)
225 subsys = subsys_lib2index(subsys);
226
227 if (subsys > osmo_log_info->num_cat)
228 subsys = DLGLOBAL;
229
Harald Welte28222962011-02-18 20:37:04 +0100230 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800231 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800232 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200233 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800234
235 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800236 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800237 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 */
Harald Welte3ae27582010-03-26 21:24:24 +0800252 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800253 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200254 else if (osmo_log_info->filter_fn)
255 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800256 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200257 if (!output)
258 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800259
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200260 /* 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);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200264 _output(tar, subsys, level, file, line, cont, format, ap);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200265 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800266 }
267}
268
Harald Welteb43bc042011-06-27 10:29:17 +0200269void logp(int subsys, char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800270 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800271{
272 va_list ap;
273
274 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800275 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800276 va_end(ap);
277}
278
Harald Welteb43bc042011-06-27 10:29:17 +0200279void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800280{
281 va_list ap;
282
283 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800284 _logp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800285 va_end(ap);
286}
287
Harald Welte3ae27582010-03-26 21:24:24 +0800288void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800289{
Harald Welte28222962011-02-18 20:37:04 +0100290 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800291}
292
Harald Welte3ae27582010-03-26 21:24:24 +0800293void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800294{
295 llist_del(&target->entry);
296}
297
Harald Welte3ae27582010-03-26 21:24:24 +0800298void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800299{
Harald Welte3ae27582010-03-26 21:24:24 +0800300 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800301}
302
Harald Welte3ae27582010-03-26 21:24:24 +0800303int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800304{
Harald Welte3ae27582010-03-26 21:24:24 +0800305 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800306 return -EINVAL;
307
Harald Welte3ae27582010-03-26 21:24:24 +0800308 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800309
310 return 0;
311}
312
Harald Welte3ae27582010-03-26 21:24:24 +0800313void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800314{
315 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800316 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800317 else
Harald Welte3ae27582010-03-26 21:24:24 +0800318 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800319}
320
Harald Welte3ae27582010-03-26 21:24:24 +0800321void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800322{
323 target->use_color = use_color;
324}
325
Harald Welte3ae27582010-03-26 21:24:24 +0800326void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800327{
328 target->print_timestamp = print_timestamp;
329}
330
Harald Welte3ae27582010-03-26 21:24:24 +0800331void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800332{
333 target->loglevel = log_level;
334}
335
Harald Welte3ae27582010-03-26 21:24:24 +0800336void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800337 int enable, int level)
338{
Harald Welte4ebdf742010-05-19 19:54:00 +0200339 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800340 return;
341 target->categories[category].enabled = !!enable;
342 target->categories[category].loglevel = level;
343}
344
Harald Welte76e72ab2011-02-17 15:52:39 +0100345static void _file_output(struct log_target *target, unsigned int level,
346 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800347{
Harald Welte0083cd32010-08-25 14:55:44 +0200348 fprintf(target->tgt_file.out, "%s", log);
349 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800350}
351
Harald Welte3ae27582010-03-26 21:24:24 +0800352struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800353{
Harald Welte3ae27582010-03-26 21:24:24 +0800354 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800355 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800356
Harald Welte3ae27582010-03-26 21:24:24 +0800357 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800358 if (!target)
359 return NULL;
360
Harald Welteb43bc042011-06-27 10:29:17 +0200361 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
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800369 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800370
371 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200372 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800373 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200374 cat->enabled = osmo_log_info->cat[i].enabled;
375 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800376 }
377
378 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800379 target->use_color = 1;
380 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800381
382 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800383 target->loglevel = 0;
384 return target;
385}
386
Harald Welte3ae27582010-03-26 21:24:24 +0800387struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800388{
Harald Weltea3b844c2010-03-27 00:04:40 +0800389/* since C89/C99 says stderr is a macro, we can safely do this! */
390#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800391 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800392
Harald Welte3ae27582010-03-26 21:24:24 +0800393 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800394 if (!target)
395 return NULL;
396
Harald Welte28222962011-02-18 20:37:04 +0100397 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200398 target->tgt_file.out = stderr;
399 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800400 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800401#else
402 return NULL;
403#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800404}
405
Harald Welte3086c392010-08-25 19:10:50 +0200406struct 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
Harald Welte28222962011-02-18 20:37:04 +0100414 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200415 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
Harald Welte28222962011-02-18 20:37:04 +0100426struct 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
Harald Welte3086c392010-08-25 19:10:50 +0200442void 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) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200449/* since C89/C99 says stderr is a macro, we can safely do this! */
450#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200451 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200452 if (target->tgt_file.out != stderr)
453#endif
454 {
Harald Welte3086c392010-08-25 19:10:50 +0200455 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) */
464int 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
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100477/* This generates the logging command string for VTY. */
Harald Weltece9fec32011-06-27 14:19:16 +0200478const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200479{
Harald Weltece9fec32011-06-27 14:19:16 +0200480 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100481 int len = 0, offset = 0, ret, i, rem;
482 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200483 char *str;
484
Harald Welteb43bc042011-06-27 10:29:17 +0200485 for (i = 0; i < info->num_cat; i++) {
486 if (info->cat[i].name == NULL)
487 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100488 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200489 }
Harald Welte7638af92010-05-11 16:39:22 +0200490
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100491 for (i = 0; i < LOGLEVEL_DEFS; i++)
492 size += strlen(loglevel_strs[i].str) + 1;
493
494 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200495 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200496 if (!str)
497 return NULL;
498
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200499 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100500 if (ret < 0)
501 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200502 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200503
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100504 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200505 if (info->cat[i].name) {
506 int j, name_len = strlen(info->cat[i].name)+1;
507 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100508
Harald Welteb43bc042011-06-27 10:29:17 +0200509 for (j = 0; j < name_len; j++)
510 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100511
Harald Welteb43bc042011-06-27 10:29:17 +0200512 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 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100518 }
519 offset--; /* to remove the trailing | */
520 rem++;
521
522 ret = snprintf(str + offset, rem, ") (");
523 if (ret < 0)
524 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200525 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100526
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;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200538 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100539 }
540 offset--; /* to remove the trailing | */
541 rem++;
542
543 ret = snprintf(str + offset, rem, ")");
544 if (ret < 0)
545 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200546 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100547err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200548 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100549 return str;
550}
551
552/* This generates the logging command description for VTY. */
Harald Weltece9fec32011-06-27 14:19:16 +0200553const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100554{
Harald Weltece9fec32011-06-27 14:19:16 +0200555 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100556 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
Harald Welteb43bc042011-06-27 10:29:17 +0200562 for (i = 0; i < info->num_cat; i++) {
563 if (info->cat[i].name == NULL)
564 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100565 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200566 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100567
568 for (i = 0; i < LOGLEVEL_DEFS; i++)
569 size += strlen(loglevel_descriptions[i]) + 1;
570
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200571 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100572 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200573 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100574 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;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200581 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100582
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200583 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
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100589 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200590 if (info->cat[i].name == NULL)
591 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100592 ret = snprintf(str + offset, rem, "%s\n",
593 info->cat[i].description);
594 if (ret < 0)
595 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200596 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100597 }
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;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200603 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100604 }
605err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200606 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200607 return str;
608}
609
Harald Welteb43bc042011-06-27 10:29:17 +0200610int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800611{
Harald Welteb43bc042011-06-27 10:29:17 +0200612 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 */
Harald Weltece9fec32011-06-27 14:19:16 +0200624 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200625
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 */
Harald Welte9fe16522011-06-27 14:00:03 +0200642 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200643 unsigned int cn = osmo_log_info->num_cat_user + i;
644 memcpy(&osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200645 &internal_cat[i], sizeof(struct log_info_cat));
646 }
647
648 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800649}