diff --git a/include/osmocom/vty/logging.h b/include/osmocom/vty/logging.h index f4e28f887..e0011bf92 100644 --- a/include/osmocom/vty/logging.h +++ b/include/osmocom/vty/logging.h @@ -4,8 +4,9 @@ #define LOGGING_STR "Configure log message to this terminal\n" #define FILTER_STR "Filter log messages\n" -void logging_vty_add_cmds(void); - +struct log_info; +void logging_vty_add_cmds(const struct log_info *cat); +struct vty; struct log_target *osmo_log_vty2tgt(struct vty *vty); #endif /* _VTY_LOGGING_H */ diff --git a/include/osmocore/logging.h b/include/osmocore/logging.h index a2c63e9a7..fd4584405 100644 --- a/include/osmocore/logging.h +++ b/include/osmocore/logging.h @@ -144,9 +144,9 @@ int log_target_file_reopen(struct log_target *tgt); void log_add_target(struct log_target *target); void log_del_target(struct log_target *target); -/* Gernerate command argument strings for VTY use */ -const char *log_vty_category_string(struct log_info *info); -const char *log_vty_level_string(struct log_info *info); +/* Generate command string for VTY use */ +const char *log_vty_command_string(const struct log_info *info); +const char *log_vty_command_description(const struct log_info *info); struct log_target *log_target_find(int type, const char *fname); extern struct llist_head osmo_log_target_list; diff --git a/src/logging.c b/src/logging.c index 876a35265..89ca6ced0 100644 --- a/src/logging.c +++ b/src/logging.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_STRINGS_H #include @@ -37,13 +38,17 @@ #include #include +#include /* for LOGGING_STR. */ + const struct log_info *osmo_log_info; static struct log_context log_context; static void *tall_log_ctx = NULL; LLIST_HEAD(osmo_log_target_list); -static const struct value_string loglevel_strs[] = { +#define LOGLEVEL_DEFS 6 /* Number of loglevels.*/ + +static const struct value_string loglevel_strs[LOGLEVEL_DEFS+1] = { { 0, "EVERYTHING" }, { LOGL_DEBUG, "DEBUG" }, { LOGL_INFO, "INFO" }, @@ -53,6 +58,17 @@ static const struct value_string loglevel_strs[] = { { 0, NULL }, }; +/* You have to keep this in sync with the structure loglevel_strs. */ +const char *loglevel_descriptions[LOGLEVEL_DEFS+1] = { + "Log simply everything", + "Log debug messages and higher levels", + "Log informational messages and higher levels", + "Log noticable messages and higher levels", + "Log error messages and higher levels", + "Log only fatal messages", + NULL, +}; + int log_parse_level(const char *lvl) { return get_string_value(loglevel_strs, lvl); @@ -418,49 +434,125 @@ int log_target_file_reopen(struct log_target *target) return 0; } -const char *log_vty_level_string(struct log_info *info) +/* This can go into some header file so others can benefit from it. */ +#define SNPRINTF_FAILURE(ret, rem, offset, len) \ +do { \ + len += ret; \ + if (ret > rem) \ + ret = rem; \ + offset += ret; \ + rem -= ret; \ +} while (0) + +/* This generates the logging command string for VTY. */ +const char *log_vty_command_string(const struct log_info *info) { - const struct value_string *vs; - unsigned int len = 3; /* ()\0 */ - char *str; - - for (vs = loglevel_strs; vs->value || vs->str; vs++) - len += strlen(vs->str) + 1; - - str = talloc_zero_size(NULL, len); - if (!str) - return NULL; - - str[0] = '('; - for (vs = loglevel_strs; vs->value || vs->str; vs++) { - strcat(str, vs->str); - strcat(str, "|"); - } - str[strlen(str)-1] = ')'; - - return str; -} - -const char *log_vty_category_string(struct log_info *info) -{ - unsigned int len = 3; /* "()\0" */ - unsigned int i; + int len = 0, offset = 0, ret, i, rem; + int size = strlen("logging level () ()") + 1; char *str; for (i = 0; i < info->num_cat; i++) - len += strlen(info->cat[i].name) + 1; + size += strlen(info->cat[i].name) + 1; - str = talloc_zero_size(NULL, len); + for (i = 0; i < LOGLEVEL_DEFS; i++) + size += strlen(loglevel_strs[i].str) + 1; + + rem = size; + str = talloc_zero_size(NULL, size); if (!str) return NULL; - str[0] = '('; - for (i = 0; i < info->num_cat; i++) { - strcat(str, info->cat[i].name+1); - strcat(str, "|"); - } - str[strlen(str)-1] = ')'; + ret = snprintf(str + offset, rem, "logging level ("); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + for (i = 0; i < info->num_cat; i++) { + int j, name_len = strlen(info->cat[i].name)+1; + char name[name_len]; + + for (j = 0; j < name_len; j++) + name[j] = tolower(info->cat[i].name[j]); + + name[name_len-1] = '\0'; + ret = snprintf(str + offset, rem, "%s|", name+1); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + } + offset--; /* to remove the trailing | */ + rem++; + + ret = snprintf(str + offset, rem, ") ("); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + + for (i = 0; i < LOGLEVEL_DEFS; i++) { + int j, loglevel_str_len = strlen(loglevel_strs[i].str)+1; + char loglevel_str[loglevel_str_len]; + + for (j = 0; j < loglevel_str_len; j++) + loglevel_str[j] = tolower(loglevel_strs[i].str[j]); + + loglevel_str[loglevel_str_len-1] = '\0'; + ret = snprintf(str + offset, rem, "%s|", loglevel_str); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + } + offset--; /* to remove the trailing | */ + rem++; + + ret = snprintf(str + offset, rem, ")"); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); +err: + return str; +} + +/* This generates the logging command description for VTY. */ +const char *log_vty_command_description(const struct log_info *info) +{ + char *str; + int i, ret, len = 0, offset = 0, rem; + unsigned int size = + strlen(LOGGING_STR + "Set the log level for a specified category\n") + 1; + + for (i = 0; i < info->num_cat; i++) + size += strlen(info->cat[i].description) + 1; + + for (i = 0; i < LOGLEVEL_DEFS; i++) + size += strlen(loglevel_descriptions[i]) + 1; + + rem = size; + str = talloc_zero_size(NULL, size); + if (!str) + return NULL; + + ret = snprintf(str + offset, rem, LOGGING_STR + "Set the log level for a specified category\n"); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + + for (i = 0; i < info->num_cat; i++) { + ret = snprintf(str + offset, rem, "%s\n", + info->cat[i].description); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + } + for (i = 0; i < LOGLEVEL_DEFS; i++) { + ret = snprintf(str + offset, rem, "%s\n", + loglevel_descriptions[i]); + if (ret < 0) + goto err; + SNPRINTF_FAILURE(ret, rem, offset, len); + } +err: return str; } diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index 904f8fece..1b356c9fe 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -149,56 +149,10 @@ DEFUN(logging_prnt_timestamp, return CMD_SUCCESS; } -/* FIXME: those have to be kept in sync with the log levels and categories */ -#define VTY_DEBUG_CATEGORIES "(rll|cc|mm|rr|rsl|nm|sms|pag|mncc|inp|mi|mib|mux|meas|sccp|msc|mgcp|ho|db|ref|gprs|ns|bssgp|llc|sndcp|isup|m2ua|pcap|nat|all)" -#define CATEGORIES_HELP \ - "A-bis Radio Link Layer (RLL)\n" \ - "Layer3 Call Control (CC)\n" \ - "Layer3 Mobility Management (MM)\n" \ - "Layer3 Radio Resource (RR)\n" \ - "A-bis Radio Signalling Link (RSL)\n" \ - "A-bis Network Management / O&M (NM/OML)\n" \ - "Layer3 Short Messagaging Service (SMS)\n" \ - "Paging Subsystem\n" \ - "MNCC API for Call Control application\n" \ - "A-bis Input Subsystem\n" \ - "A-bis Input Driver for Signalling\n" \ - "A-bis Input Driver for B-Channel (voice data)\n" \ - "A-bis B-Channel / Sub-channel Multiplexer\n" \ - "Radio Measurements\n" \ - "SCCP\n" \ - "Mobile Switching Center\n" \ - "Media Gateway Control Protocol\n" \ - "Hand-over\n" \ - "Database Layer\n" \ - "Reference Counting\n" \ - "GPRS Core\n" \ - "GPRS Network Service (NS)\n" \ - "GPRS BSS Gateway Protocol (BSSGP)\n" \ - "GPRS Logical Link Control Protocol (LLC)\n" \ - "GPRS Sub-Network Dependent Control Protocol (SNDCP)\n" \ - "ISDN User Part (ISUP)\n" \ - "SCTP M2UA\n" \ - "Trace message IO\n" \ - "BSC NAT\n" \ - "Global setting for all subsytems\n" - -#define VTY_DEBUG_LEVELS "(everything|debug|info|notice|error|fatal)" -#define LEVELS_HELP \ - "Log simply everything\n" \ - "Log debug messages and higher levels\n" \ - "Log informational messages and higher levels\n" \ - "Log noticable messages and higher levels\n" \ - "Log error messages and higher levels\n" \ - "Log only fatal messages\n" - DEFUN(logging_level, logging_level_cmd, - "logging level " VTY_DEBUG_CATEGORIES " " VTY_DEBUG_LEVELS, - LOGGING_STR - "Set the log level for a specified category\n" - CATEGORIES_HELP - LEVELS_HELP) + NULL, /* cmdstr is dynamically set in logging_vty_add_cmds(). */ + NULL) /* same thing for helpstr. */ { int category = log_parse_category(argv[0]); int level = log_parse_level(argv[1]); @@ -596,7 +550,7 @@ static int config_write_log(struct vty *vty) return 1; } -void logging_vty_add_cmds() +void logging_vty_add_cmds(const struct log_info *cat) { install_element_ve(&enable_logging_cmd); install_element_ve(&disable_logging_cmd); @@ -604,6 +558,10 @@ void logging_vty_add_cmds() install_element_ve(&logging_use_clr_cmd); install_element_ve(&logging_prnt_timestamp_cmd); install_element_ve(&logging_set_category_mask_cmd); + + /* Logging level strings are generated dynamically. */ + logging_level_cmd.string = log_vty_command_string(cat); + logging_level_cmd.doc = log_vty_command_description(cat); install_element_ve(&logging_level_cmd); install_element_ve(&show_logging_vty_cmd);