blob: 2e913e5f420d5b321389ab6a608adfffe515e471 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file logging.c
2 * Debugging/Logging support code. */
3/*
4 * (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
Harald Welte4a2bb9e2010-03-26 09:33:40 +08005 * (C) 2008 by Holger Hans Peter Freyther <zecke@selfish.org>
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
Harald Welte18fc4652011-08-17 14:14:17 +020024/* \addtogroup logging
25 * @{
Neels Hofmeyr87e45502017-06-20 00:17:59 +020026 * libosmocore Logging sub-system
Neels Hofmeyr17518fe2017-06-20 04:35:06 +020027 *
28 * \file logging.c */
Harald Welte18fc4652011-08-17 14:14:17 +020029
Harald Welte01fd5cb2010-03-26 23:51:31 +080030#include "../config.h"
31
Harald Welte4a2bb9e2010-03-26 09:33:40 +080032#include <stdarg.h>
33#include <stdlib.h>
34#include <stdio.h>
35#include <string.h>
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010036#include <ctype.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080037
38#ifdef HAVE_STRINGS_H
Harald Welte4a2bb9e2010-03-26 09:33:40 +080039#include <strings.h>
Harald Welte01fd5cb2010-03-26 23:51:31 +080040#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +080041#include <time.h>
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +010042#include <sys/time.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080043#include <errno.h>
44
Pablo Neira Ayuso83419342011-03-22 16:36:13 +010045#include <osmocom/core/talloc.h>
46#include <osmocom/core/utils.h>
47#include <osmocom/core/logging.h>
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +020048#include <osmocom/core/timer.h>
Harald Welte4a2bb9e2010-03-26 09:33:40 +080049
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010050#include <osmocom/vty/logging.h> /* for LOGGING_STR. */
51
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010052osmo_static_assert(_LOG_CTX_COUNT <= ARRAY_SIZE(((struct log_context*)NULL)->ctx),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010053 enum_logging_ctx_items_fit_in_struct_log_context);
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010054osmo_static_assert(_LOG_FLT_COUNT <= ARRAY_SIZE(((struct log_target*)NULL)->filter_data),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010055 enum_logging_filters_fit_in_log_target_filter_data);
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +010056osmo_static_assert(_LOG_FLT_COUNT <= 8*sizeof(((struct log_target*)NULL)->filter_map),
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +010057 enum_logging_filters_fit_in_log_target_filter_map);
58
Harald Welteb43bc042011-06-27 10:29:17 +020059struct log_info *osmo_log_info;
Harald Welte4a2bb9e2010-03-26 09:33:40 +080060
Harald Welte3ae27582010-03-26 21:24:24 +080061static struct log_context log_context;
62static void *tall_log_ctx = NULL;
Harald Welte28222962011-02-18 20:37:04 +010063LLIST_HEAD(osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +080064
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +010065#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/
66
67static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = {
Harald Welte9b186702013-03-19 09:55:28 +010068 { 0, "EVERYTHING" },
Harald Welte4a2bb9e2010-03-26 09:33:40 +080069 { LOGL_DEBUG, "DEBUG" },
70 { LOGL_INFO, "INFO" },
71 { LOGL_NOTICE, "NOTICE" },
72 { LOGL_ERROR, "ERROR" },
73 { LOGL_FATAL, "FATAL" },
74 { 0, NULL },
75};
76
Harald Welteb43bc042011-06-27 10:29:17 +020077#define INT2IDX(x) (-1*(x)-1)
78static const struct log_info_cat internal_cat[OSMO_NUM_DLIB] = {
79 [INT2IDX(DLGLOBAL)] = { /* -1 becomes 0 */
80 .name = "DLGLOBAL",
81 .description = "Library-internal global log family",
82 .loglevel = LOGL_NOTICE,
83 .enabled = 1,
84 },
root8a996b42011-09-26 11:22:21 +020085 [INT2IDX(DLLAPD)] = { /* -2 becomes 1 */
86 .name = "DLLAPD",
87 .description = "LAPD in libosmogsm",
Harald Welte1f0b8c22011-06-27 10:51:37 +020088 .loglevel = LOGL_NOTICE,
89 .enabled = 1,
90 },
Harald Welte892e6212011-07-19 14:31:44 +020091 [INT2IDX(DLINP)] = {
Harald Welte087e1132011-07-29 11:43:39 +020092 .name = "DLINP",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020093 .description = "A-bis Intput Subsystem",
94 .loglevel = LOGL_NOTICE,
95 .enabled = 1,
96 },
Harald Welte892e6212011-07-19 14:31:44 +020097 [INT2IDX(DLMUX)] = {
Harald Welte087e1132011-07-29 11:43:39 +020098 .name = "DLMUX",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +020099 .description = "A-bis B-Subchannel TRAU Frame Multiplex",
100 .loglevel = LOGL_NOTICE,
101 .enabled = 1,
102 },
Harald Welte892e6212011-07-19 14:31:44 +0200103 [INT2IDX(DLMI)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200104 .name = "DLMI",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200105 .description = "A-bis Input Driver for Signalling",
106 .enabled = 0, .loglevel = LOGL_NOTICE,
107 },
Harald Welte892e6212011-07-19 14:31:44 +0200108 [INT2IDX(DLMIB)] = {
Harald Welte087e1132011-07-29 11:43:39 +0200109 .name = "DLMIB",
Pablo Neira Ayuso199f3772011-07-07 19:46:38 +0200110 .description = "A-bis Input Driver for B-Channels (voice)",
111 .enabled = 0, .loglevel = LOGL_NOTICE,
112 },
Andreas Eversbergc626da92011-10-28 03:53:50 +0200113 [INT2IDX(DLSMS)] = {
114 .name = "DLSMS",
115 .description = "Layer3 Short Message Service (SMS)",
116 .enabled = 1, .loglevel = LOGL_NOTICE,
117 .color = "\033[1;38m",
118 },
Harald Welte7fd0c832014-08-20 19:58:13 +0200119 [INT2IDX(DLCTRL)] = {
120 .name = "DLCTRL",
121 .description = "Control Interface",
122 .enabled = 1, .loglevel = LOGL_NOTICE,
123 },
Holger Hans Peter Freythera5dc19d2014-12-04 14:35:21 +0100124 [INT2IDX(DLGTP)] = {
125 .name = "DLGTP",
126 .description = "GPRS GTP library",
127 .enabled = 1, .loglevel = LOGL_NOTICE,
128 },
Jacob Erlbeck79125ec2015-11-02 15:17:50 +0100129 [INT2IDX(DLSTATS)] = {
130 .name = "DLSTATS",
131 .description = "Statistics messages and logging",
132 .enabled = 1, .loglevel = LOGL_NOTICE,
133 },
Neels Hofmeyr9795cf12016-12-10 17:01:06 +0100134 [INT2IDX(DLGSUP)] = {
135 .name = "DLGSUP",
136 .description = "Generic Subscriber Update Protocol",
137 .enabled = 1, .loglevel = LOGL_NOTICE,
138 },
Harald Weltec0f00072016-04-27 18:32:35 +0200139 [INT2IDX(DLOAP)] = {
140 .name = "DLOAP",
141 .description = "Osmocom Authentication Protocol",
142 .enabled = 1, .loglevel = LOGL_NOTICE,
143 },
Harald Welte059c4042017-04-03 22:20:49 +0200144 [INT2IDX(DLSS7)] = {
145 .name = "DLSS7",
146 .description = "libosmo-sigtran Signalling System 7",
147 .enabled = 1, .loglevel = LOGL_NOTICE,
148 },
149 [INT2IDX(DLSCCP)] = {
150 .name = "DLSCCP",
151 .description = "libosmo-sigtran SCCP Implementation",
152 .enabled = 1, .loglevel = LOGL_NOTICE,
153 },
154 [INT2IDX(DLSUA)] = {
155 .name = "DLSUA",
156 .description = "libosmo-sigtran SCCP User Adaptation",
157 .enabled = 1, .loglevel = LOGL_NOTICE,
158 },
159 [INT2IDX(DLM3UA)] = {
160 .name = "DLM3UA",
161 .description = "libosmo-sigtran MTP3 User Adaptation",
162 .enabled = 1, .loglevel = LOGL_NOTICE,
163 },
Neels Hofmeyra7ccf612017-07-11 18:43:09 +0200164 [INT2IDX(DLMGCP)] = {
165 .name = "DLMGCP",
166 .description = "libosmo-mgcp Media Gateway Control Protocol",
167 .enabled = 1, .loglevel = LOGL_NOTICE,
168 },
Harald Welteb43bc042011-06-27 10:29:17 +0200169};
170
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200171/*! descriptive string for each log level */
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100172/* You have to keep this in sync with the structure loglevel_strs. */
Maxc65c5b42017-03-15 13:20:23 +0100173static const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = {
Holger Hans Peter Freyther6ec6bd92014-12-28 18:27:38 +0100174 "Don't use. It doesn't log anything",
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100175 "Log debug messages and higher levels",
176 "Log informational messages and higher levels",
Ruben Undheim029f5a12015-09-16 19:02:35 +0200177 "Log noticeable messages and higher levels",
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100178 "Log error messages and higher levels",
179 "Log only fatal messages",
180 NULL,
181};
182
Harald Welte18a7d812017-03-16 23:54:55 +0100183static void assert_loginfo(void)
184{
185 if (!osmo_log_info) {
186 fprintf(stderr, "ERROR: osmo_log_info == NULL! "
187 "You must call log_init() before using logging!\n");
188 OSMO_ASSERT(osmo_log_info);
189 }
190}
191
Harald Welteb43bc042011-06-27 10:29:17 +0200192/* special magic for negative (library-internal) log subsystem numbers */
193static int subsys_lib2index(int subsys)
194{
195 return (subsys * -1) + (osmo_log_info->num_cat_user-1);
196}
197
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200198/*! Parse a human-readable log level into a numeric value
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200199 * \param lvl[in] zero-terminated string containing log level name
200 * \returns numeric log level
201 */
Harald Welte3ae27582010-03-26 21:24:24 +0800202int log_parse_level(const char *lvl)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800203{
204 return get_string_value(loglevel_strs, lvl);
205}
206
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200207/*! convert a numeric log level into human-readable string
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200208 * \param lvl[in] numeric log level
209 * \returns zero-terminated string (log level name)
210 */
Harald Welte9ac22252010-05-11 11:19:40 +0200211const char *log_level_str(unsigned int lvl)
212{
213 return get_value_string(loglevel_strs, lvl);
214}
215
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200216/*! parse a human-readable log category into numeric form
Harald Welte18fc4652011-08-17 14:14:17 +0200217 * \param[in] category human-readable log category name
218 * \returns numeric category value, or -EINVAL otherwise
219 */
Harald Welte3ae27582010-03-26 21:24:24 +0800220int log_parse_category(const char *category)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800221{
222 int i;
223
Harald Welte18a7d812017-03-16 23:54:55 +0100224 assert_loginfo();
225
Harald Welte4ebdf742010-05-19 19:54:00 +0200226 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Harald Welteb43bc042011-06-27 10:29:17 +0200227 if (osmo_log_info->cat[i].name == NULL)
228 continue;
Harald Welte4ebdf742010-05-19 19:54:00 +0200229 if (!strcasecmp(osmo_log_info->cat[i].name+1, category))
Harald Weltefaadfe22010-03-26 21:05:43 +0800230 return i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800231 }
232
233 return -EINVAL;
234}
235
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200236/*! parse the log category mask
Harald Welte18fc4652011-08-17 14:14:17 +0200237 * \param[in] target log target to be configured
238 * \param[in] _mask log category mask string
239 *
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800240 * The format can be this: category1:category2:category3
241 * or category1,2:category2,3:...
242 */
Harald Welte3ae27582010-03-26 21:24:24 +0800243void log_parse_category_mask(struct log_target* target, const char *_mask)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800244{
245 int i = 0;
246 char *mask = strdup(_mask);
247 char *category_token = NULL;
248
Harald Welte18a7d812017-03-16 23:54:55 +0100249 assert_loginfo();
250
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800251 /* Disable everything to enable it afterwards */
Harald Welteb43bc042011-06-27 10:29:17 +0200252 for (i = 0; i < osmo_log_info->num_cat; ++i)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800253 target->categories[i].enabled = 0;
254
255 category_token = strtok(mask, ":");
Neels Hofmeyrda1b20c2016-04-14 15:12:16 +0200256 OSMO_ASSERT(category_token);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800257 do {
Harald Welte4ebdf742010-05-19 19:54:00 +0200258 for (i = 0; i < osmo_log_info->num_cat; ++i) {
Nico Golde0262d3f2012-09-21 17:44:58 +0200259 size_t length, cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800260 char* colon = strstr(category_token, ",");
Nico Golde0262d3f2012-09-21 17:44:58 +0200261
262 if (!osmo_log_info->cat[i].name)
263 continue;
264
265 length = strlen(category_token);
266 cat_length = strlen(osmo_log_info->cat[i].name);
Pablo Neira Ayuso300e78d2011-08-11 13:24:18 +0200267
268 /* Use longest length not to match subocurrences. */
269 if (cat_length > length)
270 length = cat_length;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800271
272 if (colon)
273 length = colon - category_token;
274
Harald Welte4ebdf742010-05-19 19:54:00 +0200275 if (strncasecmp(osmo_log_info->cat[i].name,
276 category_token, length) == 0) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800277 int level = 0;
278
279 if (colon)
280 level = atoi(colon+1);
281
Harald Weltefaadfe22010-03-26 21:05:43 +0800282 target->categories[i].enabled = 1;
283 target->categories[i].loglevel = level;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800284 }
285 }
286 } while ((category_token = strtok(NULL, ":")));
287
288 free(mask);
289}
290
291static const char* color(int subsys)
292{
Harald Welte4ebdf742010-05-19 19:54:00 +0200293 if (subsys < osmo_log_info->num_cat)
294 return osmo_log_info->cat[subsys].color;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800295
Harald Welted788f662010-03-26 09:45:03 +0800296 return NULL;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800297}
298
Harald Welteaa00f992016-12-02 15:30:02 +0100299const char* log_category_name(int subsys)
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100300{
301 if (subsys < osmo_log_info->num_cat)
302 return osmo_log_info->cat[subsys].name;
303
304 return NULL;
305}
306
Harald Welte3ae27582010-03-26 21:24:24 +0800307static void _output(struct log_target *target, unsigned int subsys,
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200308 unsigned int level, const char *file, int line, int cont,
Harald Welte76e72ab2011-02-17 15:52:39 +0100309 const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800310{
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800311 char buf[4096];
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200312 int ret, len = 0, offset = 0, rem = sizeof(buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800313
314 /* are we using color */
315 if (target->use_color) {
Harald Welted788f662010-03-26 09:45:03 +0800316 const char *c = color(subsys);
317 if (c) {
Holger Hans Peter Freyther5f91a402014-12-05 10:29:45 +0100318 ret = snprintf(buf + offset, rem, "%s", c);
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200319 if (ret < 0)
320 goto err;
321 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welted788f662010-03-26 09:45:03 +0800322 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800323 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800324 if (!cont) {
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100325 if (target->print_ext_timestamp) {
326 struct tm tm;
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100327 struct timeval tv;
Neels Hofmeyr8e2f7e82016-09-22 03:58:13 +0200328 osmo_gettimeofday(&tv, NULL);
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100329 localtime_r(&tv.tv_sec, &tm);
330 ret = snprintf(buf + offset, rem, "%04d%02d%02d%02d%02d%02d%03d ",
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100331 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
Jacob Erlbeckb61b2ca2015-03-17 10:21:15 +0100332 tm.tm_hour, tm.tm_min, tm.tm_sec,
333 (int)(tv.tv_usec / 1000));
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100334 if (ret < 0)
335 goto err;
336 OSMO_SNPRINTF_RET(ret, rem, offset, len);
337 } else if (target->print_timestamp) {
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800338 char *timestr;
339 time_t tm;
340 tm = time(NULL);
341 timestr = ctime(&tm);
342 timestr[strlen(timestr)-1] = '\0';
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200343 ret = snprintf(buf + offset, rem, "%s ", timestr);
344 if (ret < 0)
345 goto err;
346 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800347 }
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100348 if (target->print_category) {
Harald Welteaa00f992016-12-02 15:30:02 +0100349 ret = snprintf(buf + offset, rem, "%s ", log_category_name(subsys));
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100350 if (ret < 0)
351 goto err;
352 OSMO_SNPRINTF_RET(ret, rem, offset, len);
353 }
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200354 if (target->print_filename) {
355 ret = snprintf(buf + offset, rem, "<%4.4x> %s:%d ",
356 subsys, file, line);
357 if (ret < 0)
358 goto err;
359 OSMO_SNPRINTF_RET(ret, rem, offset, len);
360 }
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800361 }
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200362 ret = vsnprintf(buf + offset, rem, format, ap);
363 if (ret < 0)
364 goto err;
365 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800366
Pablo Neira Ayuso7503fb82011-05-03 22:32:43 +0200367 ret = snprintf(buf + offset, rem, "%s",
368 target->use_color ? "\033[0;m" : "");
369 if (ret < 0)
370 goto err;
371 OSMO_SNPRINTF_RET(ret, rem, offset, len);
372err:
373 buf[sizeof(buf)-1] = '\0';
374 target->output(target, level, buf);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800375}
376
Neels Hofmeyr42240de2016-12-12 15:13:56 +0100377/* Catch internal logging category indexes as well as out-of-bounds indexes.
378 * For internal categories, the ID is negative starting with -1; and internal
379 * logging categories are added behind the user categories. For out-of-bounds
380 * indexes, return the index of DLGLOBAL. The returned category index is
381 * guaranteed to exist in osmo_log_info, otherwise the program would abort,
382 * which should never happen unless even the DLGLOBAL category is missing. */
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100383static inline int map_subsys(int subsys)
384{
Neels Hofmeyr74802262016-12-12 16:00:24 +0100385 /* Note: comparing signed and unsigned integers */
386
387 if (subsys > 0 && ((unsigned int)subsys) >= osmo_log_info->num_cat_user)
388 subsys = DLGLOBAL;
389
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100390 if (subsys < 0)
391 subsys = subsys_lib2index(subsys);
392
Neels Hofmeyrca135742016-12-12 14:18:54 +0100393 if (subsys < 0 || subsys >= osmo_log_info->num_cat)
Neels Hofmeyr42240de2016-12-12 15:13:56 +0100394 subsys = subsys_lib2index(DLGLOBAL);
395
Neels Hofmeyrca135742016-12-12 14:18:54 +0100396 OSMO_ASSERT(!(subsys < 0 || subsys >= osmo_log_info->num_cat));
397
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100398 return subsys;
399}
400
Maxc65c5b42017-03-15 13:20:23 +0100401static inline bool should_log_to_target(struct log_target *tar, int subsys,
402 int level)
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100403{
404 struct log_category *category;
405
406 category = &tar->categories[subsys];
407
408 /* subsystem is not supposed to be logged */
409 if (!category->enabled)
Maxc65c5b42017-03-15 13:20:23 +0100410 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100411
412 /* Check the global log level */
413 if (tar->loglevel != 0 && level < tar->loglevel)
Maxc65c5b42017-03-15 13:20:23 +0100414 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100415
416 /* Check the category log level */
417 if (tar->loglevel == 0 && category->loglevel != 0 &&
418 level < category->loglevel)
Maxc65c5b42017-03-15 13:20:23 +0100419 return false;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100420
Holger Hans Peter Freyther79599ac2016-01-15 16:49:06 +0100421 /* Apply filters here... if that becomes messy we will
422 * need to put filters in a list and each filter will
423 * say stop, continue, output */
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100424 if ((tar->filter_map & (1 << LOG_FLT_ALL)) != 0)
Maxc65c5b42017-03-15 13:20:23 +0100425 return true;
Holger Hans Peter Freyther79599ac2016-01-15 16:49:06 +0100426
427 if (osmo_log_info->filter_fn)
428 return osmo_log_info->filter_fn(&log_context, tar);
429
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100430 /* TODO: Check the filter/selector too? */
Maxc65c5b42017-03-15 13:20:23 +0100431 return true;
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100432}
433
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200434/*! vararg version of logging function
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200435 * \param[in] subsys Logging sub-system
436 * \param[in] level Log level
437 * \param[in] file name of source code file
438 * \param[in] cont continuation (1) or new line (0)
439 * \param[in] format format string
440 * \param[in] ap vararg-list containing format string arguments
441 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200442void osmo_vlogp(int subsys, int level, const char *file, int line,
Harald Welte36c5a3e2011-08-27 14:33:19 +0200443 int cont, const char *format, va_list ap)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800444{
Harald Welte3ae27582010-03-26 21:24:24 +0800445 struct log_target *tar;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800446
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +0100447 subsys = map_subsys(subsys);
Harald Welteb43bc042011-06-27 10:29:17 +0200448
Harald Welte28222962011-02-18 20:37:04 +0100449 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200450 va_list bp;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800451
Maxc65c5b42017-03-15 13:20:23 +0100452 if (!should_log_to_target(tar, subsys, level))
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800453 continue;
454
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200455 /* According to the manpage, vsnprintf leaves the value of ap
456 * in undefined state. Since _output uses vsnprintf and it may
457 * be called several times, we have to pass a copy of ap. */
458 va_copy(bp, ap);
Harald Welted7c0a372016-12-02 13:52:59 +0100459 if (tar->raw_output)
460 tar->raw_output(tar, subsys, level, file, line, cont, format, bp);
461 else
462 _output(tar, subsys, level, file, line, cont, format, bp);
Pablo Neira Ayusodd93bf42011-05-19 01:40:43 +0200463 va_end(bp);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800464 }
465}
466
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200467/*! logging function used by DEBUGP() macro
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200468 * \param[in] subsys Logging sub-system
469 * \param[in] file name of source code file
470 * \param[in] cont continuation (1) or new line (0)
471 * \param[in] format format string
472 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200473void logp(int subsys, const char *file, int line, int cont,
Harald Welte3ae27582010-03-26 21:24:24 +0800474 const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800475{
476 va_list ap;
477
478 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200479 osmo_vlogp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800480 va_end(ap);
481}
482
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200483/*! logging function used by LOGP() macro
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200484 * \param[in] subsys Logging sub-system
485 * \param[in] level Log level
486 * \param[in] file name of source code file
487 * \param[in] cont continuation (1) or new line (0)
488 * \param[in] format format string
489 */
Holger Hans Peter Freytherfb4bfc22012-07-12 09:26:25 +0200490void logp2(int subsys, unsigned int level, const char *file, int line, int cont, const char *format, ...)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800491{
492 va_list ap;
493
494 va_start(ap, format);
Harald Welte36c5a3e2011-08-27 14:33:19 +0200495 osmo_vlogp(subsys, level, file, line, cont, format, ap);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800496 va_end(ap);
497}
498
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200499/*! Register a new log target with the logging core
Harald Welte18fc4652011-08-17 14:14:17 +0200500 * \param[in] target Log target to be registered
501 */
Harald Welte3ae27582010-03-26 21:24:24 +0800502void log_add_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800503{
Harald Welte28222962011-02-18 20:37:04 +0100504 llist_add_tail(&target->entry, &osmo_log_target_list);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800505}
506
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200507/*! Unregister a log target from the logging core
Harald Welte18fc4652011-08-17 14:14:17 +0200508 * \param[in] target Log target to be unregistered
509 */
Harald Welte3ae27582010-03-26 21:24:24 +0800510void log_del_target(struct log_target *target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800511{
512 llist_del(&target->entry);
513}
514
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200515/*! Reset (clear) the logging context */
Harald Welte3ae27582010-03-26 21:24:24 +0800516void log_reset_context(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800517{
Harald Welte3ae27582010-03-26 21:24:24 +0800518 memset(&log_context, 0, sizeof(log_context));
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800519}
520
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200521/*! Set the logging context
Harald Welte18fc4652011-08-17 14:14:17 +0200522 * \param[in] ctx_nr logging context number
523 * \param[in] value value to which the context is to be set
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200524 * \returns 0 in case of success; negative otherwise
Harald Welte18fc4652011-08-17 14:14:17 +0200525 *
526 * A logging context is something like the subscriber identity to which
527 * the currently processed message relates, or the BTS through which it
528 * was received. As soon as this data is known, it can be set using
529 * this function. The main use of context information is for logging
530 * filters.
531 */
Harald Welte3ae27582010-03-26 21:24:24 +0800532int log_set_context(uint8_t ctx_nr, void *value)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800533{
Harald Welte3ae27582010-03-26 21:24:24 +0800534 if (ctx_nr > LOG_MAX_CTX)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800535 return -EINVAL;
536
Harald Welte3ae27582010-03-26 21:24:24 +0800537 log_context.ctx[ctx_nr] = value;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800538
539 return 0;
540}
541
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200542/*! Enable the \ref LOG_FLT_ALL log filter
Harald Welte18fc4652011-08-17 14:14:17 +0200543 * \param[in] target Log target to be affected
544 * \param[in] all enable (1) or disable (0) the ALL filter
545 *
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100546 * When the \ref LOG_FLT_ALL filter is enabled, all log messages will be
Neels Hofmeyr812ba6d2017-02-17 16:35:27 +0100547 * printed. It acts as a wildcard. Setting it to \a 1 means there is no
548 * filtering.
Harald Welte18fc4652011-08-17 14:14:17 +0200549 */
Harald Welte3ae27582010-03-26 21:24:24 +0800550void log_set_all_filter(struct log_target *target, int all)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800551{
552 if (all)
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100553 target->filter_map |= (1 << LOG_FLT_ALL);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800554 else
Neels Hofmeyr8b86cd72017-02-23 18:03:28 +0100555 target->filter_map &= ~(1 << LOG_FLT_ALL);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800556}
557
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200558/*! Enable or disable the use of colored output
Harald Welte18fc4652011-08-17 14:14:17 +0200559 * \param[in] target Log target to be affected
560 * \param[in] use_color Use color (1) or don't use color (0)
561 */
Harald Welte3ae27582010-03-26 21:24:24 +0800562void log_set_use_color(struct log_target *target, int use_color)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800563{
564 target->use_color = use_color;
565}
566
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200567/*! Enable or disable printing of timestamps while logging
Harald Welte18fc4652011-08-17 14:14:17 +0200568 * \param[in] target Log target to be affected
569 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
570 */
Harald Welte3ae27582010-03-26 21:24:24 +0800571void log_set_print_timestamp(struct log_target *target, int print_timestamp)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800572{
573 target->print_timestamp = print_timestamp;
574}
575
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200576/*! Enable or disable printing of extended timestamps while logging
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100577 * \param[in] target Log target to be affected
578 * \param[in] print_timestamp Enable (1) or disable (0) timestamps
579 *
580 * When both timestamp and extended timestamp is enabled then only
581 * the extended timestamp will be used. The format of the timestamp
582 * is YYYYMMDDhhmmssnnn.
583 */
584void log_set_print_extended_timestamp(struct log_target *target, int print_timestamp)
585{
586 target->print_ext_timestamp = print_timestamp;
587}
588
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200589/*! Enable or disable printing of the filename while logging
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200590 * \param[in] target Log target to be affected
591 * \param[in] print_filename Enable (1) or disable (0) filenames
592 */
593void log_set_print_filename(struct log_target *target, int print_filename)
594{
595 target->print_filename = print_filename;
596}
597
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200598/*! Enable or disable printing of the category name
Holger Hans Peter Freyther2d6ad132014-12-05 09:35:30 +0100599 * \param[in] target Log target to be affected
600 * \param[in] print_catname Enable (1) or disable (0) filenames
601 *
602 * Print the category/subsys name in front of every log message.
603 */
604void log_set_print_category(struct log_target *target, int print_category)
605{
606 target->print_category = print_category;
607}
608
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200609/*! Set the global log level for a given log target
Harald Welte18fc4652011-08-17 14:14:17 +0200610 * \param[in] target Log target to be affected
611 * \param[in] log_level New global log level
612 */
Harald Welte3ae27582010-03-26 21:24:24 +0800613void log_set_log_level(struct log_target *target, int log_level)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800614{
615 target->loglevel = log_level;
616}
617
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200618/*! Set a category filter on a given log target
Harald Weltede6e4982012-12-06 21:25:27 +0100619 * \param[in] target Log target to be affected
620 * \param[in] category Log category to be affected
621 * \param[in] enable whether to enable or disable the filter
622 * \param[in] level Log level of the filter
623 */
Harald Welte3ae27582010-03-26 21:24:24 +0800624void log_set_category_filter(struct log_target *target, int category,
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800625 int enable, int level)
626{
Neels Hofmeyr886d6fd2016-12-12 13:49:03 +0100627 if (!target)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800628 return;
Neels Hofmeyr886d6fd2016-12-12 13:49:03 +0100629 category = map_subsys(category);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800630 target->categories[category].enabled = !!enable;
631 target->categories[category].loglevel = level;
632}
633
Harald Welte44c0f632017-01-15 17:58:29 +0100634#if (!EMBEDDED)
Harald Welte76e72ab2011-02-17 15:52:39 +0100635static void _file_output(struct log_target *target, unsigned int level,
636 const char *log)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800637{
Harald Welte0083cd32010-08-25 14:55:44 +0200638 fprintf(target->tgt_file.out, "%s", log);
639 fflush(target->tgt_file.out);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800640}
Harald Welte44c0f632017-01-15 17:58:29 +0100641#endif
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800642
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200643/*! Create a new log target skeleton
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200644 * \returns dynamically-allocated log target
645 * This funcition allocates a \ref log_target and initializes it
646 * with some default values. The newly created target is not
647 * registered yet.
648 */
Harald Welte3ae27582010-03-26 21:24:24 +0800649struct log_target *log_target_create(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800650{
Harald Welte3ae27582010-03-26 21:24:24 +0800651 struct log_target *target;
Harald Weltecc6313c2010-03-26 22:04:03 +0800652 unsigned int i;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800653
Harald Welte18a7d812017-03-16 23:54:55 +0100654 assert_loginfo();
655
Harald Welte3ae27582010-03-26 21:24:24 +0800656 target = talloc_zero(tall_log_ctx, struct log_target);
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800657 if (!target)
658 return NULL;
659
Harald Welteb43bc042011-06-27 10:29:17 +0200660 target->categories = talloc_zero_array(target,
661 struct log_category,
662 osmo_log_info->num_cat);
663 if (!target->categories) {
664 talloc_free(target);
665 return NULL;
666 }
667
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800668 INIT_LLIST_HEAD(&target->entry);
Harald Weltecc6313c2010-03-26 22:04:03 +0800669
670 /* initialize the per-category enabled/loglevel from defaults */
Harald Welte4ebdf742010-05-19 19:54:00 +0200671 for (i = 0; i < osmo_log_info->num_cat; i++) {
Harald Weltecc6313c2010-03-26 22:04:03 +0800672 struct log_category *cat = &target->categories[i];
Harald Welte4ebdf742010-05-19 19:54:00 +0200673 cat->enabled = osmo_log_info->cat[i].enabled;
674 cat->loglevel = osmo_log_info->cat[i].loglevel;
Harald Weltecc6313c2010-03-26 22:04:03 +0800675 }
676
677 /* global settings */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800678 target->use_color = 1;
679 target->print_timestamp = 0;
Holger Hans Peter Freytherdb153362012-09-11 11:24:51 +0200680 target->print_filename = 1;
Harald Weltecc6313c2010-03-26 22:04:03 +0800681
682 /* global log level */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800683 target->loglevel = 0;
684 return target;
685}
686
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200687/*! Create the STDERR log target
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200688 * \returns dynamically-allocated \ref log_target for STDERR */
Harald Welte3ae27582010-03-26 21:24:24 +0800689struct log_target *log_target_create_stderr(void)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800690{
Harald Weltea3b844c2010-03-27 00:04:40 +0800691/* since C89/C99 says stderr is a macro, we can safely do this! */
Harald Welteb93ce5a2017-05-15 10:58:15 +0200692#if !EMBEDDED && defined(stderr)
Harald Welte3ae27582010-03-26 21:24:24 +0800693 struct log_target *target;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800694
Harald Welte3ae27582010-03-26 21:24:24 +0800695 target = log_target_create();
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800696 if (!target)
697 return NULL;
698
Harald Welte28222962011-02-18 20:37:04 +0100699 target->type = LOG_TGT_TYPE_STDERR;
Harald Welte0083cd32010-08-25 14:55:44 +0200700 target->tgt_file.out = stderr;
701 target->output = _file_output;
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800702 return target;
Harald Weltea3b844c2010-03-27 00:04:40 +0800703#else
704 return NULL;
705#endif /* stderr */
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800706}
707
Harald Welte44c0f632017-01-15 17:58:29 +0100708#if (!EMBEDDED)
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200709/*! Create a new file-based log target
Harald Welte18fc4652011-08-17 14:14:17 +0200710 * \param[in] fname File name of the new log file
711 * \returns Log target in case of success, NULL otherwise
712 */
Harald Welte3086c392010-08-25 19:10:50 +0200713struct log_target *log_target_create_file(const char *fname)
714{
715 struct log_target *target;
716
717 target = log_target_create();
718 if (!target)
719 return NULL;
720
Harald Welte28222962011-02-18 20:37:04 +0100721 target->type = LOG_TGT_TYPE_FILE;
Harald Welte3086c392010-08-25 19:10:50 +0200722 target->tgt_file.out = fopen(fname, "a");
723 if (!target->tgt_file.out)
724 return NULL;
725
726 target->output = _file_output;
727
728 target->tgt_file.fname = talloc_strdup(target, fname);
729
730 return target;
731}
Harald Welte44c0f632017-01-15 17:58:29 +0100732#endif
Harald Welte3086c392010-08-25 19:10:50 +0200733
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200734/*! Find a registered log target
Harald Welte18fc4652011-08-17 14:14:17 +0200735 * \param[in] type Log target type
736 * \param[in] fname File name
737 * \returns Log target (if found), NULL otherwise
738 */
Harald Welte28222962011-02-18 20:37:04 +0100739struct log_target *log_target_find(int type, const char *fname)
740{
741 struct log_target *tgt;
742
743 llist_for_each_entry(tgt, &osmo_log_target_list, entry) {
744 if (tgt->type != type)
745 continue;
746 if (tgt->type == LOG_TGT_TYPE_FILE) {
747 if (!strcmp(fname, tgt->tgt_file.fname))
748 return tgt;
749 } else
750 return tgt;
751 }
752 return NULL;
753}
754
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200755/*! Unregister, close and delete a log target
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200756 * \param target[in] log target to unregister, close and delete */
Harald Welte3086c392010-08-25 19:10:50 +0200757void log_target_destroy(struct log_target *target)
758{
759
760 /* just in case, to make sure we don't have any references */
761 log_del_target(target);
762
Harald Welte44c0f632017-01-15 17:58:29 +0100763#if (!EMBEDDED)
Harald Welte3086c392010-08-25 19:10:50 +0200764 if (target->output == &_file_output) {
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200765/* since C89/C99 says stderr is a macro, we can safely do this! */
766#ifdef stderr
Harald Welte3086c392010-08-25 19:10:50 +0200767 /* don't close stderr */
Sylvain Munautaf5ee342010-09-17 14:38:17 +0200768 if (target->tgt_file.out != stderr)
769#endif
770 {
Harald Welte3086c392010-08-25 19:10:50 +0200771 fclose(target->tgt_file.out);
772 target->tgt_file.out = NULL;
773 }
774 }
Harald Welte44c0f632017-01-15 17:58:29 +0100775#endif
Harald Welte3086c392010-08-25 19:10:50 +0200776
777 talloc_free(target);
778}
779
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200780/*! close and re-open a log file (for log file rotation)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200781 * \param[in] target log target to re-open
782 * \returns 0 in case of success; negative otherwise */
Harald Welte3086c392010-08-25 19:10:50 +0200783int log_target_file_reopen(struct log_target *target)
784{
785 fclose(target->tgt_file.out);
786
787 target->tgt_file.out = fopen(target->tgt_file.fname, "a");
788 if (!target->tgt_file.out)
789 return -errno;
790
791 /* we assume target->output already to be set */
792
793 return 0;
794}
795
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200796/*! close and re-open all log files (for log file rotation)
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200797 * \returns 0 in case of success; negative otherwise */
Harald Welte4de854d2013-03-18 19:01:40 +0100798int log_targets_reopen(void)
799{
800 struct log_target *tar;
801 int rc = 0;
802
803 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
804 switch (tar->type) {
805 case LOG_TGT_TYPE_FILE:
806 if (log_target_file_reopen(tar) < 0)
807 rc = -1;
808 break;
809 default:
810 break;
811 }
812 }
813
814 return rc;
815}
816
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200817/*! Generates the logging command string for VTY
Harald Welte18fc4652011-08-17 14:14:17 +0200818 * \param[in] unused_info Deprecated parameter, no longer used!
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200819 * \returns vty command string for use by VTY command node
Harald Welte18fc4652011-08-17 14:14:17 +0200820 */
Maxc65c5b42017-03-15 13:20:23 +0100821const char *log_vty_command_string()
Harald Welte7638af92010-05-11 16:39:22 +0200822{
Harald Weltece9fec32011-06-27 14:19:16 +0200823 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100824 int len = 0, offset = 0, ret, i, rem;
825 int size = strlen("logging level () ()") + 1;
Harald Welte7638af92010-05-11 16:39:22 +0200826 char *str;
827
Harald Welte18a7d812017-03-16 23:54:55 +0100828 assert_loginfo();
829
Harald Welteb43bc042011-06-27 10:29:17 +0200830 for (i = 0; i < info->num_cat; i++) {
831 if (info->cat[i].name == NULL)
832 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100833 size += strlen(info->cat[i].name) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200834 }
Harald Welte7638af92010-05-11 16:39:22 +0200835
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100836 for (i = 0; i < LOGLEVEL_DEFS; i++)
837 size += strlen(loglevel_strs[i].str) + 1;
838
839 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200840 str = talloc_zero_size(tall_log_ctx, size);
Harald Welte7638af92010-05-11 16:39:22 +0200841 if (!str)
842 return NULL;
843
Holger Hans Peter Freyther952a18e2011-03-29 17:03:56 +0200844 ret = snprintf(str + offset, rem, "logging level (all|");
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100845 if (ret < 0)
846 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200847 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Harald Welte7638af92010-05-11 16:39:22 +0200848
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100849 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200850 if (info->cat[i].name) {
851 int j, name_len = strlen(info->cat[i].name)+1;
852 char name[name_len];
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100853
Harald Welteb43bc042011-06-27 10:29:17 +0200854 for (j = 0; j < name_len; j++)
Pau Espin Pedrol399a6f02017-06-18 14:07:37 +0200855 name[j] = tolower((unsigned char)info->cat[i].name[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100856
Harald Welteb43bc042011-06-27 10:29:17 +0200857 name[name_len-1] = '\0';
858 ret = snprintf(str + offset, rem, "%s|", name+1);
859 if (ret < 0)
860 goto err;
861 OSMO_SNPRINTF_RET(ret, rem, offset, len);
862 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100863 }
864 offset--; /* to remove the trailing | */
865 rem++;
866
867 ret = snprintf(str + offset, rem, ") (");
868 if (ret < 0)
869 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200870 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100871
872 for (i = 0; i < LOGLEVEL_DEFS; i++) {
873 int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1;
874 char loglevel_str[loglevel_str_len];
875
876 for (j = 0; j < loglevel_str_len; j++)
Pau Espin Pedrol399a6f02017-06-18 14:07:37 +0200877 loglevel_str[j] = tolower((unsigned char)loglevel_strs[i].str[j]);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100878
879 loglevel_str[loglevel_str_len-1] = '\0';
880 ret = snprintf(str + offset, rem, "%s|", loglevel_str);
881 if (ret < 0)
882 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200883 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100884 }
885 offset--; /* to remove the trailing | */
886 rem++;
887
888 ret = snprintf(str + offset, rem, ")");
889 if (ret < 0)
890 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200891 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100892err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200893 str[size-1] = '\0';
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100894 return str;
895}
896
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200897/*! Generates the logging command description for VTY
Harald Welte18fc4652011-08-17 14:14:17 +0200898 * \param[in] unused_info Deprecated parameter, no longer used!
Harald Welte2d2e2cc2016-04-25 12:11:20 +0200899 * \returns logging command description for use by VTY command node
Harald Welte18fc4652011-08-17 14:14:17 +0200900 */
Maxc65c5b42017-03-15 13:20:23 +0100901const char *log_vty_command_description()
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100902{
Harald Weltece9fec32011-06-27 14:19:16 +0200903 struct log_info *info = osmo_log_info;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100904 char *str;
905 int i, ret, len = 0, offset = 0, rem;
906 unsigned int size =
907 strlen(LOGGING_STR
908 "Set the log level for a specified category\n") + 1;
909
Harald Welte18a7d812017-03-16 23:54:55 +0100910 assert_loginfo();
911
Harald Welteb43bc042011-06-27 10:29:17 +0200912 for (i = 0; i < info->num_cat; i++) {
913 if (info->cat[i].name == NULL)
914 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100915 size += strlen(info->cat[i].description) + 1;
Harald Welteb43bc042011-06-27 10:29:17 +0200916 }
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100917
918 for (i = 0; i < LOGLEVEL_DEFS; i++)
919 size += strlen(loglevel_descriptions[i]) + 1;
920
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200921 size += strlen("Global setting for all subsystems") + 1;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100922 rem = size;
Pablo Neira Ayusof1fae4d2011-05-03 22:32:42 +0200923 str = talloc_zero_size(tall_log_ctx, size);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100924 if (!str)
925 return NULL;
926
927 ret = snprintf(str + offset, rem, LOGGING_STR
928 "Set the log level for a specified category\n");
929 if (ret < 0)
930 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200931 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100932
Pablo Neira Ayusod6b51952011-05-03 22:32:32 +0200933 ret = snprintf(str + offset, rem,
934 "Global setting for all subsystems\n");
935 if (ret < 0)
936 goto err;
937 OSMO_SNPRINTF_RET(ret, rem, offset, len);
938
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100939 for (i = 0; i < info->num_cat; i++) {
Harald Welteb43bc042011-06-27 10:29:17 +0200940 if (info->cat[i].name == NULL)
941 continue;
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100942 ret = snprintf(str + offset, rem, "%s\n",
943 info->cat[i].description);
944 if (ret < 0)
945 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200946 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100947 }
948 for (i = 0; i < LOGLEVEL_DEFS; i++) {
949 ret = snprintf(str + offset, rem, "%s\n",
950 loglevel_descriptions[i]);
951 if (ret < 0)
952 goto err;
Pablo Neira Ayuso3abad6a2011-03-28 19:24:22 +0200953 OSMO_SNPRINTF_RET(ret, rem, offset, len);
Pablo Neira Ayuso04139f12011-03-09 13:05:08 +0100954 }
955err:
Pablo Neira Ayuso534ba812011-05-03 22:32:48 +0200956 str[size-1] = '\0';
Harald Welte7638af92010-05-11 16:39:22 +0200957 return str;
958}
959
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200960/*! Initialize the Osmocom logging core
Harald Welte18fc4652011-08-17 14:14:17 +0200961 * \param[in] inf Information regarding logging categories
962 * \param[in] ctx \ref talloc context for logging allocations
963 * \returns 0 in case of success, negative in case of error
964 */
Harald Welteb43bc042011-06-27 10:29:17 +0200965int log_init(const struct log_info *inf, void *ctx)
Harald Welte4a2bb9e2010-03-26 09:33:40 +0800966{
Harald Welteb43bc042011-06-27 10:29:17 +0200967 int i;
968
969 tall_log_ctx = talloc_named_const(ctx, 1, "logging");
970 if (!tall_log_ctx)
971 return -ENOMEM;
972
973 osmo_log_info = talloc_zero(tall_log_ctx, struct log_info);
974 if (!osmo_log_info)
975 return -ENOMEM;
976
Holger Hans Peter Freytherb7d0f462013-12-29 19:38:01 +0100977 osmo_log_info->filter_fn = inf->filter_fn;
Harald Welteb43bc042011-06-27 10:29:17 +0200978 osmo_log_info->num_cat_user = inf->num_cat;
979 /* total number = number of user cat + library cat */
Harald Weltece9fec32011-06-27 14:19:16 +0200980 osmo_log_info->num_cat = inf->num_cat + ARRAY_SIZE(internal_cat);
Harald Welteb43bc042011-06-27 10:29:17 +0200981
982 osmo_log_info->cat = talloc_zero_array(osmo_log_info,
983 struct log_info_cat,
984 osmo_log_info->num_cat);
985 if (!osmo_log_info->cat) {
986 talloc_free(osmo_log_info);
987 osmo_log_info = NULL;
988 return -ENOMEM;
989 }
990
991 /* copy over the user part */
992 for (i = 0; i < inf->num_cat; i++) {
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +0200993 memcpy((struct log_info_cat *) &osmo_log_info->cat[i],
994 &inf->cat[i],
Harald Welteb43bc042011-06-27 10:29:17 +0200995 sizeof(struct log_info_cat));
996 }
997
998 /* copy over the library part */
Harald Welte9fe16522011-06-27 14:00:03 +0200999 for (i = 0; i < ARRAY_SIZE(internal_cat); i++) {
Harald Weltece9fec32011-06-27 14:19:16 +02001000 unsigned int cn = osmo_log_info->num_cat_user + i;
Holger Hans Peter Freyther06f64552012-09-11 10:31:29 +02001001 memcpy((struct log_info_cat *) &osmo_log_info->cat[cn],
Harald Welte9fe16522011-06-27 14:00:03 +02001002 &internal_cat[i], sizeof(struct log_info_cat));
1003 }
1004
1005 return 0;
Harald Welte4a2bb9e2010-03-26 09:33:40 +08001006}
Harald Welte18fc4652011-08-17 14:14:17 +02001007
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001008/* De-initialize the Osmocom logging core
Harald Welte69e6c3c2016-04-20 10:41:27 +02001009 * This function destroys all targets and releases associated memory */
1010void log_fini(void)
1011{
1012 struct log_target *tar, *tar2;
1013
1014 llist_for_each_entry_safe(tar, tar2, &osmo_log_target_list, entry)
1015 log_target_destroy(tar);
1016
1017 talloc_free(osmo_log_info);
1018 osmo_log_info = NULL;
1019 talloc_free(tall_log_ctx);
1020 tall_log_ctx = NULL;
1021}
1022
Neels Hofmeyr87e45502017-06-20 00:17:59 +02001023/*! Check whether a log entry will be generated.
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001024 * \returns != 0 if a log entry might get generated by at least one target */
1025int log_check_level(int subsys, unsigned int level)
1026{
1027 struct log_target *tar;
1028
Harald Welte18a7d812017-03-16 23:54:55 +01001029 assert_loginfo();
1030
Holger Hans Peter Freythere0dc6a12015-12-21 14:45:16 +01001031 subsys = map_subsys(subsys);
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001032
1033 /* TODO: The following could/should be cached (update on config) */
1034
1035 llist_for_each_entry(tar, &osmo_log_target_list, entry) {
Maxc65c5b42017-03-15 13:20:23 +01001036 if (!should_log_to_target(tar, subsys, level))
Jacob Erlbeckde6dd722015-11-17 11:52:24 +01001037 continue;
1038
1039 /* This might get logged (ignoring filters) */
1040 return 1;
1041 }
1042
1043 /* We are sure, that this will not be logged. */
1044 return 0;
1045}
1046
Sylvain Munautdca7d2c2012-04-18 21:53:23 +02001047/*! @} */