aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <zecke@selfish.org>2009-12-22 22:32:51 +0100
committerHarald Welte <laforge@netfilter.org>2009-12-22 22:32:51 +0100
commitb61e3b21580afc6381a6c72618d51697c7ce9771 (patch)
tree2a88eb434a28d4064f7017fd1202f10e88f341f7
parent0f9141384bc338cdf8db805fbfe502aed622f816 (diff)
Import the new logging architecture
This is the new logging architecture, including * support for multiuple logging targets like stderr and vty * log levels in addition to categories/subsystems * filtering based on imsi, i.e. only see events for one subscriber * dynamically change log level for each category for each vty
-rw-r--r--openbsc/include/openbsc/debug.h129
-rw-r--r--openbsc/include/openbsc/telnet_interface.h3
-rw-r--r--openbsc/src/abis_rsl.c2
-rw-r--r--openbsc/src/bs11_config.c8
-rw-r--r--openbsc/src/bsc_hack.c14
-rw-r--r--openbsc/src/bsc_mgcp.c7
-rw-r--r--openbsc/src/debug.c296
-rw-r--r--openbsc/src/e1_input.c2
-rw-r--r--openbsc/src/ipaccess-config.c6
-rw-r--r--openbsc/src/telnet_interface.c6
-rw-r--r--openbsc/src/vty_interface.c173
-rw-r--r--openbsc/tests/debug/Makefile.am2
12 files changed, 571 insertions, 77 deletions
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index fc387cec3..129956d8e 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -1,52 +1,51 @@
#ifndef _DEBUG_H
#define _DEBUG_H
-#define DEBUG
-
-#define DRLL 0x0001
-#define DCC 0x0002
-#define DMM 0x0004
-#define DRR 0x0008
-#define DRSL 0x0010
-#define DNM 0x0020
-
-#define DMNCC 0x0080
-#define DSMS 0x0100
-#define DPAG 0x0200
-#define DMEAS 0x0400
+#include <stdio.h>
+#include "linuxlist.h"
-#define DMI 0x1000
-#define DMIB 0x2000
-#define DMUX 0x4000
-#define DINP 0x8000
-
-#define DSCCP 0x10000
-#define DMSC 0x20000
-
-#define DMGCP 0x40000
+#define DEBUG
-#define DHO 0x80000
+/* Debug Areas of the code */
+enum {
+ DRLL,
+ DCC,
+ DMM,
+ DRR,
+ DRSL,
+ DNM,
+ DMNCC,
+ DSMS,
+ DPAG,
+ DMEAS,
+ DMI,
+ DMIB,
+ DMUX,
+ DINP,
+ DSCCP,
+ DMSC,
+ DMGCP,
+ DHO,
+ Debug_LastEntry,
+};
#ifdef DEBUG
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
#else
-#define DEBUGP(xss, fmt, args...)
+#define DEBUGP(xss, fmt, args...)
#define DEBUGPC(ss, fmt, args...)
#endif
+
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
char *hexdump(const unsigned char *buf, int len);
void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 5, 6)));
-void debug_parse_category_mask(const char* mask);
-void debug_use_color(int use_color);
-void debug_timestamp(int enable);
-extern unsigned int debug_mask;
/* new logging interface */
-#define LOGP(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ##args)
-#define LOGPC(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ##args)
+#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
+#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
/* different levels */
#define LOGL_DEBUG 1 /* debugging information */
@@ -55,4 +54,74 @@ extern unsigned int debug_mask;
#define LOGL_ERROR 7 /* error condition, requires user action */
#define LOGL_FATAL 8 /* fatal, program aborted */
+/* context */
+#define BSC_CTX_LCHAN 0
+#define BSC_CTX_SUBSCR 1
+#define BSC_CTX_BTS 2
+#define BSC_CTX_SCCP 3
+
+/* target */
+
+enum {
+ DEBUG_FILTER_IMSI = 1 << 0,
+ DEBUG_FILTER_ALL = 1 << 1,
+};
+
+struct debug_category {
+ int enabled;
+ int loglevel;
+};
+
+struct debug_target {
+ int filter_map;
+ char *imsi_filter;
+
+
+ struct debug_category categories[Debug_LastEntry];
+ int use_color;
+ int print_timestamp;
+ int loglevel;
+
+ union {
+ struct {
+ FILE *out;
+ } tgt_stdout;
+
+ struct {
+ int priority;
+ } tgt_syslog;
+
+ struct {
+ void *vty;
+ } tgt_vty;
+ };
+
+ void (*output) (struct debug_target *target, const char *string);
+
+ struct llist_head entry;
+};
+
+/* use the above macros */
+void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...) __attribute__ ((format (printf, 6, 7)));
+void debug_init(void);
+
+/* context management */
+void debug_reset_context(void);
+void debug_set_context(int ctx, void *value);
+
+/* filter on the targets */
+void debug_set_imsi_filter(struct debug_target *target, const char *imsi);
+void debug_set_all_filter(struct debug_target *target, int);
+void debug_set_use_color(struct debug_target *target, int);
+void debug_set_print_timestamp(struct debug_target *target, int);
+void debug_set_log_level(struct debug_target *target, int log_level);
+void debug_parse_category_mask(struct debug_target *target, const char* mask);
+void debug_set_category_filter(struct debug_target *target, int category, int enable, int level);
+
+
+/* management of the targets */
+struct debug_target *debug_target_create(void);
+struct debug_target *debug_target_create_stderr(void);
+void debug_add_target(struct debug_target *target);
+void debug_del_target(struct debug_target *target);
#endif /* _DEBUG_H */
diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h
index d3381e0a4..f4e976f77 100644
--- a/openbsc/include/openbsc/telnet_interface.h
+++ b/openbsc/include/openbsc/telnet_interface.h
@@ -22,7 +22,7 @@
#define TELNET_INTERFACE_H
#include "gsm_data.h"
-#include "linuxlist.h"
+#include "debug.h"
#include "select.h"
#include <vty/vty.h>
@@ -35,6 +35,7 @@ struct telnet_connection {
struct gsm_network *network;
struct bsc_fd fd;
struct vty *vty;
+ struct debug_target *dbg;
};
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 787a8038b..d669ba06d 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -237,6 +237,8 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
}
lchan = &ts->lchan[lch_idx];
+ debug_set_context(BSC_CTX_LCHAN, lchan);
+ debug_set_context(BSC_CTX_SUBSCR, lchan->subscr);
return lchan;
}
diff --git a/openbsc/src/bs11_config.c b/openbsc/src/bs11_config.c
index 2a80a49ad..b2470a9fc 100644
--- a/openbsc/src/bs11_config.c
+++ b/openbsc/src/bs11_config.c
@@ -71,6 +71,8 @@ static const char *trx1_password = "1111111111";
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
+static struct debug_target *stderr_target;
+
/* dummy function to keep gsm_data.c happy */
struct counter *counter_alloc(const char *name)
{
@@ -759,7 +761,7 @@ static void handle_options(int argc, char **argv)
serial_port = optarg;
break;
case 'b':
- debug_parse_category_mask(optarg);
+ debug_parse_category_mask(stderr_target, optarg);
break;
case 's':
fname_software = optarg;
@@ -812,6 +814,10 @@ int main(int argc, char **argv)
struct gsm_network *gsmnet;
int rc;
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
handle_options(argc, argv);
gsmnet = gsm_network_init(1, 1, NULL);
diff --git a/openbsc/src/bsc_hack.c b/openbsc/src/bsc_hack.c
index a9a5d372f..0d20d43d3 100644
--- a/openbsc/src/bsc_hack.c
+++ b/openbsc/src/bsc_hack.c
@@ -38,6 +38,7 @@
#include <openbsc/signal.h>
/* MCC and MNC for the Location Area Identifier */
+static struct debug_target *stderr_target;
struct gsm_network *bsc_gsmnet = 0;
static const char *database_name = "hlr.sqlite3";
static const char *config_file = "openbsc.cfg";
@@ -105,10 +106,10 @@ static void handle_options(int argc, char** argv)
print_help();
exit(0);
case 's':
- debug_use_color(0);
+ debug_set_use_color(stderr_target, 0);
break;
case 'd':
- debug_parse_category_mask(optarg);
+ debug_parse_category_mask(stderr_target, optarg);
break;
case 'l':
database_name = strdup(optarg);
@@ -120,7 +121,7 @@ static void handle_options(int argc, char** argv)
create_pcap_file(optarg);
break;
case 'T':
- debug_timestamp(1);
+ debug_set_print_timestamp(stderr_target, 1);
break;
case 'P':
ipacc_rtp_direct = 0;
@@ -158,11 +159,17 @@ int main(int argc, char **argv)
{
int rc;
+ debug_init();
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
talloc_ctx_init();
on_dso_load_token();
on_dso_load_rrlp();
on_dso_load_ho_dec();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+
+ /* enable filters */
+ debug_set_all_filter(stderr_target, 1);
/* parse options */
handle_options(argc, argv);
@@ -193,6 +200,7 @@ int main(int argc, char **argv)
while (1) {
bsc_upqueue(bsc_gsmnet);
+ debug_reset_context();
bsc_select_main(0);
}
}
diff --git a/openbsc/src/bsc_mgcp.c b/openbsc/src/bsc_mgcp.c
index 6d5e6b154..fff6d6039 100644
--- a/openbsc/src/bsc_mgcp.c
+++ b/openbsc/src/bsc_mgcp.c
@@ -1084,8 +1084,15 @@ int main(int argc, char** argv)
struct gsm_network dummy_network;
struct sockaddr_in addr;
int on = 1, i, rc;
+ struct debug_target *stderr_target;
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
+
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
+
handle_options(argc, argv);
telnet_init(&dummy_network, 4243);
diff --git a/openbsc/src/debug.c b/openbsc/src/debug.c
index 6e99d4144..d3d58f3fb 100644
--- a/openbsc/src/debug.c
+++ b/openbsc/src/debug.c
@@ -27,21 +27,53 @@
#include <time.h>
#include <openbsc/debug.h>
+#include <openbsc/talloc.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
-unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB|DMEAS);
+/* default categories */
+static struct debug_category default_categories[Debug_LastEntry] = {
+ [DRLL] = { .enabled = 1, .loglevel = 0},
+ [DCC] = { .enabled = 1, .loglevel = 0},
+ [DMM] = { .enabled = 1, .loglevel = 0},
+ [DRR] = { .enabled = 1, .loglevel = 0},
+ [DRSL] = { .enabled = 1, .loglevel = 0},
+ [DMM] = { .enabled = 1, .loglevel = 0},
+ [DMNCC] = { .enabled = 1, .loglevel = 0},
+ [DSMS] = { .enabled = 1, .loglevel = 0},
+ [DPAG] = { .enabled = 1, .loglevel = 0},
+ [DMEAS] = { .enabled = 0, .loglevel = 0},
+ [DMI] = { .enabled = 0, .loglevel = 0},
+ [DMIB] = { .enabled = 0, .loglevel = 0},
+ [DMUX] = { .enabled = 1, .loglevel = 0},
+ [DINP] = { .enabled = 1, .loglevel = 0},
+ [DSCCP] = { .enabled = 1, .loglevel = 0},
+ [DMSC] = { .enabled = 1, .loglevel = 0},
+ [DMGCP] = { .enabled = 1, .loglevel = 0},
+ [DHO] = { .enabled = 1, .loglevel = 0},
+};
struct debug_info {
const char *name;
const char *color;
const char *description;
int number;
+ int position;
+};
+
+struct debug_context {
+ struct gsm_lchan *lchan;
+ struct gsm_subscriber *subscr;
+ struct gsm_bts *bts;
};
+static struct debug_context debug_context;
+static void *tall_dbg_ctx = NULL;
+static LLIST_HEAD(target_list);
+
#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
-#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
-
static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
@@ -63,50 +95,51 @@ static const struct debug_info debug_info[] = {
DEBUG_CATEGORY(DHO, "DHO", "", "")
};
-static int use_color = 1;
-
-void debug_use_color(int color)
-{
- use_color = color;
-}
-
-static int print_timestamp = 0;
-
-void debug_timestamp(int enable)
-{
- print_timestamp = enable;
-}
-
-
/*
* Parse the category mask.
- * category1:category2:category3
+ * The format can be this: category1:category2:category3
+ * or category1,2:category2,3:...
*/
-void debug_parse_category_mask(const char *_mask)
+void debug_parse_category_mask(struct debug_target* target, const char *_mask)
{
- unsigned int new_mask = 0;
int i = 0;
char *mask = strdup(_mask);
char *category_token = NULL;
+ /* Disable everything to enable it afterwards */
+ for (i = 0; i < ARRAY_SIZE(target->categories); ++i)
+ target->categories[i].enabled = 0;
+
category_token = strtok(mask, ":");
do {
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
- if (strcasecmp(debug_info[i].name, category_token) == 0)
- new_mask |= debug_info[i].number;
+ char* colon = strstr(category_token, ",");
+ int length = strlen(category_token);
+
+ if (colon)
+ length = colon - category_token;
+
+ if (strncasecmp(debug_info[i].name, category_token, length) == 0) {
+ int number = debug_info[i].number;
+ int level = 0;
+
+ if (colon)
+ level = atoi(colon+1);
+
+ target->categories[number].enabled = 1;
+ target->categories[number].loglevel = level;
+ }
}
} while ((category_token = strtok(NULL, ":")));
-
free(mask);
- debug_mask = new_mask;
}
-const char* color(int subsys)
+static const char* color(int subsys)
{
int i = 0;
- for (i = 0; use_color && i < ARRAY_SIZE(debug_info); ++i) {
+ for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
if (debug_info[i].number == subsys)
return debug_info[i].color;
}
@@ -114,35 +147,97 @@ const char* color(int subsys)
return "";
}
-void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
+static void _output(struct debug_target *target, unsigned int subsys, char *file, int line,
+ int cont, const char *format, va_list ap)
{
- va_list ap;
- FILE *outfd = stderr;
+ char col[30];
+ char sub[30];
+ char tim[30];
+ char buf[4096];
+ char final[4096];
- if (!(debug_mask & subsys))
- return;
-
- va_start(ap, format);
+ /* prepare the data */
+ col[0] = '\0';
+ sub[0] = '\0';
+ tim[0] = '\0';
+ buf[0] = '\0';
- fprintf(outfd, "%s", color(subsys));
+ /* are we using color */
+ if (target->use_color)
+ snprintf(col, sizeof(col), "%s", color(subsys));
+ vsnprintf(buf, sizeof(buf), format, ap);
if (!cont) {
- if (print_timestamp) {
+ if (target->print_timestamp) {
char *timestr;
time_t tm;
tm = time(NULL);
timestr = ctime(&tm);
timestr[strlen(timestr)-1] = '\0';
- fprintf(outfd, "%s ", timestr);
+ snprintf(tim, sizeof(tim), "%s ", timestr);
}
- fprintf(outfd, "<%4.4x> %s:%d ", subsys, file, line);
+ snprintf(sub, sizeof(sub), "<%4.4x> %s:%d ", subsys, file, line);
}
- vfprintf(outfd, format, ap);
- fprintf(outfd, "\033[0;m");
+ snprintf(final, sizeof(final), "%s%s%s%s\033[0;m", col, tim, sub, buf);
+ target->output(target, final);
+}
+
+
+static void _debugp(unsigned int subsys, int level, char *file, int line,
+ int cont, const char *format, va_list ap)
+{
+ struct debug_target *tar;
+
+ llist_for_each_entry(tar, &target_list, entry) {
+ struct debug_category *category;
+ int output = 0;
+
+ category = &tar->categories[subsys];
+ /* subsystem is not supposed to be debugged */
+ if (!category->enabled)
+ continue;
+
+ /* Check the global log level */
+ if (tar->loglevel != 0 && level < tar->loglevel)
+ continue;
+
+ /* Check the category log level */
+ if (category->loglevel != 0 && level < category->loglevel)
+ continue;
+
+ /*
+ * Apply filters here... if that becomes messy we will need to put
+ * filters in a list and each filter will say stop, continue, output
+ */
+ if ((tar->filter_map & DEBUG_FILTER_ALL) != 0) {
+ output = 1;
+ } else if ((tar->filter_map & DEBUG_FILTER_IMSI) != 0
+ && debug_context.subscr && strcmp(debug_context.subscr->imsi, tar->imsi_filter) == 0) {
+ output = 1;
+ }
+
+ if (output)
+ _output(tar, subsys, file, line, cont, format, ap);
+ }
+}
+
+void debugp(unsigned int subsys, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ _debugp(subsys, LOGL_DEBUG, file, line, cont, format, ap);
va_end(ap);
+}
+
+void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
+{
+ va_list ap;
- fflush(outfd);
+ va_start(ap, format);
+ _debugp(subsys, level, file, line, cont, format, ap);
+ va_end(ap);
}
static char hexd_buff[4096];
@@ -164,3 +259,122 @@ char *hexdump(const unsigned char *buf, int len)
return hexd_buff;
}
+
+
+void debug_add_target(struct debug_target *target)
+{
+ llist_add_tail(&target->entry, &target_list);
+}
+
+void debug_del_target(struct debug_target *target)
+{
+ llist_del(&target->entry);
+}
+
+void debug_reset_context(void)
+{
+ memset(&debug_context, 0, sizeof(debug_context));
+}
+
+/* currently we are not reffing these */
+void debug_set_context(int ctx, void *value)
+{
+ switch (ctx) {
+ case BSC_CTX_LCHAN:
+ debug_context.lchan = (struct gsm_lchan *) value;
+ break;
+ case BSC_CTX_SUBSCR:
+ debug_context.subscr = (struct gsm_subscriber *) value;
+ break;
+ case BSC_CTX_BTS:
+ debug_context.bts = (struct gsm_bts *) value;
+ break;
+ case BSC_CTX_SCCP:
+ break;
+ default:
+ break;
+ }
+}
+
+void debug_set_imsi_filter(struct debug_target *target, const char *imsi)
+{
+ if (imsi) {
+ target->filter_map |= DEBUG_FILTER_IMSI;
+ target->imsi_filter = talloc_strdup(target, imsi);
+ } else if (target->imsi_filter) {
+ target->filter_map &= ~DEBUG_FILTER_IMSI;
+ talloc_free(target->imsi_filter);
+ target->imsi_filter = NULL;
+ }
+}
+
+void debug_set_all_filter(struct debug_target *target, int all)
+{
+ if (all)
+ target->filter_map |= DEBUG_FILTER_ALL;
+ else
+ target->filter_map &= ~DEBUG_FILTER_ALL;
+}
+
+void debug_set_use_color(struct debug_target *target, int use_color)
+{
+ target->use_color = use_color;
+}
+
+void debug_set_print_timestamp(struct debug_target *target, int print_timestamp)
+{
+ target->print_timestamp = print_timestamp;
+}
+
+void debug_set_log_level(struct debug_target *target, int log_level)
+{
+ target->loglevel = log_level;
+}
+
+void debug_set_category_filter(struct debug_target *target, int category, int enable, int level)
+{
+ if (category >= Debug_LastEntry)
+ return;
+ target->categories[category].enabled = !!enable;
+ target->categories[category].loglevel = level;
+}
+
+static void _stderr_output(struct debug_target *target, const char *log)
+{
+ fprintf(target->tgt_stdout.out, "%s", log);
+ fflush(target->tgt_stdout.out);
+}
+
+struct debug_target *debug_target_create(void)
+{
+ struct debug_target *target;
+
+ target = talloc_zero(tall_dbg_ctx, struct debug_target);
+ if (!target)
+ return NULL;
+
+ INIT_LLIST_HEAD(&target->entry);
+ memcpy(target->categories, default_categories, sizeof(default_categories));
+ target->use_color = 1;
+ target->print_timestamp = 0;
+ target->loglevel = 0;
+ return target;
+}
+
+struct debug_target *debug_target_create_stderr(void)
+{
+ struct debug_target *target;
+
+ target = debug_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_stdout.out = stderr;
+ target->output = _stderr_output;
+ return target;
+}
+
+void debug_init(void)
+{
+ tall_dbg_ctx = talloc_named_const(NULL, 1, "debug");
+}
diff --git a/openbsc/src/e1_input.c b/openbsc/src/e1_input.c
index 083d8f8de..0a0cc8d7e 100644
--- a/openbsc/src/e1_input.c
+++ b/openbsc/src/e1_input.c
@@ -435,6 +435,8 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
"tei %d, sapi %d\n", tei, sapi);
return -EINVAL;
}
+
+ debug_set_context(BSC_CTX_BTS, link->trx->bts);
switch (link->type) {
case E1INP_SIGN_OML:
msg->trx = link->trx;
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index c50a46581..005c93dd5 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -289,6 +289,12 @@ int main(int argc, char **argv)
struct gsm_bts *bts;
struct sockaddr_in sin;
int rc, option_index = 0, stream_id = 0xff;
+ struct debug_target *stderr_target;
+
+ debug_init();
+ stderr_target = debug_target_create_stderr();
+ debug_add_target(stderr_target);
+ debug_set_all_filter(stderr_target, 1);
printf("ipaccess-config (C) 2009 by Harald Welte\n");
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index bc91ca333..ebddaf57a 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -120,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) {
close(fd->fd);
bsc_unregister_fd(fd);
+
+ if (conn->dbg) {
+ debug_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ }
+
llist_del(&conn->entry);
talloc_free(conn);
return 0;
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index 62eee7a80..9645af23a 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -37,6 +37,7 @@
#include <openbsc/meas_rep.h>
#include <openbsc/db.h>
#include <openbsc/talloc.h>
+#include <openbsc/telnet_interface.h>
static struct gsm_network *gsmnet;
@@ -845,6 +846,170 @@ DEFUN(show_paging,
return CMD_SUCCESS;
}
+static void _vty_output(struct debug_target *tgt, const char *line)
+{
+ struct vty *vty = tgt->tgt_vty.vty;
+ vty_out(vty, "%s", line);
+ /* This is an ugly hack, but there is no easy way... */
+ if (strchr(line, '\n'))
+ vty_out(vty, "\r");
+}
+
+struct debug_target *debug_target_create_vty(struct vty *vty)
+{
+ struct debug_target *target;
+
+ target = debug_target_create();
+ if (!target)
+ return NULL;
+
+ target->tgt_vty.vty = vty;
+ target->output = _vty_output;
+ return target;
+}
+
+DEFUN(enable_logging,
+ enable_logging_cmd,
+ "logging enable",
+ "Enables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (conn->dbg) {
+ vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ conn->dbg = debug_target_create_vty(vty);
+ if (!conn->dbg)
+ return CMD_WARNING;
+
+ debug_add_target(conn->dbg);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_imsi,
+ logging_fltr_imsi_cmd,
+ "logging filter imsi IMSI",
+ "Print all messages related to a IMSI\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_imsi_filter(conn->dbg, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_fltr_all,
+ logging_fltr_all_cmd,
+ "logging filter all <0-1>",
+ "Print all messages to the console\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_all_filter(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_use_clr,
+ logging_use_clr_cmd,
+ "logging use color <0-1>",
+ "Use color for printing messages\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_use_color(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_prnt_timestamp,
+ logging_prnt_timestamp_cmd,
+ "logging print timestamp <0-1>",
+ "Print the timestamp of each message\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_print_timestamp(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_category_mask,
+ logging_set_category_mask_cmd,
+ "logging set debug mask MASK",
+ "Decide which categories to output.\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_parse_category_mask(conn->dbg, argv[0]);
+ return CMD_SUCCESS;
+}
+
+DEFUN(logging_set_log_level,
+ logging_set_log_level_cmd,
+ "logging set log level <0-8>",
+ "Set the global log level. The value 0 implies no filtering.\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_set_log_level(conn->dbg, atoi(argv[0]));
+ return CMD_SUCCESS;
+}
+
+DEFUN(diable_logging,
+ disable_logging_cmd,
+ "logging disable",
+ "Disables logging to this vty\n")
+{
+ struct telnet_connection *conn;
+
+ conn = (struct telnet_connection *) vty->priv;
+ if (!conn->dbg) {
+ vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ debug_del_target(conn->dbg);
+ talloc_free(conn->dbg);
+ conn->dbg = NULL;
+ return CMD_SUCCESS;
+}
+
DEFUN(show_stats,
show_stats_cmd,
"show statistics",
@@ -1581,6 +1746,14 @@ int bsc_vty_init(struct gsm_network *net)
install_element(VIEW_NODE, &show_paging_cmd);
install_element(VIEW_NODE, &show_stats_cmd);
+ install_element(VIEW_NODE, &enable_logging_cmd);
+ install_element(VIEW_NODE, &disable_logging_cmd);
+ install_element(VIEW_NODE, &logging_fltr_imsi_cmd);
+ install_element(VIEW_NODE, &logging_fltr_all_cmd);
+ install_element(VIEW_NODE, &logging_use_clr_cmd);
+ install_element(VIEW_NODE, &logging_prnt_timestamp_cmd);
+ install_element(VIEW_NODE, &logging_set_category_mask_cmd);
+
install_element(CONFIG_NODE, &cfg_net_cmd);
install_node(&net_node, config_write_net);
install_default(GSMNET_NODE);
diff --git a/openbsc/tests/debug/Makefile.am b/openbsc/tests/debug/Makefile.am
index 0cdf46ad5..62c906e72 100644
--- a/openbsc/tests/debug/Makefile.am
+++ b/openbsc/tests/debug/Makefile.am
@@ -1,4 +1,4 @@
INCLUDES = $(all_includes) -I$(top_srcdir)/include
noinst_PROGRAMS = debug_test
-debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c
+debug_test_SOURCES = debug_test.c $(top_srcdir)/src/debug.c $(top_srcdir)/src/talloc.c