blob: 700733f53317c7df1ac15639d5385beb48fd9a85 [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 Welteb43bc042011-06-27 10:29:17 +0200260static void _logp(int subsys, int level, char *file, int line,
Harald Welte3ae27582010-03-26 21:24:24 +0800261 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800262{
Harald Welte3ae27582010-03-26 21:24:24 +0800263 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800264
Harald Welteb43bc042011-06-27 10:29:17 +0200265 if (subsys < 0)
266 subsys = subsys_lib2index(subsys);
267
268 if (subsys > osmo_log_info->num_cat)
269 subsys = DLGLOBAL;
270
Harald Welte28222962011-02-18 20:37:04 +0100271 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Harald Welte3ae27582010-03-26 21:24:24 +0800272 struct log_category *category;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800273 int output = 0;
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200274 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800275
276 category = &tar->categories[subsys];
Harald Welte3ae27582010-03-26 21:24:24 +0800277 /* subsystem is not supposed to be logged */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800278 if (!category->enabled)
279 continue;
280
281 /* Check the global log level */
282 if (tar->loglevel != 0 && level < tar->loglevel)
283 continue;
284
285 /* Check the category log level */
286 if (tar->loglevel == 0 && category->loglevel != 0 &&
287 level < category->loglevel)
288 continue;
289
290 /* Apply filters here... if that becomes messy we will
291 * need to put filters in a list and each filter will
292 * say stop, continue, output */
Harald Welte3ae27582010-03-26 21:24:24 +0800293 if ((tar->filter_map & LOG_FILTER_ALL) != 0)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800294 output = 1;
Harald Welte4ebdf742010-05-19 19:54:00 +0200295 else if (osmo_log_info->filter_fn)
296 output = osmo_log_info->filter_fn(&log_context,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800297 tar);
Pablo Neira Ayuso81e96362011-05-03 22:32:48 +0200298 if (!output)
299 continue;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800300
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200301 /* According to the manpage, vsnprintf leaves the value of ap
302 * in undefined state. Since _output uses vsnprintf and it may
303 * be called several times, we have to pass a copy of ap. */
304 va_copy(bp, ap);
Harald Welteda127cb2011-07-02 21:51:32 +0200305 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200306 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800307 }
308}
309
Harald Welteb43bc042011-06-27 10:29:17 +0200310void logp(int subsys, char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800311 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800312{
313 va_list ap;
314
315 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800316 _logp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800317 va_end(ap);
318}
319
Harald Welteb43bc042011-06-27 10:29:17 +0200320void logp2(int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800321{
322 va_list ap;
323
324 va_start(ap, format);
Harald Welte3ae27582010-03-26 21:24:24 +0800325 _logp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800326 va_end(ap);
327}
328
Harald Welte18fc4652011-08-17 14:14:17 +0200329/*! \brief Register a new log target with the logging core
330 * \param[in] target Log target to be registered
331 */
Harald Welte3ae27582010-03-26 21:24:24 +0800332void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800333{
Harald Welte28222962011-02-18 20:37:04 +0100334 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800335}
336
Harald Welte18fc4652011-08-17 14:14:17 +0200337/*! \brief Unregister a log target from the logging core
338 * \param[in] target Log target to be unregistered
339 */
Harald Welte3ae27582010-03-26 21:24:24 +0800340void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800341{
342 llist_del(&target->entry);
343}
344
Harald Welte18fc4652011-08-17 14:14:17 +0200345/*! \brief Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800346void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800347{
Harald Welte3ae27582010-03-26 21:24:24 +0800348 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800349}
350
Harald Welte18fc4652011-08-17 14:14:17 +0200351/*! \brief Set the logging context
352 * \param[in] ctx_nr logging context number
353 * \param[in] value value to which the context is to be set
354 *
355 * A logging context is something like the subscriber identity to which
356 * the currently processed message relates, or the BTS through which it
357 * was received. As soon as this data is known, it can be set using
358 * this function. The main use of context information is for logging
359 * filters.
360 */
Harald Welte3ae27582010-03-26 21:24:24 +0800361int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800362{
Harald Welte3ae27582010-03-26 21:24:24 +0800363 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800364 return -EINVAL;
365
Harald Welte3ae27582010-03-26 21:24:24 +0800366 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800367
368 return 0;
369}
370
Harald Welte18fc4652011-08-17 14:14:17 +0200371/*! \brief Enable the \ref LOG_FILTER_ALL log filter
372 * \param[in] target Log target to be affected
373 * \param[in] all enable (1) or disable (0) the ALL filter
374 *
375 * When the \ref LOG_FILTER_ALL filter is enabled, all log messages will
376 * be printed. It acts as a wildcard. Setting it to \a 1 means there
377 * is no filtering.
378 */
Harald Welte3ae27582010-03-26 21:24:24 +0800379void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800380{
381 if (all)
Harald Welte3ae27582010-03-26 21:24:24 +0800382 target->filter_map |= LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800383 else
Harald Welte3ae27582010-03-26 21:24:24 +0800384 target->filter_map &= ~LOG_FILTER_ALL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800385}
386
Harald Welte18fc4652011-08-17 14:14:17 +0200387/*! \brief Enable or disable the use of colored output
388 * \param[in] target Log target to be affected
389 * \param[in] use_color Use color (1) or don't use color (0)
390 */
Harald Welte3ae27582010-03-26 21:24:24 +0800391void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800392{
393 target->use_color = use_color;
394}
395
Harald Welte18fc4652011-08-17 14:14:17 +0200396/*! \brief Enable or disable printing of timestamps while logging
397 * \param[in] target Log target to be affected
398 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
399 */
Harald Welte3ae27582010-03-26 21:24:24 +0800400void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800401{
402 target->print_timestamp = print_timestamp;
403}
404
Harald Welte18fc4652011-08-17 14:14:17 +0200405/*! \brief Set the global log level for a given log target
406 * \param[in] target Log target to be affected
407 * \param[in] log_level New global log level
408 */
Harald Welte3ae27582010-03-26 21:24:24 +0800409void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800410{
411 target->loglevel = log_level;
412}
413
Harald Welte3ae27582010-03-26 21:24:24 +0800414void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800415 int enable, int level)
416{
Harald Welte4ebdf742010-05-19 19:54:00 +0200417 if (category >= osmo_log_info->num_cat)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800418 return;
419 target->categories[category].enabled = !!enable;
420 target->categories[category].loglevel = level;
421}
422
Harald Welte76e72ab2011-02-17 15:52:39 +0100423static void _file_output(struct log_target *target, unsigned int level,
424 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800425{
Harald Welte0083cd32010-08-25 14:55:44 +0200426 fprintf(target->tgt_file.out, "%s", log);
427 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800428}
429
Harald Welte18fc4652011-08-17 14:14:17 +0200430/*! \brief Create a new log target skeleton */
Harald Welte3ae27582010-03-26 21:24:24 +0800431struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800432{
Harald Welte3ae27582010-03-26 21:24:24 +0800433 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800434 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800435
Harald Welte3ae27582010-03-26 21:24:24 +0800436 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800437 if (!target)
438 return NULL;
439
Harald Welteb43bc042011-06-27 10:29:17 +0200440 target->categories = talloc_zero_array(target,
441 struct log_category,
442 osmo_log_info->num_cat);
443 if (!target->categories) {
444 talloc_free(target);
445 return NULL;
446 }
447
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800448 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800449
450 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200451 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800452 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200453 cat->enabled = osmo_log_info->cat[i].enabled;
454 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800455 }
456
457 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800458 target->use_color = 1;
459 target->print_timestamp = 0;
Harald Weltecc6313c2010-03-26 22:04:03 +0800460
461 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800462 target->loglevel = 0;
463 return target;
464}
465
Harald Welte18fc4652011-08-17 14:14:17 +0200466/*! \brief Create the STDERR log target */
Harald Welte3ae27582010-03-26 21:24:24 +0800467struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800468{
Harald Weltea3b844c2010-03-27 00:04:40 +0800469/* since C89/C99 says stderr is a macro, we can safely do this! */
470#ifdef stderr
Harald Welte3ae27582010-03-26 21:24:24 +0800471 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800472
Harald Welte3ae27582010-03-26 21:24:24 +0800473 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800474 if (!target)
475 return NULL;
476
Harald Welte28222962011-02-18 20:37:04 +0100477 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200478 target->tgt_file.out = stderr;
479 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800480 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800481#else
482 return NULL;
483#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800484}
485
Harald Welte18fc4652011-08-17 14:14:17 +0200486/*! \brief Create a new file-based log target
487 * \param[in] fname File name of the new log file
488 * \returns Log target in case of success, NULL otherwise
489 */
Harald Welte3086c392010-08-25 19:10:50 +0200490struct log_target *log_target_create_file(const char *fname)
491{
492 struct log_target *target;
493
494 target = log_target_create();
495 if (!target)
496 return NULL;
497
Harald Welte28222962011-02-18 20:37:04 +0100498 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200499 target->tgt_file.out = fopen(fname, "a");
500 if (!target->tgt_file.out)
501 return NULL;
502
503 target->output = _file_output;
504
505 target->tgt_file.fname = talloc_strdup(target, fname);
506
507 return target;
508}
509
Harald Welte18fc4652011-08-17 14:14:17 +0200510/*! \brief Find a registered log target
511 * \param[in] type Log target type
512 * \param[in] fname File name
513 * \returns Log target (if found), NULL otherwise
514 */
Harald Welte28222962011-02-18 20:37:04 +0100515struct log_target *log_target_find(int type, const char *fname)
516{
517 struct log_target *tgt;
518
519 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
520 if (tgt->type != type)
521 continue;
522 if (tgt->type == LOG_TGT_TYPE_FILE) {
523 if (!strcmp(fname, tgt->tgt_file.fname))
524 return tgt;
525 } else
526 return tgt;
527 }
528 return NULL;
529}
530
Harald Welte18fc4652011-08-17 14:14:17 +0200531/*! \brief Unregister, close and delete a log target */
Harald Welte3086c392010-08-25 19:10:50 +0200532void log_target_destroy(struct log_target *target)
533{
534
535 /* just in case, to make sure we don't have any references */
536 log_del_target(target);
537
538 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200539/* since C89/C99 says stderr is a macro, we can safely do this! */
540#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200541 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200542 if (target->tgt_file.out != stderr)
543#endif
544 {
Harald Welte3086c392010-08-25 19:10:50 +0200545 fclose(target->tgt_file.out);
546 target->tgt_file.out = NULL;
547 }
548 }
549
550 talloc_free(target);
551}
552
Harald Welte18fc4652011-08-17 14:14:17 +0200553/*! \brief close and re-open a log file (for log file rotation) */
Harald Welte3086c392010-08-25 19:10:50 +0200554int log_target_file_reopen(struct log_target *target)
555{
556 fclose(target->tgt_file.out);
557
558 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
559 if (!target->tgt_file.out)
560 return -errno;
561
562 /* we assume target->output already to be set */
563
564 return 0;
565}
566
Harald Welte18fc4652011-08-17 14:14:17 +0200567/*! \brief Generates the logging command string for VTY
568 * \param[in] unused_info Deprecated parameter, no longer used!
569 */
Harald Weltece9fec32011-06-27 14:19:16 +0200570const char *log_vty_command_string(const struct log_info *unused_info)
Harald Welte7638af92010-05-11 16:39:22 +0200571{
Harald Weltece9fec32011-06-27 14:19:16 +0200572 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100573 int len = 0, offset = 0, ret, i, rem;
574 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200575 char *str;
576
Harald Welteb43bc042011-06-27 10:29:17 +0200577 for (i = 0; i < info->num_cat; i++) {
578 if (info->cat[i].name == NULL)
579 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100580 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200581 }
Harald Welte7638af92010-05-11 16:39:22 +0200582
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100583 for (i = 0; i < LOGLEVEL_DEFS; i++)
584 size += strlen(loglevel_strs[i].str) + 1;
585
586 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200587 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200588 if (!str)
589 return NULL;
590
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200591 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100592 if (ret < 0)
593 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200594 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200595
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100596 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200597 if (info->cat[i].name) {
598 int j, name_len = strlen(info->cat[i].name)+1;
599 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100600
Harald Welteb43bc042011-06-27 10:29:17 +0200601 for (j = 0; j < name_len; j++)
602 name[j] = tolower(info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100603
Harald Welteb43bc042011-06-27 10:29:17 +0200604 name[name_len-1] = '\0';
605 ret = snprintf(str + offset, rem, "%s|", name+1);
606 if (ret < 0)
607 goto err;
608 OSMO_SNPRINTF_RET(ret, rem, offset, len);
609 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100610 }
611 offset--; /* to remove the trailing | */
612 rem++;
613
614 ret = snprintf(str + offset, rem, ") (");
615 if (ret < 0)
616 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200617 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100618
619 for (i = 0; i < LOGLEVEL_DEFS; i++) {
620 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
621 char loglevel_str[loglevel_str_len];
622
623 for (j = 0; j < loglevel_str_len; j++)
624 loglevel_str[j] = tolower(loglevel_strs[i].str[j]);
625
626 loglevel_str[loglevel_str_len-1] = '\0';
627 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
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 }
632 offset--; /* to remove the trailing | */
633 rem++;
634
635 ret = snprintf(str + offset, rem, ")");
636 if (ret < 0)
637 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200638 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100639err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200640 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100641 return str;
642}
643
Harald Welte18fc4652011-08-17 14:14:17 +0200644/*! \brief Generates the logging command description for VTY
645 * \param[in] unused_info Deprecated parameter, no longer used!
646 */
Harald Weltece9fec32011-06-27 14:19:16 +0200647const char *log_vty_command_description(const struct log_info *unused_info)
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100648{
Harald Weltece9fec32011-06-27 14:19:16 +0200649 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100650 char *str;
651 int i, ret, len = 0, offset = 0, rem;
652 unsigned int size =
653 strlen(LOGGING_STR
654 "Set the log level for a specified category\n") + 1;
655
Harald Welteb43bc042011-06-27 10:29:17 +0200656 for (i = 0; i < info->num_cat; i++) {
657 if (info->cat[i].name == NULL)
658 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100659 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200660 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100661
662 for (i = 0; i < LOGLEVEL_DEFS; i++)
663 size += strlen(loglevel_descriptions[i]) + 1;
664
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200665 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100666 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200667 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100668 if (!str)
669 return NULL;
670
671 ret = snprintf(str + offset, rem, LOGGING_STR
672 "Set the log level for a specified category\n");
673 if (ret < 0)
674 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200675 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100676
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200677 ret = snprintf(str + offset, rem,
678 "Global setting for all subsystems\n");
679 if (ret < 0)
680 goto err;
681 OSMO_SNPRINTF_RET(ret, rem, offset, len);
682
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100683 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200684 if (info->cat[i].name == NULL)
685 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100686 ret = snprintf(str + offset, rem, "%s\n",
687 info->cat[i].description);
688 if (ret < 0)
689 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200690 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100691 }
692 for (i = 0; i < LOGLEVEL_DEFS; i++) {
693 ret = snprintf(str + offset, rem, "%s\n",
694 loglevel_descriptions[i]);
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 }
699err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200700 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200701 return str;
702}
703
Harald Welte18fc4652011-08-17 14:14:17 +0200704/*! \brief Initialize the Osmocom logging core
705 * \param[in] inf Information regarding logging categories
706 * \param[in] ctx \ref talloc context for logging allocations
707 * \returns 0 in case of success, negative in case of error
708 */
Harald Welteb43bc042011-06-27 10:29:17 +0200709int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800710{
Harald Welteb43bc042011-06-27 10:29:17 +0200711 int i;
712
713 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
714 if (!tall_log_ctx)
715 return -ENOMEM;
716
717 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
718 if (!osmo_log_info)
719 return -ENOMEM;
720
721 osmo_log_info->num_cat_user = inf->num_cat;
722 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200723 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200724
725 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
726 struct log_info_cat,
727 osmo_log_info->num_cat);
728 if (!osmo_log_info->cat) {
729 talloc_free(osmo_log_info);
730 osmo_log_info = NULL;
731 return -ENOMEM;
732 }
733
734 /* copy over the user part */
735 for (i = 0; i < inf->num_cat; i++) {
736 memcpy(&osmo_log_info->cat[i], &inf->cat[i],
737 sizeof(struct log_info_cat));
738 }
739
740 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200741 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +0200742 unsigned int cn = osmo_log_info->num_cat_user + i;
743 memcpy(&osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +0200744 &internal_cat[i], sizeof(struct log_info_cat));
745 }
746
747 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800748}
Harald Welte18fc4652011-08-17 14:14:17 +0200749
750/*! }@ */