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
This commit is contained in:
parent
0f9141384b
commit
b61e3b2158
|
@ -1,52 +1,51 @@
|
||||||
#ifndef _DEBUG_H
|
#ifndef _DEBUG_H
|
||||||
#define _DEBUG_H
|
#define _DEBUG_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "linuxlist.h"
|
||||||
|
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
|
|
||||||
#define DRLL 0x0001
|
/* Debug Areas of the code */
|
||||||
#define DCC 0x0002
|
enum {
|
||||||
#define DMM 0x0004
|
DRLL,
|
||||||
#define DRR 0x0008
|
DCC,
|
||||||
#define DRSL 0x0010
|
DMM,
|
||||||
#define DNM 0x0020
|
DRR,
|
||||||
|
DRSL,
|
||||||
#define DMNCC 0x0080
|
DNM,
|
||||||
#define DSMS 0x0100
|
DMNCC,
|
||||||
#define DPAG 0x0200
|
DSMS,
|
||||||
#define DMEAS 0x0400
|
DPAG,
|
||||||
|
DMEAS,
|
||||||
#define DMI 0x1000
|
DMI,
|
||||||
#define DMIB 0x2000
|
DMIB,
|
||||||
#define DMUX 0x4000
|
DMUX,
|
||||||
#define DINP 0x8000
|
DINP,
|
||||||
|
DSCCP,
|
||||||
#define DSCCP 0x10000
|
DMSC,
|
||||||
#define DMSC 0x20000
|
DMGCP,
|
||||||
|
DHO,
|
||||||
#define DMGCP 0x40000
|
Debug_LastEntry,
|
||||||
|
};
|
||||||
#define DHO 0x80000
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
#define DEBUGP(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ## args)
|
||||||
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
#define DEBUGPC(ss, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ## args)
|
||||||
#else
|
#else
|
||||||
#define DEBUGP(xss, fmt, args...)
|
#define DEBUGP(xss, fmt, args...)
|
||||||
#define DEBUGPC(ss, fmt, args...)
|
#define DEBUGPC(ss, fmt, args...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
#define static_assert(exp, name) typedef int dummy##name [(exp) ? 1 : -1];
|
||||||
|
|
||||||
char *hexdump(const unsigned char *buf, int len);
|
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 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 */
|
/* new logging interface */
|
||||||
#define LOGP(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 0, fmt, ##args)
|
#define LOGP(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 0, fmt, ##args)
|
||||||
#define LOGPC(ss, level, fmt, args...) debugp(ss, __FILE__, __LINE__, 1, fmt, ##args)
|
#define LOGPC(ss, level, fmt, args...) debugp2(ss, level, __FILE__, __LINE__, 1, fmt, ##args)
|
||||||
|
|
||||||
/* different levels */
|
/* different levels */
|
||||||
#define LOGL_DEBUG 1 /* debugging information */
|
#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_ERROR 7 /* error condition, requires user action */
|
||||||
#define LOGL_FATAL 8 /* fatal, program aborted */
|
#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 */
|
#endif /* _DEBUG_H */
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#define TELNET_INTERFACE_H
|
#define TELNET_INTERFACE_H
|
||||||
|
|
||||||
#include "gsm_data.h"
|
#include "gsm_data.h"
|
||||||
#include "linuxlist.h"
|
#include "debug.h"
|
||||||
#include "select.h"
|
#include "select.h"
|
||||||
|
|
||||||
#include <vty/vty.h>
|
#include <vty/vty.h>
|
||||||
|
@ -35,6 +35,7 @@ struct telnet_connection {
|
||||||
struct gsm_network *network;
|
struct gsm_network *network;
|
||||||
struct bsc_fd fd;
|
struct bsc_fd fd;
|
||||||
struct vty *vty;
|
struct vty *vty;
|
||||||
|
struct debug_target *dbg;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -237,6 +237,8 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, u_int8_t chan_nr)
|
||||||
}
|
}
|
||||||
|
|
||||||
lchan = &ts->lchan[lch_idx];
|
lchan = &ts->lchan[lch_idx];
|
||||||
|
debug_set_context(BSC_CTX_LCHAN, lchan);
|
||||||
|
debug_set_context(BSC_CTX_SUBSCR, lchan->subscr);
|
||||||
|
|
||||||
return lchan;
|
return lchan;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@ static const char *trx1_password = "1111111111";
|
||||||
|
|
||||||
static const u_int8_t too_fast[] = { 0x12, 0x80, 0x00, 0x00, 0x02, 0x02 };
|
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 */
|
/* dummy function to keep gsm_data.c happy */
|
||||||
struct counter *counter_alloc(const char *name)
|
struct counter *counter_alloc(const char *name)
|
||||||
{
|
{
|
||||||
|
@ -759,7 +761,7 @@ static void handle_options(int argc, char **argv)
|
||||||
serial_port = optarg;
|
serial_port = optarg;
|
||||||
break;
|
break;
|
||||||
case 'b':
|
case 'b':
|
||||||
debug_parse_category_mask(optarg);
|
debug_parse_category_mask(stderr_target, optarg);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
fname_software = optarg;
|
fname_software = optarg;
|
||||||
|
@ -812,6 +814,10 @@ int main(int argc, char **argv)
|
||||||
struct gsm_network *gsmnet;
|
struct gsm_network *gsmnet;
|
||||||
int rc;
|
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);
|
handle_options(argc, argv);
|
||||||
|
|
||||||
gsmnet = gsm_network_init(1, 1, NULL);
|
gsmnet = gsm_network_init(1, 1, NULL);
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <openbsc/signal.h>
|
#include <openbsc/signal.h>
|
||||||
|
|
||||||
/* MCC and MNC for the Location Area Identifier */
|
/* MCC and MNC for the Location Area Identifier */
|
||||||
|
static struct debug_target *stderr_target;
|
||||||
struct gsm_network *bsc_gsmnet = 0;
|
struct gsm_network *bsc_gsmnet = 0;
|
||||||
static const char *database_name = "hlr.sqlite3";
|
static const char *database_name = "hlr.sqlite3";
|
||||||
static const char *config_file = "openbsc.cfg";
|
static const char *config_file = "openbsc.cfg";
|
||||||
|
@ -105,10 +106,10 @@ static void handle_options(int argc, char** argv)
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
case 's':
|
case 's':
|
||||||
debug_use_color(0);
|
debug_set_use_color(stderr_target, 0);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
debug_parse_category_mask(optarg);
|
debug_parse_category_mask(stderr_target, optarg);
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
database_name = strdup(optarg);
|
database_name = strdup(optarg);
|
||||||
|
@ -120,7 +121,7 @@ static void handle_options(int argc, char** argv)
|
||||||
create_pcap_file(optarg);
|
create_pcap_file(optarg);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
debug_timestamp(1);
|
debug_set_print_timestamp(stderr_target, 1);
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
ipacc_rtp_direct = 0;
|
ipacc_rtp_direct = 0;
|
||||||
|
@ -158,11 +159,17 @@ int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
|
debug_init();
|
||||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
|
tall_bsc_ctx = talloc_named_const(NULL, 1, "openbsc");
|
||||||
talloc_ctx_init();
|
talloc_ctx_init();
|
||||||
on_dso_load_token();
|
on_dso_load_token();
|
||||||
on_dso_load_rrlp();
|
on_dso_load_rrlp();
|
||||||
on_dso_load_ho_dec();
|
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 */
|
/* parse options */
|
||||||
handle_options(argc, argv);
|
handle_options(argc, argv);
|
||||||
|
@ -193,6 +200,7 @@ int main(int argc, char **argv)
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
bsc_upqueue(bsc_gsmnet);
|
bsc_upqueue(bsc_gsmnet);
|
||||||
|
debug_reset_context();
|
||||||
bsc_select_main(0);
|
bsc_select_main(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1084,8 +1084,15 @@ int main(int argc, char** argv)
|
||||||
struct gsm_network dummy_network;
|
struct gsm_network dummy_network;
|
||||||
struct sockaddr_in addr;
|
struct sockaddr_in addr;
|
||||||
int on = 1, i, rc;
|
int on = 1, i, rc;
|
||||||
|
struct debug_target *stderr_target;
|
||||||
|
|
||||||
tall_bsc_ctx = talloc_named_const(NULL, 1, "mgcp-callagent");
|
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);
|
handle_options(argc, argv);
|
||||||
|
|
||||||
telnet_init(&dummy_network, 4243);
|
telnet_init(&dummy_network, 4243);
|
||||||
|
|
|
@ -27,21 +27,53 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <openbsc/debug.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 {
|
struct debug_info {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *color;
|
const char *color;
|
||||||
const char *description;
|
const char *description;
|
||||||
int number;
|
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) \
|
#define DEBUG_CATEGORY(NUMBER, NAME, COLOR, DESCRIPTION) \
|
||||||
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
|
{ .name = NAME, .color = COLOR, .description = DESCRIPTION, .number = NUMBER },
|
||||||
|
|
||||||
#define ARRAY_SIZE(array) (sizeof(array)/sizeof(array[0]))
|
|
||||||
|
|
||||||
static const struct debug_info debug_info[] = {
|
static const struct debug_info debug_info[] = {
|
||||||
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
|
DEBUG_CATEGORY(DRLL, "DRLL", "\033[1;31m", "")
|
||||||
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
|
DEBUG_CATEGORY(DCC, "DCC", "\033[1;32m", "")
|
||||||
|
@ -63,50 +95,51 @@ static const struct debug_info debug_info[] = {
|
||||||
DEBUG_CATEGORY(DHO, "DHO", "", "")
|
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.
|
* 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;
|
int i = 0;
|
||||||
char *mask = strdup(_mask);
|
char *mask = strdup(_mask);
|
||||||
char *category_token = NULL;
|
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, ":");
|
category_token = strtok(mask, ":");
|
||||||
do {
|
do {
|
||||||
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
for (i = 0; i < ARRAY_SIZE(debug_info); ++i) {
|
||||||
if (strcasecmp(debug_info[i].name, category_token) == 0)
|
char* colon = strstr(category_token, ",");
|
||||||
new_mask |= debug_info[i].number;
|
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, ":")));
|
} while ((category_token = strtok(NULL, ":")));
|
||||||
|
|
||||||
|
|
||||||
free(mask);
|
free(mask);
|
||||||
debug_mask = new_mask;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* color(int subsys)
|
static const char* color(int subsys)
|
||||||
{
|
{
|
||||||
int i = 0;
|
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)
|
if (debug_info[i].number == subsys)
|
||||||
return debug_info[i].color;
|
return debug_info[i].color;
|
||||||
}
|
}
|
||||||
|
@ -114,35 +147,97 @@ const char* color(int subsys)
|
||||||
return "";
|
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;
|
char col[30];
|
||||||
FILE *outfd = stderr;
|
char sub[30];
|
||||||
|
char tim[30];
|
||||||
|
char buf[4096];
|
||||||
|
char final[4096];
|
||||||
|
|
||||||
if (!(debug_mask & subsys))
|
/* prepare the data */
|
||||||
return;
|
col[0] = '\0';
|
||||||
|
sub[0] = '\0';
|
||||||
|
tim[0] = '\0';
|
||||||
|
buf[0] = '\0';
|
||||||
|
|
||||||
va_start(ap, format);
|
/* are we using color */
|
||||||
|
if (target->use_color)
|
||||||
fprintf(outfd, "%s", color(subsys));
|
snprintf(col, sizeof(col), "%s", color(subsys));
|
||||||
|
vsnprintf(buf, sizeof(buf), format, ap);
|
||||||
|
|
||||||
if (!cont) {
|
if (!cont) {
|
||||||
if (print_timestamp) {
|
if (target->print_timestamp) {
|
||||||
char *timestr;
|
char *timestr;
|
||||||
time_t tm;
|
time_t tm;
|
||||||
tm = time(NULL);
|
tm = time(NULL);
|
||||||
timestr = ctime(&tm);
|
timestr = ctime(&tm);
|
||||||
timestr[strlen(timestr)-1] = '\0';
|
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);
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
fflush(outfd);
|
void debugp2(unsigned int subsys, unsigned int level, char *file, int line, int cont, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, format);
|
||||||
|
_debugp(subsys, level, file, line, cont, format, ap);
|
||||||
|
va_end(ap);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char hexd_buff[4096];
|
static char hexd_buff[4096];
|
||||||
|
@ -164,3 +259,122 @@ char *hexdump(const unsigned char *buf, int len)
|
||||||
return hexd_buff;
|
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");
|
||||||
|
}
|
||||||
|
|
|
@ -435,6 +435,8 @@ int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
|
||||||
"tei %d, sapi %d\n", tei, sapi);
|
"tei %d, sapi %d\n", tei, sapi);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug_set_context(BSC_CTX_BTS, link->trx->bts);
|
||||||
switch (link->type) {
|
switch (link->type) {
|
||||||
case E1INP_SIGN_OML:
|
case E1INP_SIGN_OML:
|
||||||
msg->trx = link->trx;
|
msg->trx = link->trx;
|
||||||
|
|
|
@ -289,6 +289,12 @@ int main(int argc, char **argv)
|
||||||
struct gsm_bts *bts;
|
struct gsm_bts *bts;
|
||||||
struct sockaddr_in sin;
|
struct sockaddr_in sin;
|
||||||
int rc, option_index = 0, stream_id = 0xff;
|
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("ipaccess-config (C) 2009 by Harald Welte\n");
|
||||||
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
|
||||||
|
|
|
@ -120,6 +120,12 @@ int telnet_close_client(struct bsc_fd *fd) {
|
||||||
|
|
||||||
close(fd->fd);
|
close(fd->fd);
|
||||||
bsc_unregister_fd(fd);
|
bsc_unregister_fd(fd);
|
||||||
|
|
||||||
|
if (conn->dbg) {
|
||||||
|
debug_del_target(conn->dbg);
|
||||||
|
talloc_free(conn->dbg);
|
||||||
|
}
|
||||||
|
|
||||||
llist_del(&conn->entry);
|
llist_del(&conn->entry);
|
||||||
talloc_free(conn);
|
talloc_free(conn);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include <openbsc/meas_rep.h>
|
#include <openbsc/meas_rep.h>
|
||||||
#include <openbsc/db.h>
|
#include <openbsc/db.h>
|
||||||
#include <openbsc/talloc.h>
|
#include <openbsc/talloc.h>
|
||||||
|
#include <openbsc/telnet_interface.h>
|
||||||
|
|
||||||
static struct gsm_network *gsmnet;
|
static struct gsm_network *gsmnet;
|
||||||
|
|
||||||
|
@ -845,6 +846,170 @@ DEFUN(show_paging,
|
||||||
return CMD_SUCCESS;
|
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,
|
DEFUN(show_stats,
|
||||||
show_stats_cmd,
|
show_stats_cmd,
|
||||||
"show statistics",
|
"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_paging_cmd);
|
||||||
install_element(VIEW_NODE, &show_stats_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_element(CONFIG_NODE, &cfg_net_cmd);
|
||||||
install_node(&net_node, config_write_net);
|
install_node(&net_node, config_write_net);
|
||||||
install_default(GSMNET_NODE);
|
install_default(GSMNET_NODE);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
INCLUDES = $(all_includes) -I$(top_srcdir)/include
|
||||||
noinst_PROGRAMS = debug_test
|
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
|
||||||
|
|
Loading…
Reference in New Issue