blob: 8dfc31ed36a641f9537d766e616c5f1ec8411165 [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 },
root8a996b42011-09-26 11:22:21 +020075 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
76 .name = "DLLAPD",
77 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +020078 .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 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200103 [INT2IDX(DLSMS)] = {
104 .name = "DLSMS",
105 .description = "Layer3 Short Message Service (SMS)",
106 .enabled = 1, .loglevel = LOGL_NOTICE,
107 .color = "\033[1;38m",
108 },
Harald Welteb43bc042011-06-27 10:29:17 +0200109};
110
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100111/* You have to keep this in sync with the structure loglevel_strs. */
112const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
113 "Log simply everything",
114 "Log debug messages and higher levels",
115 "Log informational messages and higher levels",
116 "Log noticable messages and higher levels",
117 "Log error messages and higher levels",
118 "Log only fatal messages",
119 NULL,
120};
121
Harald Welteb43bc042011-06-27 10:29:17 +0200122/* special magic for negative (library-internal) log subsystem numbers */
123static int subsys_lib2index(int subsys)
124{
125 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
126}
127
Harald Welte18fc4652011-08-17 14:14:17 +0200128/*! \brief Parse a human-readable log level into a numeric value */
Harald Welte3ae27582010-03-26 21:24:24 +0800129int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800130{
131 return get_string_value(loglevel_strs, lvl);
132}
133
Harald Welte18fc4652011-08-17 14:14:17 +0200134/*! \brief convert a numeric log level into human-readable string */
Harald Welte9ac22252010-05-11 11:19:40 +0200135const char *log_level_str(unsigned int lvl)
136{
137 return get_value_string(loglevel_strs, lvl);
138}
139
Harald Welte18fc4652011-08-17 14:14:17 +0200140/*! \brief parse a human-readable log category into numeric form
141 * \param[in] category human-readable log category name
142 * \returns numeric category value, or -EINVAL otherwise
143 */
Harald Welte3ae27582010-03-26 21:24:24 +0800144int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800145{
146 int i;
147
Harald Welte4ebdf742010-05-19 19:54:00 +0200148 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200149 if (osmo_log_info->cat[i].name == NULL)
150 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200151 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800152 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800153 }
154
155 return -EINVAL;
156}
157
Harald Welte18fc4652011-08-17 14:14:17 +0200158/*! \brief parse the log category mask
159 * \param[in] target log target to be configured
160 * \param[in] _mask log category mask string
161 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800162 * The format can be this: category1:category2:category3
163 * or category1,2:category2,3:...
164 */
Harald Welte3ae27582010-03-26 21:24:24 +0800165void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800166{
167 int i = 0;
168 char *mask = strdup(_mask);
169 char *category_token = NULL;
170
171 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200172 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800173 target->categories[i].enabled = 0;
174
175 category_token = strtok(mask, ":");
176 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200177 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800178 char* colon = strstr(category_token, ",");
179 int length = strlen(category_token);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200180 int cat_length = strlen(osmo_log_info->cat[i].name);
181
182 /* Use longest length not to match subocurrences. */
183 if (cat_length > length)
184 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800185
Harald Welteb43bc042011-06-27 10:29:17 +0200186 if (!osmo_log_info->cat[i].name)
187 continue;
188
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800189 if (colon)
190 length = colon - category_token;
191
Harald Welte4ebdf742010-05-19 19:54:00 +0200192 if (strncasecmp(osmo_log_info->cat[i].name,
193 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800194 int level = 0;
195
196 if (colon)
197 level = atoi(colon+1);
198
Harald Weltefaadfe22010-03-26 21:05:43 +0800199 target->categories[i].enabled = 1;
200 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800201 }
202 }
203 } while ((category_token = strtok(NULL, ":")));
204
205 free(mask);
206}
207
208static const char* color(int subsys)
209{
Harald Welte4ebdf742010-05-19 19:54:00 +0200210 if (subsys < osmo_log_info->num_cat)
211 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800212
Harald Welted788f662010-03-26 09:45:03 +0800213 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800214}
215
Harald Welte3ae27582010-03-26 21:24:24 +0800216static void _output(struct log_target *target, unsigned int subsys,
Harald Welte76e72ab2011-02-17 15:52:39 +0100217 unsigned int level, char *file, int line, int cont,
218 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800219{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800220 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200221 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800222
223 /* are we using color */
224 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800225 const char *c = color(subsys);
226 if (c) {
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200227 ret = snprintf(buf + offset, rem, "%s", color(subsys));
228 if (ret < 0)
229 goto err;
230 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800231 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800232 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800233 if (!cont) {
234 if (target->print_timestamp) {
235 char *timestr;
236 time_t tm;
237 tm = time(NULL);
238 timestr = ctime(&tm);
239 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200240 ret = snprintf(buf + offset, rem, "%s ", timestr);
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 = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
246 subsys, file, line);
247 if (ret < 0)
248 goto err;
249 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800250 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200251 ret = vsnprintf(buf + offset, rem, format, ap);
252 if (ret < 0)
253 goto err;
254 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800255
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200256 ret = snprintf(buf + offset, rem, "%s",
257 target->use_color ? "\033[0;m" : "");
258 if (ret < 0)
259 goto err;
260 OSMO_SNPRINTF_RET(ret, rem, offset, len);
261err:
262 buf[sizeof(buf)-1] = '\0';
263 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800264}
265
Harald Welte36c5a3e2011-08-27 14:33:19 +0200266/*! \brief vararg version of logging function */
267void osmo_vlogp(int subsys, int level, char *file, int line,
268 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800269{
Harald Welte3ae27582010-03-26 21:24:24 +0800270 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800271
Harald Welteb43bc042011-06-27 10:29:17 +0200272 if (subsys < 0)
273 subsys = subsys_lib2index(subsys);
274
275 if (subsys > osmo_log_info->num_cat)
276 subsys = DLGLOBAL;
277
Harald Welte28222962011-02-18 20:37:04 +0100278 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800279 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800280 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200281 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800282
283 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800284 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800285 if (!category->enabled)
286 continue;
287
288 /* Check the global log level */
289 if (tar->loglevel != 0 && level < tar->loglevel)
290 continue;
291
292 /* Check the category log level */
293 if (tar->loglevel == 0 && category->loglevel != 0 &&
294 level < category->loglevel)
295 continue;
296
297 /* Apply filters here... if that becomes messy we will
298 * need to put filters in a list and each filter will
299 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800300 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800301 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200302 else if (osmo_log_info->filter_fn)
303 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800304 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200305 if (!output)
306 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800307
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200308 /* According to the manpage, vsnprintf leaves the value of ap
309 * in undefined state. Since _output uses vsnprintf and it may
310 * be called several times, we have to pass a copy of ap. */
311 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200312 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200313 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800314 }
315}
316
Harald Welteb43bc042011-06-27 10:29:17 +0200317void logp(int subsys, char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800318 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800319{
320 va_list ap;
321
322 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200323 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324 va_end(ap);
325}
326
Harald Welteb43bc042011-06-27 10:29:17 +0200327void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800328{
329 va_list ap;
330
331 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200332 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800333 va_end(ap);
334}
335
Harald Welte18fc4652011-08-17 14:14:17 +0200336/*! \brief Register a new log target with the logging core
337 * \param[in] target Log target to be registered
338 */
Harald Welte3ae27582010-03-26 21:24:24 +0800339void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800340{
Harald Welte28222962011-02-18 20:37:04 +0100341 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800342}
343
Harald Welte18fc4652011-08-17 14:14:17 +0200344/*! \brief Unregister a log target from the logging core
345 * \param[in] target Log target to be unregistered
346 */
Harald Welte3ae27582010-03-26 21:24:24 +0800347void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800348{
349 llist_del(&target->entry);
350}
351
Harald Welte18fc4652011-08-17 14:14:17 +0200352/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800353void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800354{
Harald Welte3ae27582010-03-26 21:24:24 +0800355 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800356}
357
Harald Welte18fc4652011-08-17 14:14:17 +0200358/*! \brief Set the logging context
359 * \param[in] ctx_nr logging context number
360 * \param[in] value value to which the context is to be set
361 *
362 * A logging context is something like the subscriber identity to which
363 * the currently processed message relates, or the BTS through which it
364 * was received. As soon as this data is known, it can be set using
365 * this function. The main use of context information is for logging
366 * filters.
367 */
Harald Welte3ae27582010-03-26 21:24:24 +0800368int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800369{
Harald Welte3ae27582010-03-26 21:24:24 +0800370 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800371 return -EINVAL;
372
Harald Welte3ae27582010-03-26 21:24:24 +0800373 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800374
375 return 0;
376}
377
Harald Welte18fc4652011-08-17 14:14:17 +0200378/*! \brief Enable the \ref LOG_FILTER_ALL log filter
379 * \param[in] target Log target to be affected
380 * \param[in] all enable (1) or disable (0) the ALL filter
381 *
382 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
383 * be printed. It acts as a wildcard. Setting it to \a 1 means there
384 * is no filtering.
385 */
Harald Welte3ae27582010-03-26 21:24:24 +0800386void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800387{
388 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800389 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800390 else
Harald Welte3ae27582010-03-26 21:24:24 +0800391 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800392}
393
Harald Welte18fc4652011-08-17 14:14:17 +0200394/*! \brief Enable or disable the use of colored output
395 * \param[in] target Log target to be affected
396 * \param[in] use_color Use color (1) or don't use color (0)
397 */
Harald Welte3ae27582010-03-26 21:24:24 +0800398void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800399{
400 target->use_color = use_color;
401}
402
Harald Welte18fc4652011-08-17 14:14:17 +0200403/*! \brief Enable or disable printing of timestamps while logging
404 * \param[in] target Log target to be affected
405 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
406 */
Harald Welte3ae27582010-03-26 21:24:24 +0800407void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800408{
409 target->print_timestamp = print_timestamp;
410}
411
Harald Welte18fc4652011-08-17 14:14:17 +0200412/*! \brief Set the global log level for a given log target
413 * \param[in] target Log target to be affected
414 * \param[in] log_level New global log level
415 */
Harald Welte3ae27582010-03-26 21:24:24 +0800416void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800417{
418 target->loglevel = log_level;
419}
420
Harald Welte3ae27582010-03-26 21:24:24 +0800421void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800422 int enable, int level)
423{
Harald Welte4ebdf742010-05-19 19:54:00 +0200424 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800425 return;
426 target->categories[category].enabled = !!enable;
427 target->categories[category].loglevel = level;
428}
429
Harald Welte76e72ab2011-02-17 15:52:39 +0100430static void _file_output(struct log_target *target, unsigned int level,
431 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800432{
Harald Welte0083cd32010-08-25 14:55:44 +0200433 fprintf(target->tgt_file.out, "%s", log);
434 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800435}
436
Harald Welte18fc4652011-08-17 14:14:17 +0200437/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800438struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800439{
Harald Welte3ae27582010-03-26 21:24:24 +0800440 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800441 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800442
Harald Welte3ae27582010-03-26 21:24:24 +0800443 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800444 if (!target)
445 return NULL;
446
Harald Welteb43bc042011-06-27 10:29:17 +0200447 target->categories = talloc_zero_array(target,
448 struct log_category,
449 osmo_log_info->num_cat);
450 if (!target->categories) {
451 talloc_free(target);
452 return NULL;
453 }
454
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800455 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800456
457 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200458 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800459 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200460 cat->enabled = osmo_log_info->cat[i].enabled;
461 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800462 }
463
464 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800465 target->use_color = 1;
466 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800467
468 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800469 target->loglevel = 0;
470 return target;
471}
472
Harald Welte18fc4652011-08-17 14:14:17 +0200473/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800474struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800475{
Harald Weltea3b844c2010-03-27 00:04:40 +0800476/* since C89/C99 says stderr is a macro, we can safely do this! */
477#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800478 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800479
Harald Welte3ae27582010-03-26 21:24:24 +0800480 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800481 if (!target)
482 return NULL;
483
Harald Welte28222962011-02-18 20:37:04 +0100484 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200485 target->tgt_file.out = stderr;
486 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800487 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800488#else
489 return NULL;
490#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800491}
492
Harald Welte18fc4652011-08-17 14:14:17 +0200493/*! \brief Create a new file-based log target
494 * \param[in] fname File name of the new log file
495 * \returns Log target in case of success, NULL otherwise
496 */
Harald Welte3086c392010-08-25 19:10:50 +0200497struct log_target *log_target_create_file(const char *fname)
498{
499 struct log_target *target;
500
501 target = log_target_create();
502 if (!target)
503 return NULL;
504
Harald Welte28222962011-02-18 20:37:04 +0100505 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200506 target->tgt_file.out = fopen(fname, "a");
507 if (!target->tgt_file.out)
508 return NULL;
509
510 target->output = _file_output;
511
512 target->tgt_file.fname = talloc_strdup(target, fname);
513
514 return target;
515}
516
Harald Welte18fc4652011-08-17 14:14:17 +0200517/*! \brief Find a registered log target
518 * \param[in] type Log target type
519 * \param[in] fname File name
520 * \returns Log target (if found), NULL otherwise
521 */
Harald Welte28222962011-02-18 20:37:04 +0100522struct log_target *log_target_find(int type, const char *fname)
523{
524 struct log_target *tgt;
525
526 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
527 if (tgt->type != type)
528 continue;
529 if (tgt->type == LOG_TGT_TYPE_FILE) {
530 if (!strcmp(fname, tgt->tgt_file.fname))
531 return tgt;
532 } else
533 return tgt;
534 }
535 return NULL;
536}
537
Harald Welte18fc4652011-08-17 14:14:17 +0200538/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200539void log_target_destroy(struct log_target *target)
540{
541
542 /* just in case, to make sure we don't have any references */
543 log_del_target(target);
544
545 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200546/* since C89/C99 says stderr is a macro, we can safely do this! */
547#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200548 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200549 if (target->tgt_file.out != stderr)
550#endif
551 {
Harald Welte3086c392010-08-25 19:10:50 +0200552 fclose(target->tgt_file.out);
553 target->tgt_file.out = NULL;
554 }
555 }
556
557 talloc_free(target);
558}
559
Harald Welte18fc4652011-08-17 14:14:17 +0200560/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200561int log_target_file_reopen(struct log_target *target)
562{
563 fclose(target->tgt_file.out);
564
565 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
566 if (!target->tgt_file.out)
567 return -errno;
568
569 /* we assume target->output already to be set */
570
571 return 0;
572}
573
Harald Welte18fc4652011-08-17 14:14:17 +0200574/*! \brief Generates the logging command string for VTY
575 * \param[in] unused_info Deprecated parameter, no longer used!
576 */
Harald Weltece9fec32011-06-27 14:19:16 +0200577const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200578{
Harald Weltece9fec32011-06-27 14:19:16 +0200579 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100580 int len = 0, offset = 0, ret, i, rem;
581 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200582 char *str;
583
Harald Welteb43bc042011-06-27 10:29:17 +0200584 for (i = 0; i < info->num_cat; i++) {
585 if (info->cat[i].name == NULL)
586 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100587 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200588 }
Harald Welte7638af92010-05-11 16:39:22 +0200589
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100590 for (i = 0; i < LOGLEVEL_DEFS; i++)
591 size += strlen(loglevel_strs[i].str) + 1;
592
593 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200594 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200595 if (!str)
596 return NULL;
597
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200598 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100599 if (ret < 0)
600 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200601 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200602
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100603 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200604 if (info->cat[i].name) {
605 int j, name_len = strlen(info->cat[i].name)+1;
606 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100607
Harald Welteb43bc042011-06-27 10:29:17 +0200608 for (j = 0; j < name_len; j++)
609 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100610
Harald Welteb43bc042011-06-27 10:29:17 +0200611 name[name_len-1] = '\0';
612 ret = snprintf(str + offset, rem, "%s|", name+1);
613 if (ret < 0)
614 goto err;
615 OSMO_SNPRINTF_RET(ret, rem, offset, len);
616 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100617 }
618 offset--; /* to remove the trailing | */
619 rem++;
620
621 ret = snprintf(str + offset, rem, ") (");
622 if (ret < 0)
623 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200624 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100625
626 for (i = 0; i < LOGLEVEL_DEFS; i++) {
627 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
628 char loglevel_str[loglevel_str_len];
629
630 for (j = 0; j < loglevel_str_len; j++)
631 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
632
633 loglevel_str[loglevel_str_len-1] = '\0';
634 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
635 if (ret < 0)
636 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200637 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100638 }
639 offset--; /* to remove the trailing | */
640 rem++;
641
642 ret = snprintf(str + offset, rem, ")");
643 if (ret < 0)
644 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200645 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100646err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200647 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100648 return str;
649}
650
Harald Welte18fc4652011-08-17 14:14:17 +0200651/*! \brief Generates the logging command description for VTY
652 * \param[in] unused_info Deprecated parameter, no longer used!
653 */
Harald Weltece9fec32011-06-27 14:19:16 +0200654const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100655{
Harald Weltece9fec32011-06-27 14:19:16 +0200656 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100657 char *str;
658 int i, ret, len = 0, offset = 0, rem;
659 unsigned int size =
660 strlen(LOGGING_STR
661 "Set the log level for a specified category\n") + 1;
662
Harald Welteb43bc042011-06-27 10:29:17 +0200663 for (i = 0; i < info->num_cat; i++) {
664 if (info->cat[i].name == NULL)
665 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100666 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200667 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100668
669 for (i = 0; i < LOGLEVEL_DEFS; i++)
670 size += strlen(loglevel_descriptions[i]) + 1;
671
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200672 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100673 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200674 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100675 if (!str)
676 return NULL;
677
678 ret = snprintf(str + offset, rem, LOGGING_STR
679 "Set the log level for a specified category\n");
680 if (ret < 0)
681 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200682 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100683
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200684 ret = snprintf(str + offset, rem,
685 "Global setting for all subsystems\n");
686 if (ret < 0)
687 goto err;
688 OSMO_SNPRINTF_RET(ret, rem, offset, len);
689
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100690 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200691 if (info->cat[i].name == NULL)
692 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100693 ret = snprintf(str + offset, rem, "%s\n",
694 info->cat[i].description);
695 if (ret < 0)
696 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200697 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100698 }
699 for (i = 0; i < LOGLEVEL_DEFS; i++) {
700 ret = snprintf(str + offset, rem, "%s\n",
701 loglevel_descriptions[i]);
702 if (ret < 0)
703 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200704 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100705 }
706err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200707 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200708 return str;
709}
710
Harald Welte18fc4652011-08-17 14:14:17 +0200711/*! \brief Initialize the Osmocom logging core
712 * \param[in] inf Information regarding logging categories
713 * \param[in] ctx \ref talloc context for logging allocations
714 * \returns 0 in case of success, negative in case of error
715 */
Harald Welteb43bc042011-06-27 10:29:17 +0200716int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800717{
Harald Welteb43bc042011-06-27 10:29:17 +0200718 int i;
719
720 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
721 if (!tall_log_ctx)
722 return -ENOMEM;
723
724 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
725 if (!osmo_log_info)
726 return -ENOMEM;
727
728 osmo_log_info->num_cat_user = inf->num_cat;
729 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200730 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200731
732 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
733 struct log_info_cat,
734 osmo_log_info->num_cat);
735 if (!osmo_log_info->cat) {
736 talloc_free(osmo_log_info);
737 osmo_log_info = NULL;
738 return -ENOMEM;
739 }
740
741 /* copy over the user part */
742 for (i = 0; i < inf->num_cat; i++) {
743 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
744 sizeof(struct log_info_cat));
745 }
746
747 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200748 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200749 unsigned int cn = osmo_log_info->num_cat_user + i;
750 memcpy(&osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200751 &internal_cat[i], sizeof(struct log_info_cat));
752 }
753
754 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800755}
Harald Welte18fc4652011-08-17 14:14:17 +0200756
Sylvain Munautdca7d2c2012-04-18 21:53:23 +0200757/*! @} */