blob: e85e8fcbb5fb72d42ee43f74076d2633146a19df [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 Welte892e6212011-07-19 14:31:44 +020075 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020076 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020077 .description = "A-bis Intput Subsystem",
78 .loglevel = LOGL_NOTICE,
79 .enabled = 1,
80 },
Harald Welte892e6212011-07-19 14:31:44 +020081 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020082 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020083 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
84 .loglevel = LOGL_NOTICE,
85 .enabled = 1,
86 },
Harald Welte892e6212011-07-19 14:31:44 +020087 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +020088 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020089 .description = "A-bis Input Driver for Signalling",
90 .enabled = 0, .loglevel = LOGL_NOTICE,
91 },
Harald Welte892e6212011-07-19 14:31:44 +020092 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +020093 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020094 .description = "A-bis Input Driver for B-Channels (voice)",
95 .enabled = 0, .loglevel = LOGL_NOTICE,
96 },
Harald Welteb43bc042011-06-27 10:29:17 +020097};
98
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010099/* You have to keep this in sync with the structure loglevel_strs. */
100const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
101 "Log simply everything",
102 "Log debug messages and higher levels",
103 "Log informational messages and higher levels",
104 "Log noticable messages and higher levels",
105 "Log error messages and higher levels",
106 "Log only fatal messages",
107 NULL,
108};
109
Harald Welteb43bc042011-06-27 10:29:17 +0200110/* special magic for negative (library-internal) log subsystem numbers */
111static int subsys_lib2index(int subsys)
112{
113 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
114}
115
Harald Welte3ae27582010-03-26 21:24:24 +0800116int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800117{
118 return get_string_value(loglevel_strs, lvl);
119}
120
Harald Welte9ac22252010-05-11 11:19:40 +0200121const char *log_level_str(unsigned int lvl)
122{
123 return get_value_string(loglevel_strs, lvl);
124}
125
Harald Welte3ae27582010-03-26 21:24:24 +0800126int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800127{
128 int i;
129
Harald Welte4ebdf742010-05-19 19:54:00 +0200130 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200131 if (osmo_log_info->cat[i].name == NULL)
132 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200133 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800134 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800135 }
136
137 return -EINVAL;
138}
139
140/*
141 * Parse the category mask.
142 * The format can be this: category1:category2:category3
143 * or category1,2:category2,3:...
144 */
Harald Welte3ae27582010-03-26 21:24:24 +0800145void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800146{
147 int i = 0;
148 char *mask = strdup(_mask);
149 char *category_token = NULL;
150
151 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200152 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800153 target->categories[i].enabled = 0;
154
155 category_token = strtok(mask, ":");
156 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200157 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800158 char* colon = strstr(category_token, ",");
159 int length = strlen(category_token);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200160 int cat_length = strlen(osmo_log_info->cat[i].name);
161
162 /* Use longest length not to match subocurrences. */
163 if (cat_length > length)
164 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800165
Harald Welteb43bc042011-06-27 10:29:17 +0200166 if (!osmo_log_info->cat[i].name)
167 continue;
168
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800169 if (colon)
170 length = colon - category_token;
171
Harald Welte4ebdf742010-05-19 19:54:00 +0200172 if (strncasecmp(osmo_log_info->cat[i].name,
173 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800174 int level = 0;
175
176 if (colon)
177 level = atoi(colon+1);
178
Harald Weltefaadfe22010-03-26 21:05:43 +0800179 target->categories[i].enabled = 1;
180 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800181 }
182 }
183 } while ((category_token = strtok(NULL, ":")));
184
185 free(mask);
186}
187
188static const char* color(int subsys)
189{
Harald Welte4ebdf742010-05-19 19:54:00 +0200190 if (subsys < osmo_log_info->num_cat)
191 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800192
Harald Welted788f662010-03-26 09:45:03 +0800193 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800194}
195
Harald Welte3ae27582010-03-26 21:24:24 +0800196static void _output(struct log_target *target, unsigned int subsys,
Harald Welte76e72ab2011-02-17 15:52:39 +0100197 unsigned int level, char *file, int line, int cont,
198 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800199{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800200 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200201 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800202
203 /* are we using color */
204 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800205 const char *c = color(subsys);
206 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200207 ret = snprintf(buf + offset, rem, "%s", color(subsys));
208 if (ret < 0)
209 goto err;
210 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800211 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800212 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800213 if (!cont) {
214 if (target->print_timestamp) {
215 char *timestr;
216 time_t tm;
217 tm = time(NULL);
218 timestr = ctime(&tm);
219 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200220 ret = snprintf(buf + offset, rem, "%s ", timestr);
221 if (ret < 0)
222 goto err;
223 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800224 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200225 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
226 subsys, file, line);
227 if (ret < 0)
228 goto err;
229 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800230 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200231 ret = vsnprintf(buf + offset, rem, format, ap);
232 if (ret < 0)
233 goto err;
234 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800235
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200236 ret = snprintf(buf + offset, rem, "%s",
237 target->use_color ? "\033[0;m" : "");
238 if (ret < 0)
239 goto err;
240 OSMO_SNPRINTF_RET(ret, rem, offset, len);
241err:
242 buf[sizeof(buf)-1] = '\0';
243 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244}
245
Harald Welteb43bc042011-06-27 10:29:17 +0200246static void _logp(int subsys, int level, char *file, int line,
Harald Welte3ae27582010-03-26 21:24:24 +0800247 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800248{
Harald Welte3ae27582010-03-26 21:24:24 +0800249 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800250
Harald Welteb43bc042011-06-27 10:29:17 +0200251 if (subsys < 0)
252 subsys = subsys_lib2index(subsys);
253
254 if (subsys > osmo_log_info->num_cat)
255 subsys = DLGLOBAL;
256
Harald Welte28222962011-02-18 20:37:04 +0100257 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800258 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800259 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200260 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800261
262 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800263 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800264 if (!category->enabled)
265 continue;
266
267 /* Check the global log level */
268 if (tar->loglevel != 0 && level < tar->loglevel)
269 continue;
270
271 /* Check the category log level */
272 if (tar->loglevel == 0 && category->loglevel != 0 &&
273 level < category->loglevel)
274 continue;
275
276 /* Apply filters here... if that becomes messy we will
277 * need to put filters in a list and each filter will
278 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800279 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800280 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200281 else if (osmo_log_info->filter_fn)
282 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800283 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200284 if (!output)
285 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800286
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200287 /* According to the manpage, vsnprintf leaves the value of ap
288 * in undefined state. Since _output uses vsnprintf and it may
289 * be called several times, we have to pass a copy of ap. */
290 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200291 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200292 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800293 }
294}
295
Harald Welteb43bc042011-06-27 10:29:17 +0200296void logp(int subsys, char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800297 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800298{
299 va_list ap;
300
301 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800302 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800303 va_end(ap);
304}
305
Harald Welteb43bc042011-06-27 10:29:17 +0200306void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800307{
308 va_list ap;
309
310 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800311 _logp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800312 va_end(ap);
313}
314
Harald Welte3ae27582010-03-26 21:24:24 +0800315void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800316{
Harald Welte28222962011-02-18 20:37:04 +0100317 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800318}
319
Harald Welte3ae27582010-03-26 21:24:24 +0800320void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800321{
322 llist_del(&target->entry);
323}
324
Harald Welte3ae27582010-03-26 21:24:24 +0800325void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800326{
Harald Welte3ae27582010-03-26 21:24:24 +0800327 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800328}
329
Harald Welte3ae27582010-03-26 21:24:24 +0800330int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800331{
Harald Welte3ae27582010-03-26 21:24:24 +0800332 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800333 return -EINVAL;
334
Harald Welte3ae27582010-03-26 21:24:24 +0800335 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800336
337 return 0;
338}
339
Harald Welte3ae27582010-03-26 21:24:24 +0800340void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800341{
342 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800343 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800344 else
Harald Welte3ae27582010-03-26 21:24:24 +0800345 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800346}
347
Harald Welte3ae27582010-03-26 21:24:24 +0800348void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800349{
350 target->use_color = use_color;
351}
352
Harald Welte3ae27582010-03-26 21:24:24 +0800353void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800354{
355 target->print_timestamp = print_timestamp;
356}
357
Harald Welte3ae27582010-03-26 21:24:24 +0800358void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800359{
360 target->loglevel = log_level;
361}
362
Harald Welte3ae27582010-03-26 21:24:24 +0800363void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800364 int enable, int level)
365{
Harald Welte4ebdf742010-05-19 19:54:00 +0200366 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800367 return;
368 target->categories[category].enabled = !!enable;
369 target->categories[category].loglevel = level;
370}
371
Harald Welte76e72ab2011-02-17 15:52:39 +0100372static void _file_output(struct log_target *target, unsigned int level,
373 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800374{
Harald Welte0083cd32010-08-25 14:55:44 +0200375 fprintf(target->tgt_file.out, "%s", log);
376 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800377}
378
Harald Welte3ae27582010-03-26 21:24:24 +0800379struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800380{
Harald Welte3ae27582010-03-26 21:24:24 +0800381 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800382 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800383
Harald Welte3ae27582010-03-26 21:24:24 +0800384 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800385 if (!target)
386 return NULL;
387
Harald Welteb43bc042011-06-27 10:29:17 +0200388 target->categories = talloc_zero_array(target,
389 struct log_category,
390 osmo_log_info->num_cat);
391 if (!target->categories) {
392 talloc_free(target);
393 return NULL;
394 }
395
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800396 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800397
398 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200399 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800400 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200401 cat->enabled = osmo_log_info->cat[i].enabled;
402 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800403 }
404
405 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800406 target->use_color = 1;
407 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800408
409 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800410 target->loglevel = 0;
411 return target;
412}
413
Harald Welte3ae27582010-03-26 21:24:24 +0800414struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800415{
Harald Weltea3b844c2010-03-27 00:04:40 +0800416/* since C89/C99 says stderr is a macro, we can safely do this! */
417#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800418 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800419
Harald Welte3ae27582010-03-26 21:24:24 +0800420 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800421 if (!target)
422 return NULL;
423
Harald Welte28222962011-02-18 20:37:04 +0100424 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200425 target->tgt_file.out = stderr;
426 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800427 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800428#else
429 return NULL;
430#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800431}
432
Harald Welte3086c392010-08-25 19:10:50 +0200433struct log_target *log_target_create_file(const char *fname)
434{
435 struct log_target *target;
436
437 target = log_target_create();
438 if (!target)
439 return NULL;
440
Harald Welte28222962011-02-18 20:37:04 +0100441 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200442 target->tgt_file.out = fopen(fname, "a");
443 if (!target->tgt_file.out)
444 return NULL;
445
446 target->output = _file_output;
447
448 target->tgt_file.fname = talloc_strdup(target, fname);
449
450 return target;
451}
452
Harald Welte28222962011-02-18 20:37:04 +0100453struct log_target *log_target_find(int type, const char *fname)
454{
455 struct log_target *tgt;
456
457 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
458 if (tgt->type != type)
459 continue;
460 if (tgt->type == LOG_TGT_TYPE_FILE) {
461 if (!strcmp(fname, tgt->tgt_file.fname))
462 return tgt;
463 } else
464 return tgt;
465 }
466 return NULL;
467}
468
Harald Welte3086c392010-08-25 19:10:50 +0200469void log_target_destroy(struct log_target *target)
470{
471
472 /* just in case, to make sure we don't have any references */
473 log_del_target(target);
474
475 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200476/* since C89/C99 says stderr is a macro, we can safely do this! */
477#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200478 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200479 if (target->tgt_file.out != stderr)
480#endif
481 {
Harald Welte3086c392010-08-25 19:10:50 +0200482 fclose(target->tgt_file.out);
483 target->tgt_file.out = NULL;
484 }
485 }
486
487 talloc_free(target);
488}
489
490/* close and re-open a log file (for log file rotation) */
491int log_target_file_reopen(struct log_target *target)
492{
493 fclose(target->tgt_file.out);
494
495 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
496 if (!target->tgt_file.out)
497 return -errno;
498
499 /* we assume target->output already to be set */
500
501 return 0;
502}
503
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100504/* This generates the logging command string for VTY. */
Harald Weltece9fec32011-06-27 14:19:16 +0200505const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200506{
Harald Weltece9fec32011-06-27 14:19:16 +0200507 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100508 int len = 0, offset = 0, ret, i, rem;
509 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200510 char *str;
511
Harald Welteb43bc042011-06-27 10:29:17 +0200512 for (i = 0; i < info->num_cat; i++) {
513 if (info->cat[i].name == NULL)
514 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100515 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200516 }
Harald Welte7638af92010-05-11 16:39:22 +0200517
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100518 for (i = 0; i < LOGLEVEL_DEFS; i++)
519 size += strlen(loglevel_strs[i].str) + 1;
520
521 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200522 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200523 if (!str)
524 return NULL;
525
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200526 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100527 if (ret < 0)
528 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200529 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200530
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100531 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200532 if (info->cat[i].name) {
533 int j, name_len = strlen(info->cat[i].name)+1;
534 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100535
Harald Welteb43bc042011-06-27 10:29:17 +0200536 for (j = 0; j < name_len; j++)
537 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100538
Harald Welteb43bc042011-06-27 10:29:17 +0200539 name[name_len-1] = '\0';
540 ret = snprintf(str + offset, rem, "%s|", name+1);
541 if (ret < 0)
542 goto err;
543 OSMO_SNPRINTF_RET(ret, rem, offset, len);
544 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100545 }
546 offset--; /* to remove the trailing | */
547 rem++;
548
549 ret = snprintf(str + offset, rem, ") (");
550 if (ret < 0)
551 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200552 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100553
554 for (i = 0; i < LOGLEVEL_DEFS; i++) {
555 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
556 char loglevel_str[loglevel_str_len];
557
558 for (j = 0; j < loglevel_str_len; j++)
559 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
560
561 loglevel_str[loglevel_str_len-1] = '\0';
562 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
563 if (ret < 0)
564 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200565 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100566 }
567 offset--; /* to remove the trailing | */
568 rem++;
569
570 ret = snprintf(str + offset, rem, ")");
571 if (ret < 0)
572 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200573 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100574err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200575 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100576 return str;
577}
578
579/* This generates the logging command description for VTY. */
Harald Weltece9fec32011-06-27 14:19:16 +0200580const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100581{
Harald Weltece9fec32011-06-27 14:19:16 +0200582 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100583 char *str;
584 int i, ret, len = 0, offset = 0, rem;
585 unsigned int size =
586 strlen(LOGGING_STR
587 "Set the log level for a specified category\n") + 1;
588
Harald Welteb43bc042011-06-27 10:29:17 +0200589 for (i = 0; i < info->num_cat; i++) {
590 if (info->cat[i].name == NULL)
591 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100592 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200593 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100594
595 for (i = 0; i < LOGLEVEL_DEFS; i++)
596 size += strlen(loglevel_descriptions[i]) + 1;
597
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200598 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100599 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200600 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100601 if (!str)
602 return NULL;
603
604 ret = snprintf(str + offset, rem, LOGGING_STR
605 "Set the log level for a specified category\n");
606 if (ret < 0)
607 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200608 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100609
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200610 ret = snprintf(str + offset, rem,
611 "Global setting for all subsystems\n");
612 if (ret < 0)
613 goto err;
614 OSMO_SNPRINTF_RET(ret, rem, offset, len);
615
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100616 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200617 if (info->cat[i].name == NULL)
618 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100619 ret = snprintf(str + offset, rem, "%s\n",
620 info->cat[i].description);
621 if (ret < 0)
622 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200623 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100624 }
625 for (i = 0; i < LOGLEVEL_DEFS; i++) {
626 ret = snprintf(str + offset, rem, "%s\n",
627 loglevel_descriptions[i]);
628 if (ret < 0)
629 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200630 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100631 }
632err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200633 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200634 return str;
635}
636
Harald Welteb43bc042011-06-27 10:29:17 +0200637int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800638{
Harald Welteb43bc042011-06-27 10:29:17 +0200639 int i;
640
641 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
642 if (!tall_log_ctx)
643 return -ENOMEM;
644
645 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
646 if (!osmo_log_info)
647 return -ENOMEM;
648
649 osmo_log_info->num_cat_user = inf->num_cat;
650 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200651 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200652
653 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
654 struct log_info_cat,
655 osmo_log_info->num_cat);
656 if (!osmo_log_info->cat) {
657 talloc_free(osmo_log_info);
658 osmo_log_info = NULL;
659 return -ENOMEM;
660 }
661
662 /* copy over the user part */
663 for (i = 0; i < inf->num_cat; i++) {
664 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
665 sizeof(struct log_info_cat));
666 }
667
668 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200669 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200670 unsigned int cn = osmo_log_info->num_cat_user + i;
671 memcpy(&osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200672 &internal_cat[i], sizeof(struct log_info_cat));
673 }
674
675 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800676}