blob: 9dd63e7353ca5470f9ecaa6bb1683cf6dc7bf0a3 [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 Welte18fc4652011-08-17 14:14:17 +020023/* \addtogroup logging
24 * @{
25 */
26
27/* \file logging.c */
28
Harald Welte01fd5cb2010-03-26 23:51:31 +080029#include "../config.h"
30
Harald Welte4a2bb9e2010-03-26 09:33:40 +080031#include <stdarg.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010035#include <ctype.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080036
37#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080038#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080039#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +080040#include <time.h>
41#include <errno.h>
42
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010043#include <osmocom/core/talloc.h>
44#include <osmocom/core/utils.h>
45#include <osmocom/core/logging.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080046
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010047#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
48
Harald Welteb43bc042011-06-27 10:29:17 +020049struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080050
Harald Welte3ae27582010-03-26 21:24:24 +080051static struct log_context log_context;
52static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010053LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080054
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010055#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
56
57static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte4a2bb9e2010-03-26 09:33:40 +080058 { 0, "EVERYTHING" },
59 { LOGL_DEBUG, "DEBUG" },
60 { LOGL_INFO, "INFO" },
61 { LOGL_NOTICE, "NOTICE" },
62 { LOGL_ERROR, "ERROR" },
63 { LOGL_FATAL, "FATAL" },
64 { 0, NULL },
65};
66
Harald Welteb43bc042011-06-27 10:29:17 +020067#define INT2IDX(x) (-1*(x)-1)
68static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
69 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
70 .name = "DLGLOBAL",
71 .description = "Library-internal global log family",
72 .loglevel = LOGL_NOTICE,
73 .enabled = 1,
74 },
Harald Welte1f0b8c22011-06-27 10:51:37 +020075 [INT2IDX(DLLAPDM)] = { /* -2 becomes 1 */
76 .name = "DLLAPDM",
77 .description = "LAPDm in libosmogsm",
78 .loglevel = LOGL_NOTICE,
79 .enabled = 1,
80 },
Harald Welte892e6212011-07-19 14:31:44 +020081 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020082 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020083 .description = "A-bis Intput Subsystem",
84 .loglevel = LOGL_NOTICE,
85 .enabled = 1,
86 },
Harald Welte892e6212011-07-19 14:31:44 +020087 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020088 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020089 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
90 .loglevel = LOGL_NOTICE,
91 .enabled = 1,
92 },
Harald Welte892e6212011-07-19 14:31:44 +020093 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +020094 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020095 .description = "A-bis Input Driver for Signalling",
96 .enabled = 0, .loglevel = LOGL_NOTICE,
97 },
Harald Welte892e6212011-07-19 14:31:44 +020098 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +020099 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200100 .description = "A-bis Input Driver for B-Channels (voice)",
101 .enabled = 0, .loglevel = LOGL_NOTICE,
102 },
Harald Welteb43bc042011-06-27 10:29:17 +0200103};
104
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100105/* You have to keep this in sync with the structure loglevel_strs. */
106const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
107 "Log simply everything",
108 "Log debug messages and higher levels",
109 "Log informational messages and higher levels",
110 "Log noticable messages and higher levels",
111 "Log error messages and higher levels",
112 "Log only fatal messages",
113 NULL,
114};
115
Harald Welteb43bc042011-06-27 10:29:17 +0200116/* special magic for negative (library-internal) log subsystem numbers */
117static int subsys_lib2index(int subsys)
118{
119 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
120}
121
Harald Welte18fc4652011-08-17 14:14:17 +0200122/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800123int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800124{
125 return get_string_value(loglevel_strs, lvl);
126}
127
Harald Welte18fc4652011-08-17 14:14:17 +0200128/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200129const char *log_level_str(unsigned int lvl)
130{
131 return get_value_string(loglevel_strs, lvl);
132}
133
Harald Welte18fc4652011-08-17 14:14:17 +0200134/*! \brief parse a human-readable log category into numeric form
135 * \param[in] category human-readable log category name
136 * \returns numeric category value, or -EINVAL otherwise
137 */
Harald Welte3ae27582010-03-26 21:24:24 +0800138int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800139{
140 int i;
141
Harald Welte4ebdf742010-05-19 19:54:00 +0200142 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200143 if (osmo_log_info->cat[i].name == NULL)
144 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200145 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800146 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800147 }
148
149 return -EINVAL;
150}
151
Harald Welte18fc4652011-08-17 14:14:17 +0200152/*! \brief parse the log category mask
153 * \param[in] target log target to be configured
154 * \param[in] _mask log category mask string
155 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800156 * The format can be this: category1:category2:category3
157 * or category1,2:category2,3:...
158 */
Harald Welte3ae27582010-03-26 21:24:24 +0800159void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800160{
161 int i = 0;
162 char *mask = strdup(_mask);
163 char *category_token = NULL;
164
165 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200166 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800167 target->categories[i].enabled = 0;
168
169 category_token = strtok(mask, ":");
170 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200171 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800172 char* colon = strstr(category_token, ",");
173 int length = strlen(category_token);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200174 int cat_length = strlen(osmo_log_info->cat[i].name);
175
176 /* Use longest length not to match subocurrences. */
177 if (cat_length > length)
178 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800179
Harald Welteb43bc042011-06-27 10:29:17 +0200180 if (!osmo_log_info->cat[i].name)
181 continue;
182
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800183 if (colon)
184 length = colon - category_token;
185
Harald Welte4ebdf742010-05-19 19:54:00 +0200186 if (strncasecmp(osmo_log_info->cat[i].name,
187 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800188 int level = 0;
189
190 if (colon)
191 level = atoi(colon+1);
192
Harald Weltefaadfe22010-03-26 21:05:43 +0800193 target->categories[i].enabled = 1;
194 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800195 }
196 }
197 } while ((category_token = strtok(NULL, ":")));
198
199 free(mask);
200}
201
202static const char* color(int subsys)
203{
Harald Welte4ebdf742010-05-19 19:54:00 +0200204 if (subsys < osmo_log_info->num_cat)
205 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800206
Harald Welted788f662010-03-26 09:45:03 +0800207 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800208}
209
Harald Welte3ae27582010-03-26 21:24:24 +0800210static void _output(struct log_target *target, unsigned int subsys,
Harald Welte76e72ab2011-02-17 15:52:39 +0100211 unsigned int level, char *file, int line, int cont,
212 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800213{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800214 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200215 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800216
217 /* are we using color */
218 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800219 const char *c = color(subsys);
220 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200221 ret = snprintf(buf + offset, rem, "%s", color(subsys));
222 if (ret < 0)
223 goto err;
224 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800225 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800226 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800227 if (!cont) {
228 if (target->print_timestamp) {
229 char *timestr;
230 time_t tm;
231 tm = time(NULL);
232 timestr = ctime(&tm);
233 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200234 ret = snprintf(buf + offset, rem, "%s ", timestr);
235 if (ret < 0)
236 goto err;
237 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800238 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200239 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
240 subsys, file, line);
241 if (ret < 0)
242 goto err;
243 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200245 ret = vsnprintf(buf + offset, rem, format, ap);
246 if (ret < 0)
247 goto err;
248 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800249
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200250 ret = snprintf(buf + offset, rem, "%s",
251 target->use_color ? "\033[0;m" : "");
252 if (ret < 0)
253 goto err;
254 OSMO_SNPRINTF_RET(ret, rem, offset, len);
255err:
256 buf[sizeof(buf)-1] = '\0';
257 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800258}
259
Harald Welte36c5a3e2011-08-27 14:33:19 +0200260/*! \brief vararg version of logging function */
261void osmo_vlogp(int subsys, int level, char *file, int line,
262 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800263{
Harald Welte3ae27582010-03-26 21:24:24 +0800264 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800265
Harald Welteb43bc042011-06-27 10:29:17 +0200266 if (subsys < 0)
267 subsys = subsys_lib2index(subsys);
268
269 if (subsys > osmo_log_info->num_cat)
270 subsys = DLGLOBAL;
271
Harald Welte28222962011-02-18 20:37:04 +0100272 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800273 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800274 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200275 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800276
277 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800278 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800279 if (!category->enabled)
280 continue;
281
282 /* Check the global log level */
283 if (tar->loglevel != 0 && level < tar->loglevel)
284 continue;
285
286 /* Check the category log level */
287 if (tar->loglevel == 0 && category->loglevel != 0 &&
288 level < category->loglevel)
289 continue;
290
291 /* Apply filters here... if that becomes messy we will
292 * need to put filters in a list and each filter will
293 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800294 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800295 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200296 else if (osmo_log_info->filter_fn)
297 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800298 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200299 if (!output)
300 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800301
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200302 /* According to the manpage, vsnprintf leaves the value of ap
303 * in undefined state. Since _output uses vsnprintf and it may
304 * be called several times, we have to pass a copy of ap. */
305 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200306 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200307 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800308 }
309}
310
Harald Welteb43bc042011-06-27 10:29:17 +0200311void logp(int subsys, char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800312 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800313{
314 va_list ap;
315
316 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200317 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800318 va_end(ap);
319}
320
Harald Welteb43bc042011-06-27 10:29:17 +0200321void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800322{
323 va_list ap;
324
325 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200326 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800327 va_end(ap);
328}
329
Harald Welte18fc4652011-08-17 14:14:17 +0200330/*! \brief Register a new log target with the logging core
331 * \param[in] target Log target to be registered
332 */
Harald Welte3ae27582010-03-26 21:24:24 +0800333void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800334{
Harald Welte28222962011-02-18 20:37:04 +0100335 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800336}
337
Harald Welte18fc4652011-08-17 14:14:17 +0200338/*! \brief Unregister a log target from the logging core
339 * \param[in] target Log target to be unregistered
340 */
Harald Welte3ae27582010-03-26 21:24:24 +0800341void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800342{
343 llist_del(&target->entry);
344}
345
Harald Welte18fc4652011-08-17 14:14:17 +0200346/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800347void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800348{
Harald Welte3ae27582010-03-26 21:24:24 +0800349 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800350}
351
Harald Welte18fc4652011-08-17 14:14:17 +0200352/*! \brief Set the logging context
353 * \param[in] ctx_nr logging context number
354 * \param[in] value value to which the context is to be set
355 *
356 * A logging context is something like the subscriber identity to which
357 * the currently processed message relates, or the BTS through which it
358 * was received. As soon as this data is known, it can be set using
359 * this function. The main use of context information is for logging
360 * filters.
361 */
Harald Welte3ae27582010-03-26 21:24:24 +0800362int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800363{
Harald Welte3ae27582010-03-26 21:24:24 +0800364 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800365 return -EINVAL;
366
Harald Welte3ae27582010-03-26 21:24:24 +0800367 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800368
369 return 0;
370}
371
Harald Welte18fc4652011-08-17 14:14:17 +0200372/*! \brief Enable the \ref LOG_FILTER_ALL log filter
373 * \param[in] target Log target to be affected
374 * \param[in] all enable (1) or disable (0) the ALL filter
375 *
376 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
377 * be printed. It acts as a wildcard. Setting it to \a 1 means there
378 * is no filtering.
379 */
Harald Welte3ae27582010-03-26 21:24:24 +0800380void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800381{
382 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800383 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800384 else
Harald Welte3ae27582010-03-26 21:24:24 +0800385 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800386}
387
Harald Welte18fc4652011-08-17 14:14:17 +0200388/*! \brief Enable or disable the use of colored output
389 * \param[in] target Log target to be affected
390 * \param[in] use_color Use color (1) or don't use color (0)
391 */
Harald Welte3ae27582010-03-26 21:24:24 +0800392void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800393{
394 target->use_color = use_color;
395}
396
Harald Welte18fc4652011-08-17 14:14:17 +0200397/*! \brief Enable or disable printing of timestamps while logging
398 * \param[in] target Log target to be affected
399 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
400 */
Harald Welte3ae27582010-03-26 21:24:24 +0800401void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800402{
403 target->print_timestamp = print_timestamp;
404}
405
Harald Welte18fc4652011-08-17 14:14:17 +0200406/*! \brief Set the global log level for a given log target
407 * \param[in] target Log target to be affected
408 * \param[in] log_level New global log level
409 */
Harald Welte3ae27582010-03-26 21:24:24 +0800410void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800411{
412 target->loglevel = log_level;
413}
414
Harald Welte3ae27582010-03-26 21:24:24 +0800415void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800416 int enable, int level)
417{
Harald Welte4ebdf742010-05-19 19:54:00 +0200418 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800419 return;
420 target->categories[category].enabled = !!enable;
421 target->categories[category].loglevel = level;
422}
423
Harald Welte76e72ab2011-02-17 15:52:39 +0100424static void _file_output(struct log_target *target, unsigned int level,
425 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800426{
Harald Welte0083cd32010-08-25 14:55:44 +0200427 fprintf(target->tgt_file.out, "%s", log);
428 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800429}
430
Harald Welte18fc4652011-08-17 14:14:17 +0200431/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800432struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800433{
Harald Welte3ae27582010-03-26 21:24:24 +0800434 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800435 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800436
Harald Welte3ae27582010-03-26 21:24:24 +0800437 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800438 if (!target)
439 return NULL;
440
Harald Welteb43bc042011-06-27 10:29:17 +0200441 target->categories = talloc_zero_array(target,
442 struct log_category,
443 osmo_log_info->num_cat);
444 if (!target->categories) {
445 talloc_free(target);
446 return NULL;
447 }
448
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800449 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800450
451 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200452 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800453 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200454 cat->enabled = osmo_log_info->cat[i].enabled;
455 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800456 }
457
458 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800459 target->use_color = 1;
460 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800461
462 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800463 target->loglevel = 0;
464 return target;
465}
466
Harald Welte18fc4652011-08-17 14:14:17 +0200467/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800468struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800469{
Harald Weltea3b844c2010-03-27 00:04:40 +0800470/* since C89/C99 says stderr is a macro, we can safely do this! */
471#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800472 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800473
Harald Welte3ae27582010-03-26 21:24:24 +0800474 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800475 if (!target)
476 return NULL;
477
Harald Welte28222962011-02-18 20:37:04 +0100478 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200479 target->tgt_file.out = stderr;
480 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800481 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800482#else
483 return NULL;
484#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800485}
486
Harald Welte18fc4652011-08-17 14:14:17 +0200487/*! \brief Create a new file-based log target
488 * \param[in] fname File name of the new log file
489 * \returns Log target in case of success, NULL otherwise
490 */
Harald Welte3086c392010-08-25 19:10:50 +0200491struct log_target *log_target_create_file(const char *fname)
492{
493 struct log_target *target;
494
495 target = log_target_create();
496 if (!target)
497 return NULL;
498
Harald Welte28222962011-02-18 20:37:04 +0100499 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200500 target->tgt_file.out = fopen(fname, "a");
501 if (!target->tgt_file.out)
502 return NULL;
503
504 target->output = _file_output;
505
506 target->tgt_file.fname = talloc_strdup(target, fname);
507
508 return target;
509}
510
Harald Welte18fc4652011-08-17 14:14:17 +0200511/*! \brief Find a registered log target
512 * \param[in] type Log target type
513 * \param[in] fname File name
514 * \returns Log target (if found), NULL otherwise
515 */
Harald Welte28222962011-02-18 20:37:04 +0100516struct log_target *log_target_find(int type, const char *fname)
517{
518 struct log_target *tgt;
519
520 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
521 if (tgt->type != type)
522 continue;
523 if (tgt->type == LOG_TGT_TYPE_FILE) {
524 if (!strcmp(fname, tgt->tgt_file.fname))
525 return tgt;
526 } else
527 return tgt;
528 }
529 return NULL;
530}
531
Harald Welte18fc4652011-08-17 14:14:17 +0200532/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200533void log_target_destroy(struct log_target *target)
534{
535
536 /* just in case, to make sure we don't have any references */
537 log_del_target(target);
538
539 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200540/* since C89/C99 says stderr is a macro, we can safely do this! */
541#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200542 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200543 if (target->tgt_file.out != stderr)
544#endif
545 {
Harald Welte3086c392010-08-25 19:10:50 +0200546 fclose(target->tgt_file.out);
547 target->tgt_file.out = NULL;
548 }
549 }
550
551 talloc_free(target);
552}
553
Harald Welte18fc4652011-08-17 14:14:17 +0200554/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200555int log_target_file_reopen(struct log_target *target)
556{
557 fclose(target->tgt_file.out);
558
559 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
560 if (!target->tgt_file.out)
561 return -errno;
562
563 /* we assume target->output already to be set */
564
565 return 0;
566}
567
Harald Welte18fc4652011-08-17 14:14:17 +0200568/*! \brief Generates the logging command string for VTY
569 * \param[in] unused_info Deprecated parameter, no longer used!
570 */
Harald Weltece9fec32011-06-27 14:19:16 +0200571const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200572{
Harald Weltece9fec32011-06-27 14:19:16 +0200573 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100574 int len = 0, offset = 0, ret, i, rem;
575 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200576 char *str;
577
Harald Welteb43bc042011-06-27 10:29:17 +0200578 for (i = 0; i < info->num_cat; i++) {
579 if (info->cat[i].name == NULL)
580 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100581 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200582 }
Harald Welte7638af92010-05-11 16:39:22 +0200583
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100584 for (i = 0; i < LOGLEVEL_DEFS; i++)
585 size += strlen(loglevel_strs[i].str) + 1;
586
587 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200588 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200589 if (!str)
590 return NULL;
591
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200592 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100593 if (ret < 0)
594 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200595 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200596
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100597 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200598 if (info->cat[i].name) {
599 int j, name_len = strlen(info->cat[i].name)+1;
600 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100601
Harald Welteb43bc042011-06-27 10:29:17 +0200602 for (j = 0; j < name_len; j++)
603 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100604
Harald Welteb43bc042011-06-27 10:29:17 +0200605 name[name_len-1] = '\0';
606 ret = snprintf(str + offset, rem, "%s|", name+1);
607 if (ret < 0)
608 goto err;
609 OSMO_SNPRINTF_RET(ret, rem, offset, len);
610 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100611 }
612 offset--; /* to remove the trailing | */
613 rem++;
614
615 ret = snprintf(str + offset, rem, ") (");
616 if (ret < 0)
617 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200618 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100619
620 for (i = 0; i < LOGLEVEL_DEFS; i++) {
621 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
622 char loglevel_str[loglevel_str_len];
623
624 for (j = 0; j < loglevel_str_len; j++)
625 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
626
627 loglevel_str[loglevel_str_len-1] = '\0';
628 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
629 if (ret < 0)
630 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200631 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100632 }
633 offset--; /* to remove the trailing | */
634 rem++;
635
636 ret = snprintf(str + offset, rem, ")");
637 if (ret < 0)
638 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200639 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100640err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200641 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100642 return str;
643}
644
Harald Welte18fc4652011-08-17 14:14:17 +0200645/*! \brief Generates the logging command description for VTY
646 * \param[in] unused_info Deprecated parameter, no longer used!
647 */
Harald Weltece9fec32011-06-27 14:19:16 +0200648const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100649{
Harald Weltece9fec32011-06-27 14:19:16 +0200650 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100651 char *str;
652 int i, ret, len = 0, offset = 0, rem;
653 unsigned int size =
654 strlen(LOGGING_STR
655 "Set the log level for a specified category\n") + 1;
656
Harald Welteb43bc042011-06-27 10:29:17 +0200657 for (i = 0; i < info->num_cat; i++) {
658 if (info->cat[i].name == NULL)
659 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100660 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200661 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100662
663 for (i = 0; i < LOGLEVEL_DEFS; i++)
664 size += strlen(loglevel_descriptions[i]) + 1;
665
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200666 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100667 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200668 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100669 if (!str)
670 return NULL;
671
672 ret = snprintf(str + offset, rem, LOGGING_STR
673 "Set the log level for a specified category\n");
674 if (ret < 0)
675 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200676 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100677
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200678 ret = snprintf(str + offset, rem,
679 "Global setting for all subsystems\n");
680 if (ret < 0)
681 goto err;
682 OSMO_SNPRINTF_RET(ret, rem, offset, len);
683
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100684 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200685 if (info->cat[i].name == NULL)
686 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100687 ret = snprintf(str + offset, rem, "%s\n",
688 info->cat[i].description);
689 if (ret < 0)
690 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200691 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100692 }
693 for (i = 0; i < LOGLEVEL_DEFS; i++) {
694 ret = snprintf(str + offset, rem, "%s\n",
695 loglevel_descriptions[i]);
696 if (ret < 0)
697 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200698 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100699 }
700err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200701 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200702 return str;
703}
704
Harald Welte18fc4652011-08-17 14:14:17 +0200705/*! \brief Initialize the Osmocom logging core
706 * \param[in] inf Information regarding logging categories
707 * \param[in] ctx \ref talloc context for logging allocations
708 * \returns 0 in case of success, negative in case of error
709 */
Harald Welteb43bc042011-06-27 10:29:17 +0200710int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800711{
Harald Welteb43bc042011-06-27 10:29:17 +0200712 int i;
713
714 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
715 if (!tall_log_ctx)
716 return -ENOMEM;
717
718 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
719 if (!osmo_log_info)
720 return -ENOMEM;
721
722 osmo_log_info->num_cat_user = inf->num_cat;
723 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200724 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200725
726 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
727 struct log_info_cat,
728 osmo_log_info->num_cat);
729 if (!osmo_log_info->cat) {
730 talloc_free(osmo_log_info);
731 osmo_log_info = NULL;
732 return -ENOMEM;
733 }
734
735 /* copy over the user part */
736 for (i = 0; i < inf->num_cat; i++) {
737 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
738 sizeof(struct log_info_cat));
739 }
740
741 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200742 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200743 unsigned int cn = osmo_log_info->num_cat_user + i;
744 memcpy(&osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200745 &internal_cat[i], sizeof(struct log_info_cat));
746 }
747
748 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800749}
Harald Welte18fc4652011-08-17 14:14:17 +0200750
751/*! }@ */