diff options
Diffstat (limited to 'src/vty')
-rw-r--r-- | src/vty/Makefile.am | 10 | ||||
-rw-r--r-- | src/vty/command.c | 847 | ||||
-rw-r--r-- | src/vty/cpu_sched_vty.c | 682 | ||||
-rw-r--r-- | src/vty/fsm_vty.c | 99 | ||||
-rw-r--r-- | src/vty/logging_vty.c | 296 | ||||
-rw-r--r-- | src/vty/stats_vty.c | 281 | ||||
-rw-r--r-- | src/vty/talloc_ctx_vty.c | 17 | ||||
-rw-r--r-- | src/vty/tdef_vty.c | 13 | ||||
-rw-r--r-- | src/vty/telnet_interface.c | 61 | ||||
-rw-r--r-- | src/vty/utils.c | 114 | ||||
-rw-r--r-- | src/vty/vector.c | 5 | ||||
-rw-r--r-- | src/vty/vty.c | 138 |
12 files changed, 2115 insertions, 448 deletions
diff --git a/src/vty/Makefile.am b/src/vty/Makefile.am index 35350cc3..4a4f16ba 100644 --- a/src/vty/Makefile.am +++ b/src/vty/Makefile.am @@ -1,10 +1,10 @@ # This is _NOT_ the library release version, it's an API version. # Please read chapter "Library interface versions" of the libtool documentation # before making any modifications: https://www.gnu.org/software/libtool/manual/html_node/Versioning.html -LIBVERSION=8:1:4 +LIBVERSION=13:1:0 -AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -AM_CFLAGS = -Wall $(TALLOC_CFLAGS) +AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include -I$(top_builddir) +AM_CFLAGS = -Wall $(TALLOC_CFLAGS) $(PTHREAD_CFLAGS) if ENABLE_VTY lib_LTLIBRARIES = libosmovty.la @@ -12,7 +12,7 @@ lib_LTLIBRARIES = libosmovty.la libosmovty_la_SOURCES = buffer.c command.c vty.c vector.c utils.c \ telnet_interface.c logging_vty.c stats_vty.c \ fsm_vty.c talloc_ctx_vty.c \ - tdef_vty.c + cpu_sched_vty.c tdef_vty.c libosmovty_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined -libosmovty_la_LIBADD = $(top_builddir)/src/libosmocore.la $(TALLOC_LIBS) +libosmovty_la_LIBADD = $(top_builddir)/src/core/libosmocore.la $(TALLOC_LIBS) $(PTHREAD_LIBS) endif diff --git a/src/vty/command.c b/src/vty/command.c index 9b32d22d..1719690b 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -37,14 +37,19 @@ Boston, MA 02110-1301, USA. */ #include <unistd.h> #include <ctype.h> #include <time.h> +#include <limits.h> +#include <signal.h> #include <sys/time.h> #include <sys/stat.h> +#include <sys/types.h> #include <osmocom/vty/vector.h> #include <osmocom/vty/vty.h> #include <osmocom/vty/command.h> +#include <osmocom/core/logging.h> #include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> #include <osmocom/core/utils.h> #ifndef MAXPATHLEN @@ -62,6 +67,9 @@ Boston, MA 02110-1301, USA. */ void *tall_vty_cmd_ctx; +/* Set by on_dso_load_starttime() for "show uptime". */ +static struct timespec starttime; + /* Command vector which includes some level of command lists. Normally each daemon maintains each own cmdvec. */ vector cmdvec; @@ -69,6 +77,21 @@ vector cmdvec; /* Host information structure. */ struct host host; +struct vty_parent_node { + struct llist_head entry; + + /*! private data, specified by creator */ + void *priv; + void *index; + + /*! Node status of this vty */ + int node; + + /*! When reading from a config file, these are the indenting characters expected for children of + * this VTY node. */ + char *indent; +}; + /* Standard command node structures. */ struct cmd_node auth_node = { AUTH_NODE, @@ -621,23 +644,134 @@ static char *xml_escape(const char *inp) typedef int (*print_func_t)(void *data, const char *fmt, ...); +static const struct value_string cmd_attr_desc[] = { + { CMD_ATTR_DEPRECATED, "This command is deprecated" }, + { CMD_ATTR_HIDDEN, "This command is hidden (check expert mode)" }, + { CMD_ATTR_IMMEDIATE, "This command applies immediately" }, + { CMD_ATTR_NODE_EXIT, "This command applies on VTY node exit" }, + /* CMD_ATTR_LIB_COMMAND is intentionally skipped */ + { 0, NULL } +}; + +/* Public attributes (to be printed in the VTY / XML reference) */ +#define CMD_ATTR_PUBLIC_MASK \ + (CMD_ATTR_HIDDEN | CMD_ATTR_IMMEDIATE | CMD_ATTR_NODE_EXIT) + +/* Get a flag character for a global VTY command attribute */ +static char cmd_attr_get_flag(unsigned int attr) +{ + switch (attr) { + case CMD_ATTR_HIDDEN: + return '^'; + case CMD_ATTR_IMMEDIATE: + return '!'; + case CMD_ATTR_NODE_EXIT: + return '@'; + default: + return '.'; + } +} + +/* Description of attributes shared between the lib commands */ +static const char * const cmd_lib_attr_desc[32] = { + /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = \ + * "Brief but meaningful description", */ + [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = \ + "This command applies on ASP restart", + [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = \ + "This command applies on IPA link establishment", + [OSMO_ABIS_LIB_ATTR_LINE_UPD] = \ + "This command applies on E1 line update", +}; + +/* Flag letters of attributes shared between the lib commands. + * NOTE: uppercase letters only, the rest is reserved for applications. */ +static const char cmd_lib_attr_letters[32] = { + /* [OSMO_LIBNAME_LIB_ATTR_ATTRNAME] = 'X', */ + [OSMO_SCCP_LIB_ATTR_RSTRT_ASP] = 'A', + [OSMO_ABIS_LIB_ATTR_IPA_NEW_LNK] = 'I', + [OSMO_ABIS_LIB_ATTR_LINE_UPD] = 'L', +}; + /* * Write one cmd_element as XML via a print_func_t. */ -static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, void *data, const char *newline) +static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_func, + void *data, const char *newline) { char *xml_string = xml_escape(cmd->string); + unsigned int i; print_func(data, " <command id='%s'>%s", xml_string, newline); + + /* Print global attributes and their description */ + if (cmd->attr & CMD_ATTR_PUBLIC_MASK) { /* ... only public ones */ + print_func(data, " <attributes scope='global'>%s", newline); + + for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) { + char *xml_att_desc; + char flag; + + if (~cmd->attr & cmd_attr_desc[i].value) + continue; + + xml_att_desc = xml_escape(cmd_attr_desc[i].str); + print_func(data, " <attribute doc='%s'", + xml_att_desc, newline); + talloc_free(xml_att_desc); + + flag = cmd_attr_get_flag(cmd_attr_desc[i].value); + if (flag != '.') + print_func(data, " flag='%c'", flag); + print_func(data, " />%s", newline); + } + + print_func(data, " </attributes>%s", newline); + } + + /* Print application specific attributes and their description */ + if (cmd->usrattr != 0x00) { /* ... if at least one flag is set */ + const char * const *desc; + const char *letters; + + if (cmd->attr & CMD_ATTR_LIB_COMMAND) { + print_func(data, " <attributes scope='library'>%s", newline); + letters = &cmd_lib_attr_letters[0]; + desc = &cmd_lib_attr_desc[0]; + } else { + print_func(data, " <attributes scope='application'>%s", newline); + letters = &host.app_info->usr_attr_letters[0]; + desc = &host.app_info->usr_attr_desc[0]; + } + + for (i = 0; i < ARRAY_SIZE(host.app_info->usr_attr_desc); i++) { + char *xml_att_desc; + char flag; + + /* Skip attribute if *not* set */ + if (~cmd->usrattr & ((unsigned)1 << i)) + continue; + + xml_att_desc = xml_escape(desc[i]); + print_func(data, " <attribute doc='%s'", xml_att_desc); + talloc_free(xml_att_desc); + + if ((flag = letters[i]) != '\0') + print_func(data, " flag='%c'", flag); + print_func(data, " />%s", newline); + } + + print_func(data, " </attributes>%s", newline); + } + print_func(data, " <params>%s", newline); - int j; - for (j = 0; j < vector_count(cmd->strvec); ++j) { - vector descvec = vector_slot(cmd->strvec, j); - int i; - for (i = 0; i < vector_active(descvec); ++i) { + for (i = 0; i < vector_count(cmd->strvec); ++i) { + vector descvec = vector_slot(cmd->strvec, i); + int j; + for (j = 0; j < vector_active(descvec); ++j) { char *xml_param, *xml_doc; - struct desc *desc = vector_slot(descvec, i); + struct desc *desc = vector_slot(descvec, j); if (desc == NULL) continue; @@ -657,12 +791,27 @@ static int vty_dump_element(struct cmd_element *cmd, print_func_t print_func, vo return 0; } -static bool vty_command_is_common(struct cmd_element *cmd); +static bool vty_command_is_common(const struct cmd_element *cmd); /* * Dump all nodes and commands associated with a given node as XML via a print_func_t. + * + * (gflag_mask, match = false) - print only those commands with non-matching flags. + * (gflag_mask, match = true) - print only those commands with matching flags. + * + * Some examples: + * + * Print all commands except deprecated and hidden (default mode): + * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, false) + * Print only deprecated and hidden commands: + * (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN, true) + * Print all commands except deprecated (expert mode): + * (CMD_ATTR_DEPRECATED, false) + * Print only hidden commands: + * (CMD_ATTR_HIDDEN, true) */ -static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline) +static int vty_dump_nodes(print_func_t print_func, void *data, const char *newline, + unsigned char gflag_mask, bool match) { int i, j; int same_name_count; @@ -675,27 +824,27 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli print_func(data, " <description>These commands are available on all VTY nodes. They are listed" " here only once, to unclutter the VTY reference.</description>%s", newline); for (i = 0; i < vector_active(cmdvec); ++i) { - struct cmd_node *cnode; - cnode = vector_slot(cmdvec, i); + const struct cmd_node *cnode = vector_slot(cmdvec, i); if (!cnode) continue; if (cnode->node != CONFIG_NODE) continue; for (j = 0; j < vector_active(cnode->cmd_vector); ++j) { - struct cmd_element *elem; - elem = vector_slot(cnode->cmd_vector, j); + const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j); if (!vty_command_is_common(elem)) continue; - if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) - vty_dump_element(elem, print_func, data, newline); + if (!match && (elem->attr & gflag_mask) != 0x00) + continue; + if (match && (elem->attr & gflag_mask) == 0x00) + continue; + vty_dump_element(elem, print_func, data, newline); } } print_func(data, " </node>%s", newline); for (i = 0; i < vector_active(cmdvec); ++i) { - struct cmd_node *cnode; - cnode = vector_slot(cmdvec, i); + const struct cmd_node *cnode = vector_slot(cmdvec, i); if (!cnode) continue; if (vector_active(cnode->cmd_vector) < 1) @@ -706,8 +855,7 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli * 'name', the second becomes 'name_2', then 'name_3', ... */ same_name_count = 1; for (j = 0; j < i; ++j) { - struct cmd_node *cnode2; - cnode2 = vector_slot(cmdvec, j); + const struct cmd_node *cnode2 = vector_slot(cmdvec, j); if (!cnode2) continue; if (strcmp(cnode->name, cnode2->name) == 0) @@ -721,12 +869,14 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli print_func(data, " <name>%s</name>%s", cnode->name, newline); for (j = 0; j < vector_active(cnode->cmd_vector); ++j) { - struct cmd_element *elem; - elem = vector_slot(cnode->cmd_vector, j); + const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j); if (vty_command_is_common(elem)) continue; - if (!(elem->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) - vty_dump_element(elem, print_func, data, newline); + if (!match && (elem->attr & gflag_mask) != 0x00) + continue; + if (match && (elem->attr & gflag_mask) == 0x00) + continue; + vty_dump_element(elem, print_func, data, newline); } print_func(data, " </node>%s", newline); @@ -750,7 +900,10 @@ static int print_func_vty(void *data, const char *format, ...) static int vty_dump_xml_ref_to_vty(struct vty *vty) { - return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE); + unsigned char gflag_mask = CMD_ATTR_DEPRECATED; + if (!vty->expert_mode) + gflag_mask |= CMD_ATTR_HIDDEN; + return vty_dump_nodes(print_func_vty, vty, VTY_NEWLINE, gflag_mask, false); } static int print_func_stream(void *data, const char *format, ...) @@ -763,11 +916,61 @@ static int print_func_stream(void *data, const char *format, ...) return rc; } +const struct value_string vty_ref_gen_mode_names[] = { + { VTY_REF_GEN_MODE_DEFAULT, "default" }, + { VTY_REF_GEN_MODE_EXPERT, "expert" }, + { VTY_REF_GEN_MODE_HIDDEN, "hidden" }, + { 0, NULL } +}; + +const struct value_string vty_ref_gen_mode_desc[] = { + { VTY_REF_GEN_MODE_DEFAULT, "all commands except deprecated and hidden" }, + { VTY_REF_GEN_MODE_EXPERT, "all commands including hidden, excluding deprecated" }, + { VTY_REF_GEN_MODE_HIDDEN, "hidden commands only" }, + { 0, NULL } +}; + +/*! Print the XML reference of all VTY nodes to the given stream. + * \param[out] stream Output stream to print the XML reference to. + * \param[in] mode The XML reference generation mode. + * \returns always 0 for now, no errors possible. + */ +int vty_dump_xml_ref_mode(FILE *stream, enum vty_ref_gen_mode mode) +{ + unsigned char gflag_mask; + bool match = false; + + switch (mode) { + case VTY_REF_GEN_MODE_EXPERT: + /* All commands except deprecated */ + gflag_mask = CMD_ATTR_DEPRECATED; + break; + case VTY_REF_GEN_MODE_HIDDEN: + /* Only hidden commands */ + gflag_mask = CMD_ATTR_HIDDEN; + match = true; + break; + case VTY_REF_GEN_MODE_DEFAULT: + default: + /* All commands except deprecated and hidden */ + gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN; + break; + } + + return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match); +} + /*! Print the XML reference of all VTY nodes to the given stream. + * \param[out] stream Output stream to print the XML reference to. + * \returns always 0 for now, no errors possible. + * + * NOTE: this function is deprecated because it does not allow to + * specify the XML reference generation mode (default mode + * is hard-coded). Use vty_dump_xml_ref_mode() instead. */ int vty_dump_xml_ref(FILE *stream) { - return vty_dump_nodes(print_func_stream, stream, "\n"); + return vty_dump_xml_ref_mode(stream, VTY_REF_GEN_MODE_DEFAULT); } /* Check if a command with given string exists at given node */ @@ -807,6 +1010,16 @@ void install_element(int ntype, struct cmd_element *cmd) cmd->cmdsize = cmd_cmdsize(cmd->strvec); } +/*! Install a library command into a node + * \param[in] ntype Node Type + * \param[in] cmd element to be installed + */ +void install_lib_element(int ntype, struct cmd_element *cmd) +{ + cmd->attr |= CMD_ATTR_LIB_COMMAND; + install_element(ntype, cmd); +} + /* Install a command into VIEW and ENABLE node */ void install_element_ve(struct cmd_element *cmd) { @@ -814,6 +1027,13 @@ void install_element_ve(struct cmd_element *cmd) install_element(ENABLE_NODE, cmd); } +/* Install a library command into VIEW and ENABLE node */ +void install_lib_element_ve(struct cmd_element *cmd) +{ + cmd->attr |= CMD_ATTR_LIB_COMMAND; + install_element_ve(cmd); +} + #ifdef VTY_CRYPT_PW static unsigned char itoa64[] = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; @@ -887,6 +1107,7 @@ static int config_write_host(struct vty *vty) static vector cmd_node_vector(vector v, enum node_type ntype) { struct cmd_node *cnode = vector_slot(v, ntype); + OSMO_ASSERT(cnode != NULL); return cnode->cmd_vector; } @@ -1144,7 +1365,6 @@ static enum match_type cmd_ipv6_prefix_match(const char *str) int colons = 0, nums = 0, double_colon = 0; int mask; const char *sp = NULL; - char *endptr = NULL; if (str == NULL) return PARTLY_MATCH; @@ -1242,11 +1462,7 @@ static enum match_type cmd_ipv6_prefix_match(const char *str) if (state < STATE_MASK) return PARTLY_MATCH; - mask = strtol(str, &endptr, 10); - if (*endptr != '\0') - return NO_MATCH; - - if (mask < 0 || mask > 128) + if (osmo_str_to_int(&mask, str, 10, 0, 128)) return NO_MATCH; /* I don't know why mask < 13 makes command match partly. @@ -1262,21 +1478,69 @@ static enum match_type cmd_ipv6_prefix_match(const char *str) #endif /* HAVE_IPV6 */ -#define DECIMAL_STRLEN_MAX 10 -static int cmd_range_match(const char *range, const char *str) +#if ULONG_MAX == 18446744073709551615UL +#define DECIMAL_STRLEN_MAX_UNSIGNED 20 +#elif ULONG_MAX == 4294967295UL +#define DECIMAL_STRLEN_MAX_UNSIGNED 10 +#else +#error "ULONG_MAX not defined!" +#endif + +#if LONG_MAX == 9223372036854775807L +#define DECIMAL_STRLEN_MAX_SIGNED 19 +#elif LONG_MAX == 2147483647L +#define DECIMAL_STRLEN_MAX_SIGNED 10 +#else +#error "LONG_MAX not defined!" +#endif + +/* This function is aimed at quickly guessing & filtering the numeric base a + * string can contain, by no means validates the entire value. + * Returns 16 if string follows pattern "({+,-}0x[digits])" + * Returns 10 if string follows pattern "({+,-}[digits])" + * Returns a negative value if something other is detected (error) +*/ +static int check_base(const char *str) +{ + const char *ptr = str; + /* Skip any space */ + while (isspace(*ptr)) ptr++; + + /* Skip optional sign: */ + if (*ptr == '+' || *ptr == '-') + ptr++; + if (*ptr == '0') { + ptr++; + if (*ptr == '\0' || isdigit(*ptr)) + return 10; + if (!(*ptr == 'x' || *ptr == 'X')) + return -1; + ptr++; + if (isxdigit(*ptr)) + return 16; + return -1; + } + return 10; +} + +int vty_cmd_range_match(const char *range, const char *str) { char *p; - char buf[DECIMAL_STRLEN_MAX + 1]; + char buf[DECIMAL_STRLEN_MAX_UNSIGNED + 1]; char *endptr = NULL; + int min_base, max_base, val_base; if (str == NULL) return 1; + if ((val_base = check_base(str)) < 0) + return 0; + if (range[1] == '-') { signed long min = 0, max = 0, val; - val = strtol(str, &endptr, 10); + val = strtol(str, &endptr, val_base); if (*endptr != '\0') return 0; @@ -1284,11 +1548,13 @@ static int cmd_range_match(const char *range, const char *str) p = strchr(range, '-'); if (p == NULL) return 0; - if (p - range > DECIMAL_STRLEN_MAX) + if (p - range > DECIMAL_STRLEN_MAX_SIGNED) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; - min = -strtol(buf, &endptr, 10); + if ((min_base = check_base(buf)) < 0) + return 0; + min = -strtol(buf, &endptr, min_base); if (*endptr != '\0') return 0; @@ -1296,11 +1562,13 @@ static int cmd_range_match(const char *range, const char *str) p = strchr(range, '>'); if (p == NULL) return 0; - if (p - range > DECIMAL_STRLEN_MAX) + if (p - range > DECIMAL_STRLEN_MAX_SIGNED) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; - max = strtol(buf, &endptr, 10); + if ((max_base = check_base(buf)) < 0) + return 0; + max = strtol(buf, &endptr, max_base); if (*endptr != '\0') return 0; @@ -1309,7 +1577,10 @@ static int cmd_range_match(const char *range, const char *str) } else { unsigned long min, max, val; - val = strtoul(str, &endptr, 10); + if (str[0] == '-') + return 0; + + val = strtoul(str, &endptr, val_base); if (*endptr != '\0') return 0; @@ -1317,11 +1588,13 @@ static int cmd_range_match(const char *range, const char *str) p = strchr(range, '-'); if (p == NULL) return 0; - if (p - range > DECIMAL_STRLEN_MAX) + if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; - min = strtoul(buf, &endptr, 10); + if ((min_base = check_base(buf)) < 0) + return 0; + min = strtoul(buf, &endptr, min_base); if (*endptr != '\0') return 0; @@ -1329,11 +1602,13 @@ static int cmd_range_match(const char *range, const char *str) p = strchr(range, '>'); if (p == NULL) return 0; - if (p - range > DECIMAL_STRLEN_MAX) + if (p - range > DECIMAL_STRLEN_MAX_UNSIGNED) return 0; strncpy(buf, range, p - range); buf[p - range] = '\0'; - max = strtoul(buf, &endptr, 10); + if ((max_base = check_base(buf)) < 0) + return 0; + max = strtoul(buf, &endptr, max_base); if (*endptr != '\0') return 0; @@ -1341,6 +1616,14 @@ static int cmd_range_match(const char *range, const char *str) return 0; } + /* Don't allow ranges specified by min and max with different bases */ + if (min_base != max_base) + return 0; + /* arg value passed must match the base of the range */ + if (min_base != val_base) + return 0; + + /* Everything's fine */ return 1; } @@ -1384,7 +1667,7 @@ cmd_match(const char *str, const char *command, return VARARG_MATCH; else if (CMD_RANGE(str)) { - if (cmd_range_match(str, command)) + if (vty_cmd_range_match(str, command)) return RANGE_MATCH; } #ifdef HAVE_IPV6 @@ -1581,7 +1864,7 @@ is_cmd_ambiguous(char *command, vector v, int index, enum match_type type) } break; case RANGE_MATCH: - if (cmd_range_match + if (vty_cmd_range_match (str, command)) { if (matched && strcmp(matched, @@ -1674,7 +1957,7 @@ static const char *cmd_entry_function_desc(const char *src, const char *dst) return dst; if (CMD_RANGE(dst)) { - if (cmd_range_match(dst, src)) + if (vty_cmd_range_match(dst, src)) return dst; else return NULL; @@ -1863,7 +2146,9 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status) if (!cmd_element) continue; - if (cmd_element->attr & (CMD_ATTR_DEPRECATED|CMD_ATTR_HIDDEN)) + if (cmd_element->attr & CMD_ATTR_DEPRECATED) + continue; + if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN)) continue; strvec = cmd_element->strvec; @@ -2168,6 +2453,7 @@ static bool vty_pop_parent(struct vty *vty) llist_del(&parent->entry); vty->node = parent->node; vty->priv = parent->priv; + vty->index = parent->index; if (vty->indent) talloc_free(vty->indent); vty->indent = parent->indent; @@ -2198,23 +2484,23 @@ static void vty_clear_parents(struct vty *vty) int vty_go_parent(struct vty *vty) { switch (vty->node) { - case AUTH_NODE: - case VIEW_NODE: - case ENABLE_NODE: - case CONFIG_NODE: - vty_clear_parents(vty); - break; + case AUTH_NODE: + case VIEW_NODE: + case ENABLE_NODE: + case CONFIG_NODE: + vty_clear_parents(vty); + break; - case AUTH_ENABLE_NODE: - vty->node = VIEW_NODE; - vty_clear_parents(vty); - break; + case AUTH_ENABLE_NODE: + vty->node = VIEW_NODE; + vty_clear_parents(vty); + break; - default: - if (host.app_info->go_parent_cb) - host.app_info->go_parent_cb(vty); - vty_pop_parent(vty); - break; + default: + if (host.app_info->go_parent_cb) + host.app_info->go_parent_cb(vty); + vty_pop_parent(vty); + break; } return vty->node; @@ -2378,6 +2664,7 @@ cmd_execute_command_real(vector vline, struct vty *vty, struct vty_parent_node this_node = { .node = vty->node, .priv = vty->priv, + .index = vty->index, .indent = vty->indent, }; struct vty_parent_node *parent = vty_parent(vty); @@ -2653,6 +2940,7 @@ int config_from_file(struct vty *vty, FILE * fp) this_node = (struct vty_parent_node){ .node = vty->node, .priv = vty->priv, + .index = vty->index, .indent = vty->indent, }; @@ -2709,7 +2997,7 @@ return_invalid_indent: /* Configration from terminal */ DEFUN(config_terminal, config_terminal_cmd, - "configure terminal", + "configure [terminal]", "Configuration from vty interface\n" "Configuration terminal\n") { if (vty_config_lock(vty)) @@ -2723,7 +3011,10 @@ DEFUN(config_terminal, } /* Enable command */ -DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") +DEFUN(enable, config_enable_cmd, + "enable [expert-mode]", + "Turn on privileged mode command\n" + "Enable the expert mode (show hidden commands)\n") { /* If enable password is NULL, change to ENABLE_NODE */ if ((host.enable == NULL && host.enable_encrypt == NULL) || @@ -2732,6 +3023,8 @@ DEFUN(enable, config_enable_cmd, "enable", "Turn on privileged mode command\n") else vty->node = AUTH_ENABLE_NODE; + vty->expert_mode = argc > 0; + return CMD_SUCCESS; } @@ -2741,6 +3034,9 @@ DEFUN(disable, { if (vty->node == ENABLE_NODE) vty->node = VIEW_NODE; + + vty->expert_mode = false; + return CMD_SUCCESS; } @@ -2792,6 +3088,18 @@ gDEFUN(config_exit, return CMD_SUCCESS; } +DEFUN(shutdown, + shutdown_cmd, "shutdown", "Request a shutdown of the program\n") +{ + LOGP(DLGLOBAL, LOGL_INFO, "Shutdown requested from telnet\n"); + vty_out(vty, "%s is shutting down. Bye!%s", host.app_info->name, VTY_NEWLINE); + + /* Same exit path as if it was killed by the service manager */ + kill(getpid(), SIGTERM); + + return CMD_SUCCESS; +} + /* Show version. */ DEFUN(show_version, show_version_cmd, "show version", SHOW_STR "Displays program version\n") @@ -2811,38 +3119,256 @@ DEFUN(show_online_help, return CMD_SUCCESS; } +DEFUN(show_pid, + show_pid_cmd, "show pid", SHOW_STR "Displays the process ID\n") +{ + vty_out(vty, "%d%s", getpid(), VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN(show_uptime, + show_uptime_cmd, "show uptime", SHOW_STR "Displays how long the program has been running\n") +{ + vty_out(vty, "%s has been running for ", host.app_info->name); + vty_out_uptime(vty, &starttime); + vty_out_newline(vty); + + return CMD_SUCCESS; +} + /* Help display function for all node. */ gDEFUN(config_help, config_help_cmd, "help", "Description of the interactive help system\n") { - vty_out(vty, - "This VTY provides advanced help features. When you need help,%s\ -anytime at the command line please press '?'.%s\ -%s\ -If nothing matches, the help list will be empty and you must backup%s\ - until entering a '?' shows the available options.%s\ -Two styles of help are provided:%s\ -1. Full help is available when you are ready to enter a%s\ -command argument (e.g. 'show ?') and describes each possible%s\ -argument.%s\ -2. Partial help is provided when an abbreviated argument is entered%s\ - and you want to know what arguments match the input%s\ - (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "This VTY provides advanced help features. When you need help,%s" + "anytime at the command line please press '?'.%s%s" + "If nothing matches, the help list will be empty and you must backup%s" + " until entering a '?' shows the available options.%s" + "Two styles of help are provided:%s" + "1. Full help is available when you are ready to enter a%s" + "command argument (e.g. 'show ?') and describes each possible%s" + "argument.%s" + "2. Partial help is provided when an abbreviated argument is entered%s" + " and you want to know what arguments match the input%s" + " (e.g. 'show me?'.)%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, + VTY_NEWLINE); + return CMD_SUCCESS; +} + +enum { + ATTR_TYPE_GLOBAL = (1 << 0), + ATTR_TYPE_LIB = (1 << 1), + ATTR_TYPE_APP = (1 << 2), +}; + +static void print_attr_list(struct vty *vty, unsigned int attr_mask) +{ + const char *desc; + unsigned int i; + bool found; + char flag; + + if (attr_mask & ATTR_TYPE_GLOBAL) { + vty_out(vty, " Global attributes:%s", VTY_NEWLINE); + + for (i = 0; i < ARRAY_SIZE(cmd_attr_desc) - 1; i++) { + flag = cmd_attr_get_flag(cmd_attr_desc[i].value); + desc = cmd_attr_desc[i].str; + + /* Skip attributes without flags */ + if (flag != '.') + vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE); + } + } + + if (attr_mask & ATTR_TYPE_LIB) { + vty_out(vty, " Library specific attributes:%s", VTY_NEWLINE); + + for (i = 0, found = false; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) { + if ((desc = cmd_lib_attr_desc[i]) == NULL) + continue; + found = true; + + flag = cmd_lib_attr_letters[i]; + if (flag == '\0') + flag = '.'; + + vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE); + } + + if (!found) + vty_out(vty, " (no attributes)%s", VTY_NEWLINE); + } + + if (attr_mask & ATTR_TYPE_APP) { + vty_out(vty, " Application specific attributes:%s", VTY_NEWLINE); + + for (i = 0, found = false; i < VTY_CMD_USR_ATTR_NUM; i++) { + if ((desc = host.app_info->usr_attr_desc[i]) == NULL) + continue; + found = true; + + flag = host.app_info->usr_attr_letters[i]; + if (flag == '\0') + flag = '.'; + + vty_out(vty, " %c %s%s", flag, desc, VTY_NEWLINE); + } + + if (!found) + vty_out(vty, " (no attributes)%s", VTY_NEWLINE); + } +} + +gDEFUN(show_vty_attr_all, show_vty_attr_all_cmd, + "show vty-attributes", + SHOW_STR "List of VTY attributes\n") +{ + print_attr_list(vty, 0xff); + return CMD_SUCCESS; +} + +gDEFUN(show_vty_attr, show_vty_attr_cmd, + "show vty-attributes (application|library|global)", + SHOW_STR "List of VTY attributes\n" + "Application specific attributes only\n" + "Library specific attributes only\n" + "Global attributes only\n") +{ + unsigned int attr_mask = 0; + + if (argv[0][0] == 'g') /* global */ + attr_mask |= ATTR_TYPE_GLOBAL; + else if (argv[0][0] == 'l') /* library */ + attr_mask |= ATTR_TYPE_LIB; + else if (argv[0][0] == 'a') /* application */ + attr_mask |= ATTR_TYPE_APP; + + print_attr_list(vty, attr_mask); return CMD_SUCCESS; } +/* Compose flag bit-mask for all commands within the given node */ +static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode) +{ + unsigned int flag_mask = 0x00; + unsigned int f, i; + + for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) { + for (i = 0; i < vector_active(cnode->cmd_vector); i++) { + const struct cmd_element *cmd; + char flag_letter; + + if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL) + continue; + if (cmd->attr & CMD_ATTR_DEPRECATED) + continue; + if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) + continue; + if (~cmd->usrattr & ((unsigned)1 << f)) + continue; + + if (cmd->attr & CMD_ATTR_LIB_COMMAND) + flag_letter = cmd_lib_attr_letters[f]; + else + flag_letter = host.app_info->usr_attr_letters[f]; + + if (flag_letter == '\0') + continue; + + flag_mask |= (1 << f); + break; + } + } + + return flag_mask; +} + +/* Compose global flag char-mask for the given command (e.g. "!" or "@") */ +static const char *cmd_gflag_mask(const struct cmd_element *cmd) +{ + static char char_mask[8 + 1]; + char *ptr = &char_mask[0]; + + /* Mutually exclusive global attributes */ + if (cmd->attr & CMD_ATTR_HIDDEN) + *(ptr++) = cmd_attr_get_flag(CMD_ATTR_HIDDEN); + else if (cmd->attr & CMD_ATTR_IMMEDIATE) + *(ptr++) = cmd_attr_get_flag(CMD_ATTR_IMMEDIATE); + else if (cmd->attr & CMD_ATTR_NODE_EXIT) + *(ptr++) = cmd_attr_get_flag(CMD_ATTR_NODE_EXIT); + else + *(ptr++) = '.'; + + *ptr = '\0'; + + return char_mask; +} + +/* Compose app / lib flag char-mask for the given command (e.g. ".F.OB..") */ +static const char *cmd_flag_mask(const struct cmd_element *cmd, + unsigned int flag_mask) +{ + static char char_mask[VTY_CMD_USR_ATTR_NUM + 1]; + char *ptr = &char_mask[0]; + char flag_letter; + unsigned int f; + + for (f = 0; f < VTY_CMD_USR_ATTR_NUM; f++) { + if (~flag_mask & ((unsigned)1 << f)) + continue; + if (~cmd->usrattr & ((unsigned)1 << f)) { + *(ptr++) = '.'; + continue; + } + + if (cmd->attr & CMD_ATTR_LIB_COMMAND) + flag_letter = cmd_lib_attr_letters[f]; + else + flag_letter = host.app_info->usr_attr_letters[f]; + + *(ptr++) = flag_letter ? flag_letter : '.'; + } + + *ptr = '\0'; + + return char_mask; +} + /* Help display function for all node. */ -gDEFUN(config_list, config_list_cmd, "list", "Print command list\n") +gDEFUN(config_list, config_list_cmd, + "list [with-flags]", + "Print command list\n" + "Also print the VTY attribute flags\n") { unsigned int i; struct cmd_node *cnode = vector_slot(cmdvec, vty->node); + unsigned int flag_mask = 0x00; struct cmd_element *cmd; - for (i = 0; i < vector_active(cnode->cmd_vector); i++) - if ((cmd = vector_slot(cnode->cmd_vector, i)) != NULL - && !(cmd->attr & (CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN))) + if (argc > 0) + flag_mask = node_flag_mask(cnode, vty->expert_mode); + + for (i = 0; i < vector_active(cnode->cmd_vector); i++) { + if ((cmd = vector_slot(cnode->cmd_vector, i)) == NULL) + continue; + if (cmd->attr & CMD_ATTR_DEPRECATED) + continue; + if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) + continue; + if (!argc) vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); + else { + vty_out(vty, " %s %s %s%s", + cmd_gflag_mask(cmd), + cmd_flag_mask(cmd, flag_mask), + cmd->string, VTY_NEWLINE); + } + } + return CMD_SUCCESS; } @@ -3331,16 +3857,7 @@ DEFUN(config_terminal_length, config_terminal_length_cmd, "Set number of lines on a screen\n" "Number of lines on screen (0 for no pausing)\n") { - int lines; - char *endptr = NULL; - - lines = strtol(argv[0], &endptr, 10); - if (lines < 0 || lines > 512 || *endptr != '\0') { - vty_out(vty, "length is malformed%s", VTY_NEWLINE); - return CMD_WARNING; - } - vty->lines = lines; - + vty->lines = atoi(argv[0]); return CMD_SUCCESS; } @@ -3359,16 +3876,7 @@ DEFUN(service_terminal_length, service_terminal_length_cmd, "System wide terminal length configuration\n" "Number of lines of VTY (0 means no line control)\n") { - int lines; - char *endptr = NULL; - - lines = strtol(argv[0], &endptr, 10); - if (lines < 0 || lines > 512 || *endptr != '\0') { - vty_out(vty, "length is malformed%s", VTY_NEWLINE); - return CMD_WARNING; - } - host.lines = lines; - + host.lines = atoi(argv[0]); return CMD_SUCCESS; } @@ -3789,7 +4297,12 @@ DEFUN(no_banner_motd, /* Set config filename. Called from vty.c */ void host_config_set(const char *filename) { - host.config = talloc_strdup(tall_vty_cmd_ctx, filename); + osmo_talloc_replace_string(tall_vty_cmd_ctx, &host.config, filename); +} + +const char *host_config_file(void) +{ + return host.config; } /*! Deprecated, now happens implicitly when calling install_node(). @@ -3809,29 +4322,34 @@ void vty_install_default(int node) /*! Install common commands like 'exit' and 'list'. */ static void install_basic_node_commands(int node) { - install_element(node, &config_help_cmd); - install_element(node, &config_list_cmd); + install_lib_element(node, &config_help_cmd); + install_lib_element(node, &config_list_cmd); - install_element(node, &config_write_terminal_cmd); - install_element(node, &config_write_file_cmd); - install_element(node, &config_write_memory_cmd); - install_element(node, &config_write_cmd); - install_element(node, &show_running_config_cmd); + install_lib_element(node, &show_vty_attr_all_cmd); + install_lib_element(node, &show_vty_attr_cmd); - install_element(node, &config_exit_cmd); + install_lib_element(node, &config_write_terminal_cmd); + install_lib_element(node, &config_write_file_cmd); + install_lib_element(node, &config_write_memory_cmd); + install_lib_element(node, &config_write_cmd); + install_lib_element(node, &show_running_config_cmd); + + install_lib_element(node, &config_exit_cmd); if (node >= CONFIG_NODE) { /* It's not a top node. */ - install_element(node, &config_end_cmd); + install_lib_element(node, &config_end_cmd); } } /*! Return true if a node is installed by install_basic_node_commands(), so * that we can avoid repeating them for each and every node during 'show * running-config' */ -static bool vty_command_is_common(struct cmd_element *cmd) +static bool vty_command_is_common(const struct cmd_element *cmd) { if (cmd == &config_help_cmd + || cmd == &show_vty_attr_all_cmd + || cmd == &show_vty_attr_cmd || cmd == &config_list_cmd || cmd == &config_write_terminal_cmd || cmd == &config_write_file_cmd @@ -3909,55 +4427,92 @@ void cmd_init(int terminal) install_node(&config_node, config_write_host); /* Each node's basic commands. */ - install_element(VIEW_NODE, &show_version_cmd); - install_element(VIEW_NODE, &show_online_help_cmd); + install_lib_element(VIEW_NODE, &show_pid_cmd); + install_lib_element(VIEW_NODE, &show_uptime_cmd); + install_lib_element(VIEW_NODE, &show_version_cmd); + install_lib_element(VIEW_NODE, &show_online_help_cmd); if (terminal) { - install_element(VIEW_NODE, &config_list_cmd); - install_element(VIEW_NODE, &config_exit_cmd); - install_element(VIEW_NODE, &config_help_cmd); - install_element(VIEW_NODE, &config_enable_cmd); - install_element(VIEW_NODE, &config_terminal_length_cmd); - install_element(VIEW_NODE, &config_terminal_no_length_cmd); - install_element(VIEW_NODE, &echo_cmd); + install_lib_element(VIEW_NODE, &config_list_cmd); + install_lib_element(VIEW_NODE, &config_exit_cmd); + install_lib_element(VIEW_NODE, &config_help_cmd); + install_lib_element(VIEW_NODE, &show_vty_attr_all_cmd); + install_lib_element(VIEW_NODE, &show_vty_attr_cmd); + install_lib_element(VIEW_NODE, &config_enable_cmd); + install_lib_element(VIEW_NODE, &config_terminal_length_cmd); + install_lib_element(VIEW_NODE, &config_terminal_no_length_cmd); + install_lib_element(VIEW_NODE, &echo_cmd); } if (terminal) { - install_element(ENABLE_NODE, &config_disable_cmd); - install_element(ENABLE_NODE, &config_terminal_cmd); - install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + install_lib_element(ENABLE_NODE, &config_disable_cmd); + install_lib_element(ENABLE_NODE, &config_terminal_cmd); + install_lib_element(ENABLE_NODE, ©_runningconfig_startupconfig_cmd); + install_lib_element(ENABLE_NODE, &shutdown_cmd); } - install_element (ENABLE_NODE, &show_startup_config_cmd); - install_element(ENABLE_NODE, &show_version_cmd); - install_element(ENABLE_NODE, &show_online_help_cmd); + install_lib_element(ENABLE_NODE, &show_startup_config_cmd); + install_lib_element(ENABLE_NODE, &show_version_cmd); + install_lib_element(ENABLE_NODE, &show_online_help_cmd); if (terminal) { - install_element(ENABLE_NODE, &config_terminal_length_cmd); - install_element(ENABLE_NODE, &config_terminal_no_length_cmd); - install_element(ENABLE_NODE, &echo_cmd); + install_lib_element(ENABLE_NODE, &config_terminal_length_cmd); + install_lib_element(ENABLE_NODE, &config_terminal_no_length_cmd); + install_lib_element(ENABLE_NODE, &echo_cmd); } - install_element(CONFIG_NODE, &hostname_cmd); - install_element(CONFIG_NODE, &no_hostname_cmd); + install_lib_element(CONFIG_NODE, &hostname_cmd); + install_lib_element(CONFIG_NODE, &no_hostname_cmd); if (terminal) { - install_element(CONFIG_NODE, &password_cmd); - install_element(CONFIG_NODE, &password_text_cmd); - install_element(CONFIG_NODE, &enable_password_cmd); - install_element(CONFIG_NODE, &enable_password_text_cmd); - install_element(CONFIG_NODE, &no_enable_password_cmd); + install_lib_element(CONFIG_NODE, &password_cmd); + install_lib_element(CONFIG_NODE, &password_text_cmd); + install_lib_element(CONFIG_NODE, &enable_password_cmd); + install_lib_element(CONFIG_NODE, &enable_password_text_cmd); + install_lib_element(CONFIG_NODE, &no_enable_password_cmd); #ifdef VTY_CRYPT_PW - install_element(CONFIG_NODE, &service_password_encrypt_cmd); - install_element(CONFIG_NODE, &no_service_password_encrypt_cmd); + install_lib_element(CONFIG_NODE, &service_password_encrypt_cmd); + install_lib_element(CONFIG_NODE, &no_service_password_encrypt_cmd); #endif - install_element(CONFIG_NODE, &banner_motd_default_cmd); - install_element(CONFIG_NODE, &banner_motd_file_cmd); - install_element(CONFIG_NODE, &no_banner_motd_cmd); - install_element(CONFIG_NODE, &service_terminal_length_cmd); - install_element(CONFIG_NODE, &no_service_terminal_length_cmd); + install_lib_element(CONFIG_NODE, &banner_motd_default_cmd); + install_lib_element(CONFIG_NODE, &banner_motd_file_cmd); + install_lib_element(CONFIG_NODE, &no_banner_motd_cmd); + install_lib_element(CONFIG_NODE, &service_terminal_length_cmd); + install_lib_element(CONFIG_NODE, &no_service_terminal_length_cmd); } srand(time(NULL)); } +static __attribute__((constructor)) void on_dso_load_starttime(void) +{ + osmo_clock_gettime(CLOCK_MONOTONIC, &starttime); +} + +/* FIXME: execute this section in the unit test instead */ +static __attribute__((constructor)) void on_dso_load(void) +{ + unsigned int i, j; + + /* Check total number of the library specific attributes */ + OSMO_ASSERT(_OSMO_CORE_LIB_ATTR_COUNT < 32); + + /* Check for duplicates in the list of library specific flags */ + for (i = 0; i < _OSMO_CORE_LIB_ATTR_COUNT; i++) { + if (cmd_lib_attr_letters[i] == '\0') + continue; + + /* Some flag characters are reserved for global attributes */ + const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED; + for (j = 0; j < ARRAY_SIZE(rafc); j++) + OSMO_ASSERT(cmd_lib_attr_letters[i] != rafc[j]); + + /* Only upper case flag letters are allowed for libraries */ + OSMO_ASSERT(cmd_lib_attr_letters[i] >= 'A'); + OSMO_ASSERT(cmd_lib_attr_letters[i] <= 'Z'); + + for (j = i + 1; j < _OSMO_CORE_LIB_ATTR_COUNT; j++) + OSMO_ASSERT(cmd_lib_attr_letters[i] != cmd_lib_attr_letters[j]); + } +} + /*! @} */ diff --git a/src/vty/cpu_sched_vty.c b/src/vty/cpu_sched_vty.c new file mode 100644 index 00000000..7198f747 --- /dev/null +++ b/src/vty/cpu_sched_vty.c @@ -0,0 +1,682 @@ +/*! \file cpu_sched_vty.c + * Implementation to CPU / Threading / Scheduler properties from VTY configuration. + */ +/* (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * SPDX-License-Identifier: GPLv2+ + */ + +#define _GNU_SOURCE + +#include "config.h" + +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <sched.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <dirent.h> +#include <pthread.h> +#include <inttypes.h> + +#include <osmocom/vty/vty.h> +#include <osmocom/vty/command.h> +#include <osmocom/vty/tdef_vty.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/fsm.h> +#include <osmocom/core/linuxlist.h> + +/*! \addtogroup Tdef_VTY + * + * CPU Scheduling related VTY API. + * + * @{ + * \file cpu_sched_vty.c + */ + +enum sched_vty_thread_id { + SCHED_VTY_THREAD_SELF, + SCHED_VTY_THREAD_ALL, + SCHED_VTY_THREAD_ID, + SCHED_VTY_THREAD_NAME, + SCHED_VTY_THREAD_UNKNOWN, +}; + +struct cpu_affinity_it { + struct llist_head entry; + enum sched_vty_thread_id tid_type; + char bufname[64]; + cpu_set_t *cpuset; + size_t cpuset_size; + bool delay; +}; + +struct sched_vty_opts { + void *tall_ctx; + int sched_rr_prio; + struct llist_head cpu_affinity_li; + pthread_mutex_t cpu_affinity_li_mutex; +}; + +static struct sched_vty_opts *sched_vty_opts; + +static struct cmd_node sched_node = { + L_CPU_SCHED_NODE, + "%s(config-cpu-sched)# ", + 1, +}; + +/* returns number of configured CPUs in the system, or negative otherwise */ +static int get_num_cpus(void) { + static unsigned int num_cpus = 0; + long ln; + + if (num_cpus) + return num_cpus; + + /* This is expensive (goes across /sys, so let's do it only once. It is + * guaranteed it won't change during process span anyway). */ + ln = sysconf(_SC_NPROCESSORS_CONF); + if (ln < 0) { + LOGP(DLGLOBAL, LOGL_ERROR, "sysconf(_SC_NPROCESSORS_CONF) failed: %s\n", + strerror(errno)); + return -1; + } + num_cpus = (unsigned int) ln; + return num_cpus; +} + +/* Parses string with CPU hex Affinity Mask, with right-most bit being CPU0, and + * fills a cpuset of size cpuset_size. + */ +static int parse_cpu_hex_mask(const char *str, cpu_set_t *cpuset, size_t cpuset_size) +{ + int len = strlen(str); + const char *ptr = str + len - 1; + int cpu = 0; + + /* skip optional '0x' prefix format */ + if (len >= 2 && str[0] == '0' && str[1] == 'x') + str += 2; + CPU_ZERO_S(cpuset_size, cpuset); + + while (ptr >= str) { + char c = *ptr; + uint8_t val; + + if (c >= '0' && c <= '9') { + val = c - '0'; + } else { + c = (char)tolower((int)c); + if (c >= 'a' && c <= 'f') + val = c + (10 - 'a'); + else + return -1; + } + if (val & 0x01) + CPU_SET_S(cpu, cpuset_size, cpuset); + if (val & 0x02) + CPU_SET_S(cpu + 1, cpuset_size, cpuset); + if (val & 0x04) + CPU_SET_S(cpu + 2, cpuset_size, cpuset); + if (val & 0x08) + CPU_SET_S(cpu + 3, cpuset_size, cpuset); + ptr--; + cpu += 4; + } + + return 0; +} + +/* Generates a hexstring in str from cpuset of size cpuset_size */ +static int generate_cpu_hex_mask(char *str, size_t str_buf_size, + cpu_set_t *cpuset, size_t cpuset_size) +{ + char *ptr = str; + int cpu; + bool first_nonzero_found = false; + + /* 2 char per byte, + '0x' prefix + '\0' */ + if (cpuset_size * 2 + 2 + 1 > str_buf_size) + return -1; + + *ptr++ = '0'; + *ptr++ = 'x'; + + for (cpu = cpuset_size*8 - 4; cpu >= 0; cpu -= 4) { + uint8_t val = 0; + + if (CPU_ISSET_S(cpu, cpuset_size, cpuset)) + val |= 0x01; + if (CPU_ISSET_S(cpu + 1, cpuset_size, cpuset)) + val |= 0x02; + if (CPU_ISSET_S(cpu + 2, cpuset_size, cpuset)) + val |= 0x04; + if (CPU_ISSET_S(cpu + 3, cpuset_size, cpuset)) + val |= 0x08; + + if (val < 10) + *ptr = '0' + val; + else + *ptr = ('a' - 10) + val; + if (val) + first_nonzero_found = true; + if (first_nonzero_found) + ptr++; + + } + if (!first_nonzero_found) + *ptr++ = '0'; + *ptr = '\0'; + return 0; +} + +/* Checks whther a thread identified by tid exists and belongs to the running process */ +static bool proc_tid_exists(pid_t tid) +{ + DIR *proc_dir; + struct dirent *entry; + char dirname[100]; + int tid_it; + bool found = false; + + snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid()); + proc_dir = opendir(dirname); + if (!proc_dir) + return false; /*FIXME; print error */ + + while ((entry = readdir(proc_dir))) { + if (entry->d_name[0] == '.') + continue; + tid_it = atoi(entry->d_name); + if (tid_it == tid) { + found = true; + break; + } + } + + closedir(proc_dir); + return found; +} + +/* Checks whther a thread identified by name exists and belongs to the running + * process, and returns its disocevered TID in res_pid. + */ +static bool proc_name_exists(const char *name, pid_t *res_pid) +{ + DIR *proc_dir; + struct dirent *entry; + char path[100]; + char buf[17]; /* 15 + \n + \0 */ + int tid_it; + int fd; + pid_t mypid = getpid(); + bool found = false; + int rc; + + *res_pid = 0; + + snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid); + proc_dir = opendir(path); + if (!proc_dir) + return false; + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + + tid_it = atoi(entry->d_name); + snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int) tid_it); + if ((fd = open(path, O_RDONLY)) == -1) + continue; + rc = read(fd, buf, sizeof(buf) - 1); + if (rc >= 0) { + /* Last may char contain a '\n', get rid of it */ + if (rc > 0 && buf[rc - 1] == '\n') + buf[rc - 1] = '\0'; + else + buf[rc] = '\0'; + if (strcmp(name, buf) == 0) { + *res_pid = tid_it; + found = true; + } + } + close(fd); + + if (found) + break; + } + + closedir(proc_dir); + return found; +} + +/* Parse VTY THREADNAME variable, return its type and fill discovered res_pid if required */ +static enum sched_vty_thread_id procname2pid(pid_t *res_pid, const char *str, bool applynow) +{ + size_t i, len; + bool is_pid = true; + + if (strcmp(str, "all") == 0) { + *res_pid = 0; + return SCHED_VTY_THREAD_ALL; + } + + if (strcmp(str, "self") == 0) { + *res_pid = 0; + return SCHED_VTY_THREAD_SELF; + } + + len = strlen(str); + for (i = 0; i < len; i++) { + if (!isdigit(str[i])) { + is_pid = false; + break; + } + } + if (is_pid) { + int64_t val; + if (osmo_str_to_int64(&val, str, 0, 0, INT64_MAX)) + return SCHED_VTY_THREAD_UNKNOWN; + *res_pid = (pid_t)val; + if (*res_pid != val) + return SCHED_VTY_THREAD_UNKNOWN; + if (!applynow || proc_tid_exists(*res_pid)) + return SCHED_VTY_THREAD_ID; + else + return SCHED_VTY_THREAD_UNKNOWN; + } + + if (len > 15) { + /* Thread names only allow up to 15+1 null chars, see man pthread_setname_np */ + return SCHED_VTY_THREAD_UNKNOWN; + } + + if (applynow) { + if (proc_name_exists(str, res_pid)) + return SCHED_VTY_THREAD_NAME; + else + return SCHED_VTY_THREAD_UNKNOWN; + } else { + /* assume a thread will be named after it */ + *res_pid = 0; + return SCHED_VTY_THREAD_NAME; + } +} + +/* Wrapper for sched_setaffinity applying to single thread or all threads in process based on tid_type. */ +static int my_sched_setaffinity(enum sched_vty_thread_id tid_type, pid_t pid, + cpu_set_t *cpuset, size_t cpuset_size) +{ + DIR *proc_dir; + struct dirent *entry; + char dirname[100]; + char str_mask[1024]; + int tid_it; + int rc = 0; + + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0) + str_mask[0] = '\0'; + + if (tid_type != SCHED_VTY_THREAD_ALL) { + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n", + (unsigned long) pid, str_mask); + + rc = sched_setaffinity(pid, sizeof(cpu_set_t), cpuset); + return rc; + } + + snprintf(dirname, sizeof(dirname), "/proc/%ld/task", (long int)getpid()); + proc_dir = opendir(dirname); + if (!proc_dir) + return -EINVAL; + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + tid_it = atoi(entry->d_name); + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting CPU affinity mask for tid %lu to: %s\n", + (unsigned long) tid_it, str_mask); + + rc = sched_setaffinity(tid_it, sizeof(cpu_set_t), cpuset); + if (rc == -1) + break; + } + + closedir(proc_dir); + return rc; + +} + +DEFUN_ATTR(cfg_sched_cpu_affinity, cfg_sched_cpu_affinity_cmd, + "cpu-affinity (self|all|<0-4294967295>|THREADNAME) CPUHEXMASK [delay]", + "Set CPU affinity mask on a (group of) thread(s)\n" + "Set CPU affinity mask on thread running the VTY\n" + "Set CPU affinity mask on all process' threads\n" + "Set CPU affinity mask on a thread with specified PID\n" + "Set CPU affinity mask on a thread with specified thread name\n" + "CPU affinity mask\n" + "If set, delay applying the affinity mask now and let the app handle it at a later point\n", + CMD_ATTR_IMMEDIATE) +{ + const char* str_who = argv[0]; + const char *str_mask = argv[1]; + bool applynow = (argc != 3); + int rc; + pid_t pid; + enum sched_vty_thread_id tid_type; + struct cpu_affinity_it *it, *it_next; + cpu_set_t *cpuset; + size_t cpuset_size; + + tid_type = procname2pid(&pid, str_who, applynow); + if (tid_type == SCHED_VTY_THREAD_UNKNOWN) { + vty_out(vty, "%% Failed parsing target thread %s%s", + str_who, VTY_NEWLINE); + return CMD_WARNING; + } + + if (tid_type == SCHED_VTY_THREAD_ID && !applynow) { + vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on tid %lu%s", + (unsigned long)pid, VTY_NEWLINE); + return CMD_WARNING; + } + if (tid_type == SCHED_VTY_THREAD_ALL && !applynow) { + vty_out(vty, "%% It makes no sense to delay applying cpu-affinity on all threads%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + cpuset = CPU_ALLOC(get_num_cpus()); + cpuset_size = CPU_ALLOC_SIZE(get_num_cpus()); + if (parse_cpu_hex_mask(str_mask, cpuset, cpuset_size) < 0) { + vty_out(vty, "%% Failed parsing CPU Affinity Mask %s%s", + str_mask, VTY_NEWLINE); + CPU_FREE(cpuset); + return CMD_WARNING; + } + + if (applynow) { + rc = my_sched_setaffinity(tid_type, pid, cpuset, cpuset_size); + if (rc == -1) { + vty_out(vty, "%% Failed setting sched CPU Affinity Mask %s: %s%s", + str_mask, strerror(errno), VTY_NEWLINE); + CPU_FREE(cpuset); + return CMD_WARNING; + } + } + + /* Keep history of cmds applied to be able to rewrite config. If PID was passed + directly it makes no sense to store it since PIDs are temporary */ + if (tid_type == SCHED_VTY_THREAD_SELF || + tid_type == SCHED_VTY_THREAD_ALL || + tid_type == SCHED_VTY_THREAD_NAME) { + pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex); + + /* Drop previous entries matching, since they will be overwritten */ + llist_for_each_entry_safe(it, it_next, &sched_vty_opts->cpu_affinity_li, entry) { + if (strcmp(it->bufname, str_who) == 0) { + llist_del(&it->entry); + CPU_FREE(it->cpuset); + talloc_free(it); + break; + } + } + it = talloc_zero(sched_vty_opts->tall_ctx, struct cpu_affinity_it); + OSMO_STRLCPY_ARRAY(it->bufname, str_who); + it->tid_type = tid_type; + it->cpuset = cpuset; + it->cpuset_size = cpuset_size; + it->delay = !applynow; + llist_add_tail(&it->entry, &sched_vty_opts->cpu_affinity_li); + + pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex); + } else { + /* We don't need cpuset for later, free it: */ + CPU_FREE(cpuset); + } + return CMD_SUCCESS; +} + +static int set_sched_rr(unsigned int prio) +{ + struct sched_param param; + int rc; + memset(¶m, 0, sizeof(param)); + param.sched_priority = prio; + LOGP(DLGLOBAL, LOGL_NOTICE, "Setting SCHED_RR priority %d\n", param.sched_priority); + rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); + if (rc == -1) { + LOGP(DLGLOBAL, LOGL_ERROR, "Setting SCHED_RR priority %d failed: %s\n", + param.sched_priority, strerror(errno)); + return -1; + } + return 0; +} + +DEFUN_ATTR(cfg_sched_policy, cfg_sched_policy_cmd, + "policy rr <1-32>", + "Set the scheduling policy to use for the process\n" + "Use the SCHED_RR real-time scheduling algorithm\n" + "Set the SCHED_RR real-time priority\n", + CMD_ATTR_IMMEDIATE) +{ + sched_vty_opts->sched_rr_prio = atoi(argv[0]); + + if (set_sched_rr(sched_vty_opts->sched_rr_prio) < 0) { + vty_out(vty, "%% Failed setting SCHED_RR priority %d%s", + sched_vty_opts->sched_rr_prio, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_sched, + cfg_sched_cmd, + "cpu-sched", "Configure CPU Scheduler related settings") +{ + vty->index = NULL; + vty->node = L_CPU_SCHED_NODE; + + return CMD_SUCCESS; +} + +DEFUN(show_sched_threads, show_sched_threads_cmd, + "show cpu-sched threads", + SHOW_STR + "Show Sched section information\n" + "Show information about running threads)\n") +{ + DIR *proc_dir; + struct dirent *entry; + char path[100]; + char name[17]; + char str_mask[1024]; + int tid_it; + int fd; + pid_t mypid = getpid(); + int rc; + cpu_set_t *cpuset; + size_t cpuset_size; + + vty_out(vty, "Thread list for PID %lu:%s", (unsigned long) mypid, VTY_NEWLINE); + + snprintf(path, sizeof(path), "/proc/%ld/task", (long int)mypid); + proc_dir = opendir(path); + if (!proc_dir) { + vty_out(vty, "%% Failed opening dir%s%s", path, VTY_NEWLINE); + return CMD_WARNING; + } + + while ((entry = readdir(proc_dir))) + { + if (entry->d_name[0] == '.') + continue; + + tid_it = atoi(entry->d_name); + snprintf(path, sizeof(path), "/proc/%ld/task/%ld/comm", (long int)mypid, (long int)tid_it); + if ((fd = open(path, O_RDONLY)) != -1) { + rc = read(fd, name, sizeof(name) - 1); + if (rc >= 0) { + /* Last may char contain a '\n', get rid of it */ + if (rc > 0 && name[rc - 1] == '\n') + name[rc - 1] = '\0'; + else + name[rc] = '\0'; + } + close(fd); + } else { + name[0] = '\0'; + } + + str_mask[0] = '\0'; + cpuset = CPU_ALLOC(get_num_cpus()); + cpuset_size = CPU_ALLOC_SIZE(get_num_cpus()); + CPU_ZERO_S(cpuset_size, cpuset); + if (sched_getaffinity(tid_it, cpuset_size, cpuset) == 0) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), cpuset, cpuset_size) < 0) + str_mask[0] = '\0'; + } + CPU_FREE(cpuset); + + vty_out(vty, " TID: %lu, NAME: '%s', cpu-affinity: %s%s", + (unsigned long) tid_it, name, str_mask, VTY_NEWLINE); + } + + closedir(proc_dir); + return CMD_SUCCESS; +} + +static int config_write_sched(struct vty *vty) +{ + struct cpu_affinity_it *it; + char str_mask[1024]; + + /* Only add the node if there's something to write under it */ + if (sched_vty_opts->sched_rr_prio || !llist_empty(&sched_vty_opts->cpu_affinity_li)) + vty_out(vty, "cpu-sched%s", VTY_NEWLINE); + + if (sched_vty_opts->sched_rr_prio) + vty_out(vty, " policy rr %d%s", sched_vty_opts->sched_rr_prio, VTY_NEWLINE); + + llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), it->cpuset, it->cpuset_size) < 0) + OSMO_STRLCPY_ARRAY(str_mask, "ERROR"); + vty_out(vty, " cpu-affinity %s %s%s%s", it->bufname, str_mask, + it->delay ? " delay" : "", VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/*! Initialize sched VTY nodes + * \param[in] tall_ctx Talloc context to use internally by vty_sched subsystem. + * \return 0 on success, non-zero on error. + */ +int osmo_cpu_sched_vty_init(void *tall_ctx) +{ + OSMO_ASSERT(!sched_vty_opts); /* assert only called once */ + + sched_vty_opts = talloc_zero(tall_ctx, struct sched_vty_opts); + sched_vty_opts->tall_ctx = tall_ctx; + INIT_LLIST_HEAD(&sched_vty_opts->cpu_affinity_li); + pthread_mutex_init(&sched_vty_opts->cpu_affinity_li_mutex, NULL); + + install_lib_element(CONFIG_NODE, &cfg_sched_cmd); + install_node(&sched_node, config_write_sched); + + install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_policy_cmd); + install_lib_element(L_CPU_SCHED_NODE, &cfg_sched_cpu_affinity_cmd); + + install_lib_element_ve(&show_sched_threads_cmd); + + /* Initialize amount of cpus now */ + if (get_num_cpus() < 0) + return -1; + + return 0; +} + +/*! Apply cpu-affinity on calling thread based on VTY configuration + * \return 0 on success, non-zero on error. + */ +int osmo_cpu_sched_vty_apply_localthread(void) +{ + struct cpu_affinity_it *it, *it_match = NULL; + char name[16]; /* 15 + \0 */ + char str_mask[1024]; + bool has_name = false; + int rc = 0; + + /* Assert subsystem was inited and structs are preset */ + if (!sched_vty_opts) { + LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask impossible: no opts!\n"); + return 0; + } + +#ifdef HAVE_PTHREAD_GETNAME_NP + if (pthread_getname_np(pthread_self(), name, sizeof(name)) == 0) + has_name = true; +#endif + + /* Get latest matching mask for the thread */ + pthread_mutex_lock(&sched_vty_opts->cpu_affinity_li_mutex); + llist_for_each_entry(it, &sched_vty_opts->cpu_affinity_li, entry) { + switch (it->tid_type) { + case SCHED_VTY_THREAD_SELF: + continue; /* self to the VTY thread, not us */ + case SCHED_VTY_THREAD_ALL: + it_match = it; + break; + case SCHED_VTY_THREAD_NAME: + if (!has_name) + continue; + if (strcmp(name, it->bufname) != 0) + continue; + it_match = it; + break; + default: + OSMO_ASSERT(0); + } + } + + if (it_match) { + rc = my_sched_setaffinity(SCHED_VTY_THREAD_SELF, 0, it_match->cpuset, it_match->cpuset_size); + if (rc == -1) { + if (generate_cpu_hex_mask(str_mask, sizeof(str_mask), + it_match->cpuset, it_match->cpuset_size) < 0) + str_mask[0] = '\0'; + LOGP(DLGLOBAL, LOGL_FATAL, "Setting cpu-affinity mask %s failed: %s\n", + str_mask, strerror(errno)); + } + } + pthread_mutex_unlock(&sched_vty_opts->cpu_affinity_li_mutex); + return rc; +} + +/*! @} */ diff --git a/src/vty/fsm_vty.c b/src/vty/fsm_vty.c index 9bde241c..19c35daa 100644 --- a/src/vty/fsm_vty.c +++ b/src/vty/fsm_vty.c @@ -14,16 +14,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h> @@ -51,62 +47,82 @@ extern struct llist_head osmo_g_fsms; /*! Print information about a FSM [class] to the given VTY * \param vty The VTY to which to print + * \param[in] prefix prefix to print at start of each line (typically indenting) * \param[in] fsm The FSM class to print */ -void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm) +void vty_out_fsm2(struct vty *vty, const char *prefix, struct osmo_fsm *fsm) { unsigned int i; const struct value_string *evt_name; - vty_out(vty, "FSM Name: '%s', Log Subsys: '%s'%s", fsm->name, + vty_out(vty, "%sFSM Name: '%s', Log Subsys: '%s'%s", prefix, fsm->name, log_category_name(fsm->log_subsys), VTY_NEWLINE); /* list the events */ if (fsm->event_names) { for (evt_name = fsm->event_names; evt_name->str != NULL; evt_name++) { - vty_out(vty, " Event %02u (0x%08x): '%s'%s", evt_name->value, - (1 << evt_name->value), evt_name->str, VTY_NEWLINE); + vty_out(vty, "%s Event %02u (0x%08x): '%s'%s", prefix, evt_name->value, + (1U << evt_name->value), evt_name->str, VTY_NEWLINE); } } else - vty_out(vty, " No event names are defined for this FSM! Please fix!%s", VTY_NEWLINE); + vty_out(vty, "%s No event names are defined for this FSM! Please fix!%s", prefix, VTY_NEWLINE); /* list the states */ - vty_out(vty, " Number of States: %u%s", fsm->num_states, VTY_NEWLINE); + vty_out(vty, "%s Number of States: %u%s", prefix, fsm->num_states, VTY_NEWLINE); for (i = 0; i < fsm->num_states; i++) { const struct osmo_fsm_state *state = &fsm->states[i]; - vty_out(vty, " State %-20s InEvtMask: 0x%08x, OutStateMask: 0x%08x%s", + vty_out(vty, "%s State %-20s InEvtMask: 0x%08x, OutStateMask: 0x%08x%s", prefix, state->name, state->in_event_mask, state->out_state_mask, VTY_NEWLINE); } } +/*! Print information about a FSM [class] to the given VTY + * \param vty The VTY to which to print + * \param[in] fsm The FSM class to print + */ +void vty_out_fsm(struct vty *vty, struct osmo_fsm *fsm) +{ + vty_out_fsm2(vty, "", fsm); +} + /*! Print a FSM instance to the given VTY * \param vty The VTY to which to print + * \param[in] prefix prefix to print at start of each line (typically indenting) * \param[in] fsmi The FSM instance to print */ -void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi) +void vty_out_fsm_inst2(struct vty *vty, const char *prefix, struct osmo_fsm_inst *fsmi) { struct osmo_fsm_inst *child; - vty_out(vty, "FSM Instance Name: '%s', ID: '%s'%s", + vty_out(vty, "%sFSM Instance Name: '%s', ID: '%s'%s", prefix, fsmi->name, fsmi->id, VTY_NEWLINE); - vty_out(vty, " Log-Level: '%s', State: '%s'%s", + vty_out(vty, "%s Log-Level: '%s', State: '%s'%s", prefix, log_level_str(fsmi->log_level), osmo_fsm_state_name(fsmi->fsm, fsmi->state), VTY_NEWLINE); if (fsmi->T) - vty_out(vty, " Timer: %u%s", fsmi->T, VTY_NEWLINE); + vty_out(vty, "%s Timer: %u%s", prefix, fsmi->T, VTY_NEWLINE); if (fsmi->proc.parent) { - vty_out(vty, " Parent: '%s', Term-Event: '%s'%s", + vty_out(vty, "%s Parent: '%s', Term-Event: '%s'%s", prefix, fsmi->proc.parent->name, osmo_fsm_event_name(fsmi->proc.parent->fsm, fsmi->proc.parent_term_event), VTY_NEWLINE); } llist_for_each_entry(child, &fsmi->proc.children, proc.child) { - vty_out(vty, " Child: '%s'%s", child->name, VTY_NEWLINE); + vty_out(vty, "%s Child: '%s'%s", prefix, child->name, VTY_NEWLINE); } } +/*! Print a FSM instance to the given VTY + * \param vty The VTY to which to print + * \param[in] fsmi The FSM instance to print + */ +void vty_out_fsm_inst(struct vty *vty, struct osmo_fsm_inst *fsmi) +{ + vty_out_fsm_inst2(vty, "", fsmi); +} + #define SH_FSM_STR SHOW_STR "Show information about finite state machines\n" #define SH_FSMI_STR SHOW_STR "Show information about finite state machine instances\n" @@ -142,6 +158,44 @@ DEFUN(show_fsm, show_fsm_cmd, return CMD_SUCCESS; } +DEFUN(show_fsm_state_graph, show_fsm_state_graph_cmd, + "show fsm-state-graph NAME", + SHOW_STR "Generate a state transition graph (using DOT language)\n" + "FSM name\n") +{ + const struct osmo_fsm *fsm; + + fsm = osmo_fsm_find_by_name(argv[0]); + if (!fsm) { + vty_out(vty, "Error: FSM with name '%s' doesn't exist!%s", + argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "digraph \"%s\" {%s", fsm->name, VTY_NEWLINE); + + for (unsigned int i = 0; i < fsm->num_states; i++) { + const struct osmo_fsm_state *st = &fsm->states[i]; + + vty_out(vty, "\t\"%s\"; # out_state_mask=0x%08x%s", + st->name, st->out_state_mask, VTY_NEWLINE); + + for (unsigned int j = 0; j < sizeof(st->out_state_mask) * 8; j++) { + if (~st->out_state_mask & (1 << j)) + continue; + vty_out(vty, "\t\"%s\" -> \"%s\";%s", + st->name, osmo_fsm_state_name(fsm, j), + VTY_NEWLINE); + } + + vty_out(vty, "%s", VTY_NEWLINE); + } + + vty_out(vty, "}%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + DEFUN(show_fsm_insts, show_fsm_insts_cmd, "show fsm-instances all", SH_FSMI_STR @@ -196,9 +250,10 @@ void osmo_fsm_vty_add_cmds(void) if (osmo_fsm_vty_cmds_installed) return; - install_element_ve(&show_fsm_cmd); - install_element_ve(&show_fsms_cmd); - install_element_ve(&show_fsm_inst_cmd); - install_element_ve(&show_fsm_insts_cmd); + install_lib_element_ve(&show_fsm_cmd); + install_lib_element_ve(&show_fsms_cmd); + install_lib_element_ve(&show_fsm_state_graph_cmd); + install_lib_element_ve(&show_fsm_inst_cmd); + install_lib_element_ve(&show_fsm_insts_cmd); osmo_fsm_vty_cmds_installed = true; } diff --git a/src/vty/logging_vty.c b/src/vty/logging_vty.c index c51b4373..678ae686 100644 --- a/src/vty/logging_vty.c +++ b/src/vty/logging_vty.c @@ -15,16 +15,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h> @@ -33,6 +29,7 @@ #include <osmocom/core/strrb.h> #include <osmocom/core/loggingrb.h> #include <osmocom/core/gsmtap.h> +#include <osmocom/core/application.h> #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h> @@ -130,7 +127,7 @@ DEFUN(enable_logging, conn = (struct telnet_connection *) vty->priv; if (conn->dbg) { - vty_out(vty, "Logging already enabled.%s", VTY_NEWLINE); + vty_out(vty, "%% Logging already enabled.%s", VTY_NEWLINE); return CMD_WARNING; } @@ -157,7 +154,7 @@ struct log_target *osmo_log_vty2tgt(struct vty *vty) conn = (struct telnet_connection *) vty->priv; if (!conn->dbg) - vty_out(vty, "Logging was not enabled.%s", VTY_NEWLINE); + vty_out(vty, "%% Logging was not enabled.%s", VTY_NEWLINE); return conn->dbg; } @@ -224,6 +221,22 @@ DEFUN(logging_prnt_ext_timestamp, RET_WITH_UNLOCK(CMD_SUCCESS); } +DEFUN(logging_prnt_tid, + logging_prnt_tid_cmd, + "logging print thread-id (0|1)", + LOGGING_STR "Log output settings\n" + "Configure log message logging Thread ID\n" + "Don't prefix each log message\n" + "Prefix each log message with current Thread ID\n") +{ + struct log_target *tgt; + + ACQUIRE_VTY_LOG_TGT_WITH_LOCK(vty, tgt); + + log_set_print_tid(tgt, atoi(argv[0])); + RET_WITH_UNLOCK(CMD_SUCCESS); +} + DEFUN(logging_prnt_cat, logging_prnt_cat_cmd, "logging print category (0|1)", @@ -337,6 +350,9 @@ static void gen_logging_level_cmd_strs(struct cmd_element *cmd, osmo_talloc_asprintf(tall_log_ctx, cmd_str, ") %s", level_args); osmo_talloc_asprintf(tall_log_ctx, doc_str, "%s", level_strs); + talloc_set_name_const(cmd_str, "vty_log_level_cmd_str"); + talloc_set_name_const(doc_str, "vty_log_level_doc_str"); + cmd->string = cmd_str; cmd->doc = doc_str; } @@ -352,12 +368,12 @@ DEFUN(logging_level, int level = log_parse_level(argv[1]); if (level < 0) { - vty_out(vty, "Invalid level `%s'%s", argv[1], VTY_NEWLINE); + vty_out(vty, "%% Invalid level '%s'%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } if (category < 0) { - vty_out(vty, "Invalid category `%s'%s", argv[0], VTY_NEWLINE); + vty_out(vty, "%% Invalid category '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } @@ -366,6 +382,10 @@ DEFUN(logging_level, tgt->categories[category].enabled = 1; tgt->categories[category].loglevel = level; +#if !defined(EMBEDDED) + log_cache_update(category, 1, level); +#endif + RET_WITH_UNLOCK(CMD_SUCCESS); } @@ -390,6 +410,9 @@ DEFUN(logging_level_set_all, logging_level_set_all_cmd, cat->enabled = 1; cat->loglevel = level; +#if !defined(EMBEDDED) + log_cache_update(i, 1, level); +#endif } RET_WITH_UNLOCK(CMD_SUCCESS); } @@ -575,7 +598,7 @@ gDEFUN(cfg_description, cfg_description_cmd, char **dptr = vty->index_sub; if (!dptr) { - vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + vty_out(vty, "%% vty->index_sub == NULL%s", VTY_NEWLINE); return CMD_WARNING; } @@ -596,7 +619,7 @@ gDEFUN(cfg_no_description, cfg_no_description_cmd, char **dptr = vty->index_sub; if (!dptr) { - vty_out(vty, "vty->index_sub == NULL%s", VTY_NEWLINE); + vty_out(vty, "%% vty->index_sub == NULL%s", VTY_NEWLINE); return CMD_WARNING; } @@ -728,6 +751,64 @@ DEFUN(cfg_no_log_syslog, cfg_no_log_syslog_cmd, } #endif /* HAVE_SYSLOG_H */ +DEFUN(cfg_log_systemd_journal, cfg_log_systemd_journal_cmd, + "log systemd-journal [raw]", + LOG_STR "Logging to systemd-journal\n" + "Offload rendering of the meta information (location, category) to systemd\n") +{ +#ifdef ENABLE_SYSTEMD_LOGGING + struct log_target *tgt; + bool raw = argc > 0; + + log_tgt_mutex_lock(); + tgt = log_target_find(LOG_TGT_TYPE_SYSTEMD, NULL); + if (tgt == NULL) { + tgt = log_target_create_systemd(raw); + if (tgt == NULL) { + vty_out(vty, "%% Unable to create systemd-journal " + "log target%s", VTY_NEWLINE); + RET_WITH_UNLOCK(CMD_WARNING); + } + log_add_target(tgt); + } else if (tgt->sd_journal.raw != raw) { + log_target_systemd_set_raw(tgt, raw); + } + + vty->index = tgt; + vty->node = CFG_LOG_NODE; + + RET_WITH_UNLOCK(CMD_SUCCESS); +#else + vty_out(vty, "%% systemd-journal logging is not available " + "in this build of libosmocore%s", VTY_NEWLINE); + return CMD_WARNING; +#endif /* ENABLE_SYSTEMD_LOGGING */ +} + +DEFUN(cfg_no_log_systemd_journal, cfg_no_log_systemd_journal_cmd, + "no log systemd-journal", + NO_STR LOG_STR "Logging to systemd-journal\n") +{ +#ifdef ENABLE_SYSTEMD_LOGGING + struct log_target *tgt; + + log_tgt_mutex_lock(); + tgt = log_target_find(LOG_TGT_TYPE_SYSTEMD, NULL); + if (!tgt) { + vty_out(vty, "%% No systemd-journal logging active%s", VTY_NEWLINE); + RET_WITH_UNLOCK(CMD_WARNING); + } + + log_target_destroy(tgt); + + RET_WITH_UNLOCK(CMD_SUCCESS); +#else + vty_out(vty, "%% systemd-journal logging is not available " + "in this build of libosmocore%s", VTY_NEWLINE); + return CMD_WARNING; +#endif /* ENABLE_SYSTEMD_LOGGING */ +} + DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd, "log gsmtap [HOSTNAME]", LOG_STR "Logging via GSMTAP\n" @@ -756,9 +837,31 @@ DEFUN(cfg_log_gsmtap, cfg_log_gsmtap_cmd, RET_WITH_UNLOCK(CMD_SUCCESS); } +DEFUN(cfg_no_log_gsmtap, cfg_no_log_gsmtap_cmd, + "no log gsmtap [HOSTNAME]", + NO_STR LOG_STR "Logging via GSMTAP\n" + "Host name to send the GSMTAP logging to (UDP port 4729)\n") +{ + const char *hostname = argc ? argv[0] : "127.0.0.1"; + struct log_target *tgt; + + log_tgt_mutex_lock(); + tgt = log_target_find(LOG_TGT_TYPE_GSMTAP, hostname); + if (tgt == NULL) { + vty_out(vty, "%% Unable to find GSMTAP log target for %s%s", + hostname, VTY_NEWLINE); + RET_WITH_UNLOCK(CMD_WARNING); + } + + log_target_destroy(tgt); + + RET_WITH_UNLOCK(CMD_SUCCESS); +} + DEFUN(cfg_log_stderr, cfg_log_stderr_cmd, - "log stderr", - LOG_STR "Logging via STDERR of the process\n") + "log stderr [blocking-io]", + LOG_STR "Logging via STDERR of the process\n" + "Use blocking, synchronous I/O\n") { struct log_target *tgt; @@ -774,6 +877,11 @@ DEFUN(cfg_log_stderr, cfg_log_stderr_cmd, log_add_target(tgt); } + if (argc > 0 && !strcmp(argv[0], "blocking-io")) + log_target_file_switch_to_stream(tgt); + else + log_target_file_switch_to_wqueue(tgt); + vty->index = tgt; vty->node = CFG_LOG_NODE; @@ -794,13 +902,15 @@ DEFUN(cfg_no_log_stderr, cfg_no_log_stderr_cmd, } log_target_destroy(tgt); + osmo_stderr_target = NULL; RET_WITH_UNLOCK(CMD_SUCCESS); } DEFUN(cfg_log_file, cfg_log_file_cmd, - "log file .FILENAME", - LOG_STR "Logging to text file\n" "Filename\n") + "log file FILENAME [blocking-io]", + LOG_STR "Logging to text file\n" "Filename\n" + "Use blocking, synchronous I/O\n") { const char *fname = argv[0]; struct log_target *tgt; @@ -810,13 +920,18 @@ DEFUN(cfg_log_file, cfg_log_file_cmd, if (!tgt) { tgt = log_target_create_file(fname); if (!tgt) { - vty_out(vty, "%% Unable to create file `%s'%s", + vty_out(vty, "%% Unable to create file '%s'%s", fname, VTY_NEWLINE); RET_WITH_UNLOCK(CMD_WARNING); } log_add_target(tgt); } + if (argc > 1 && !strcmp(argv[1], "blocking-io")) + log_target_file_switch_to_stream(tgt); + else + log_target_file_switch_to_wqueue(tgt); + vty->index = tgt; vty->node = CFG_LOG_NODE; @@ -825,7 +940,7 @@ DEFUN(cfg_log_file, cfg_log_file_cmd, DEFUN(cfg_no_log_file, cfg_no_log_file_cmd, - "no log file .FILENAME", + "no log file FILENAME", NO_STR LOG_STR "Logging to text file\n" "Filename\n") { const char *fname = argv[0]; @@ -834,7 +949,7 @@ DEFUN(cfg_no_log_file, cfg_no_log_file_cmd, log_tgt_mutex_lock(); tgt = log_target_find(LOG_TGT_TYPE_FILE, fname); if (!tgt) { - vty_out(vty, "%% No such log file `%s'%s", + vty_out(vty, "%% No such log file '%s'%s", fname, VTY_NEWLINE); RET_WITH_UNLOCK(CMD_WARNING); } @@ -900,7 +1015,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) return 1; break; case LOG_TGT_TYPE_STDERR: - vty_out(vty, "log stderr%s", VTY_NEWLINE); + if (tgt->tgt_file.wqueue) + vty_out(vty, "log stderr%s", VTY_NEWLINE); + else + vty_out(vty, "log stderr blocking-io%s", VTY_NEWLINE); break; case LOG_TGT_TYPE_SYSLOG: #ifdef HAVE_SYSLOG_H @@ -911,7 +1029,10 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) #endif break; case LOG_TGT_TYPE_FILE: - vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); + if (tgt->tgt_file.wqueue) + vty_out(vty, "log file %s%s", tgt->tgt_file.fname, VTY_NEWLINE); + else + vty_out(vty, "log file %s blocking-io%s", tgt->tgt_file.fname, VTY_NEWLINE); break; case LOG_TGT_TYPE_STRRB: vty_out(vty, "log alarms %zu%s", @@ -921,6 +1042,11 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) vty_out(vty, "log gsmtap %s%s", tgt->tgt_gsmtap.hostname, VTY_NEWLINE); break; + case LOG_TGT_TYPE_SYSTEMD: + vty_out(vty, "log systemd-journal%s%s", + tgt->sd_journal.raw ? " raw" : "", + VTY_NEWLINE); + break; } vty_out(vty, " logging filter all %u%s", @@ -935,6 +1061,8 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) tgt->print_category_hex ? 1 : 0, VTY_NEWLINE); vty_out(vty, " logging print category %d%s", tgt->print_category ? 1 : 0, VTY_NEWLINE); + vty_out(vty, " logging print thread-id %d%s", + tgt->print_tid ? 1 : 0, VTY_NEWLINE); if (tgt->print_ext_timestamp) vty_out(vty, " logging print extended-timestamp 1%s", VTY_NEWLINE); else @@ -942,8 +1070,9 @@ static int config_write_log_single(struct vty *vty, struct log_target *tgt) tgt->print_timestamp ? 1 : 0, VTY_NEWLINE); if (tgt->print_level) vty_out(vty, " logging print level 1%s", VTY_NEWLINE); - vty_out(vty, " logging print file %s%s", + vty_out(vty, " logging print file %s%s%s", get_value_string(logging_print_file_args, tgt->print_filename2), + tgt->print_filename_pos == LOG_FILENAME_POS_LINE_END ? " last" : "", VTY_NEWLINE); if (tgt->loglevel) { @@ -1011,7 +1140,7 @@ void logging_vty_add_deprecated_subsys(void *ctx, const char *name) "Deprecated Category\n"; cmd->attr = CMD_ATTR_DEPRECATED; - install_element(CFG_LOG_NODE, cmd); + install_lib_element(CFG_LOG_NODE, cmd); } /* logp (<categories>) (debug|...|fatal) .LOGMESSAGE*/ @@ -1023,6 +1152,23 @@ DEFUN(vty_logp, int category = log_parse_category(argv[0]); int level = log_parse_level(argv[1]); char *str = argv_concat(argv, argc, 2); + + if (level < 0) { + vty_out(vty, "%% Invalid level '%s'%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + if (category < 0) { + vty_out(vty, "%% Invalid category '%s'%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + /* Properly handle library specific sub-systems */ + if ((unsigned int) category >= osmo_log_info->num_cat_user) { + category -= osmo_log_info->num_cat_user - 1; + category *= -1; + } + LOGP(category, level, "%s\n", str); return CMD_SUCCESS; } @@ -1048,26 +1194,30 @@ static void gen_vty_logp_cmd_strs(struct cmd_element *cmd) osmo_talloc_asprintf(tall_log_ctx, doc_str, "Arbitrary message to log on given category and log level\n"); + talloc_set_name_const(cmd_str, "vty_logp_cmd_str"); + talloc_set_name_const(doc_str, "vty_logp_doc_str"); + cmd->string = cmd_str; cmd->doc = doc_str; } /*! Register logging related commands to the VTY. Call this once from * your application if you want to support those commands. */ -void logging_vty_add_cmds() +void logging_vty_add_cmds(void) { - install_element_ve(&enable_logging_cmd); - install_element_ve(&disable_logging_cmd); - install_element_ve(&logging_fltr_all_cmd); - install_element_ve(&logging_use_clr_cmd); - install_element_ve(&logging_prnt_timestamp_cmd); - install_element_ve(&logging_prnt_ext_timestamp_cmd); - install_element_ve(&logging_prnt_cat_cmd); - install_element_ve(&logging_prnt_cat_hex_cmd); - install_element_ve(&logging_prnt_level_cmd); - install_element_ve(&logging_prnt_file_cmd); - install_element_ve(&logging_set_category_mask_cmd); - install_element_ve(&logging_set_category_mask_old_cmd); + install_lib_element_ve(&enable_logging_cmd); + install_lib_element_ve(&disable_logging_cmd); + install_lib_element_ve(&logging_fltr_all_cmd); + install_lib_element_ve(&logging_use_clr_cmd); + install_lib_element_ve(&logging_prnt_timestamp_cmd); + install_lib_element_ve(&logging_prnt_ext_timestamp_cmd); + install_lib_element_ve(&logging_prnt_tid_cmd); + install_lib_element_ve(&logging_prnt_cat_cmd); + install_lib_element_ve(&logging_prnt_cat_hex_cmd); + install_lib_element_ve(&logging_prnt_level_cmd); + install_lib_element_ve(&logging_prnt_file_cmd); + install_lib_element_ve(&logging_set_category_mask_cmd); + install_lib_element_ve(&logging_set_category_mask_old_cmd); /* logging level (<categories>) (debug|...|fatal) */ gen_logging_level_cmd_strs(&logging_level_cmd, @@ -1077,47 +1227,51 @@ void logging_vty_add_cmds() gen_logging_level_cmd_strs(&deprecated_logging_level_everything_cmd, "everything", EVERYTHING_STR); - install_element_ve(&logging_level_cmd); - install_element_ve(&logging_level_set_all_cmd); - install_element_ve(&logging_level_force_all_cmd); - install_element_ve(&no_logging_level_force_all_cmd); - install_element_ve(&deprecated_logging_level_everything_cmd); - install_element_ve(&deprecated_logging_level_all_cmd); - install_element_ve(&deprecated_logging_level_all_everything_cmd); + install_lib_element_ve(&logging_level_cmd); + install_lib_element_ve(&logging_level_set_all_cmd); + install_lib_element_ve(&logging_level_force_all_cmd); + install_lib_element_ve(&no_logging_level_force_all_cmd); + install_lib_element_ve(&deprecated_logging_level_everything_cmd); + install_lib_element_ve(&deprecated_logging_level_all_cmd); + install_lib_element_ve(&deprecated_logging_level_all_everything_cmd); gen_vty_logp_cmd_strs(&vty_logp_cmd); - install_element_ve(&vty_logp_cmd); + install_lib_element_ve(&vty_logp_cmd); - install_element_ve(&show_logging_vty_cmd); - install_element_ve(&show_alarms_cmd); + install_lib_element_ve(&show_logging_vty_cmd); + install_lib_element_ve(&show_alarms_cmd); install_node(&cfg_log_node, config_write_log); - install_element(CFG_LOG_NODE, &logging_fltr_all_cmd); - install_element(CFG_LOG_NODE, &logging_use_clr_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_cat_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_cat_hex_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_level_cmd); - install_element(CFG_LOG_NODE, &logging_prnt_file_cmd); - install_element(CFG_LOG_NODE, &logging_level_cmd); - install_element(CFG_LOG_NODE, &logging_level_set_all_cmd); - install_element(CFG_LOG_NODE, &logging_level_force_all_cmd); - install_element(CFG_LOG_NODE, &no_logging_level_force_all_cmd); - install_element(CFG_LOG_NODE, &deprecated_logging_level_everything_cmd); - install_element(CFG_LOG_NODE, &deprecated_logging_level_all_cmd); - install_element(CFG_LOG_NODE, &deprecated_logging_level_all_everything_cmd); - - install_element(CONFIG_NODE, &cfg_log_stderr_cmd); - install_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); - install_element(CONFIG_NODE, &cfg_log_file_cmd); - install_element(CONFIG_NODE, &cfg_no_log_file_cmd); - install_element(CONFIG_NODE, &cfg_log_alarms_cmd); - install_element(CONFIG_NODE, &cfg_no_log_alarms_cmd); + install_lib_element(CFG_LOG_NODE, &logging_fltr_all_cmd); + install_lib_element(CFG_LOG_NODE, &logging_use_clr_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_timestamp_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_ext_timestamp_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_tid_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_cat_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_cat_hex_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_level_cmd); + install_lib_element(CFG_LOG_NODE, &logging_prnt_file_cmd); + install_lib_element(CFG_LOG_NODE, &logging_level_cmd); + install_lib_element(CFG_LOG_NODE, &logging_level_set_all_cmd); + install_lib_element(CFG_LOG_NODE, &logging_level_force_all_cmd); + install_lib_element(CFG_LOG_NODE, &no_logging_level_force_all_cmd); + install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_everything_cmd); + install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_all_cmd); + install_lib_element(CFG_LOG_NODE, &deprecated_logging_level_all_everything_cmd); + + install_lib_element(CONFIG_NODE, &cfg_log_stderr_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_stderr_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_file_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_file_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_alarms_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_alarms_cmd); #ifdef HAVE_SYSLOG_H - install_element(CONFIG_NODE, &cfg_log_syslog_cmd); - install_element(CONFIG_NODE, &cfg_log_syslog_local_cmd); - install_element(CONFIG_NODE, &cfg_no_log_syslog_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_syslog_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_syslog_local_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_syslog_cmd); #endif - install_element(CONFIG_NODE, &cfg_log_gsmtap_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_systemd_journal_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_systemd_journal_cmd); + install_lib_element(CONFIG_NODE, &cfg_log_gsmtap_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_log_gsmtap_cmd); } diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c index 46282817..f940018f 100644 --- a/src/vty/stats_vty.c +++ b/src/vty/stats_vty.c @@ -1,5 +1,5 @@ /* - * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> + * (C) 2009-2022 by Harald Welte <laforge@gnumonks.org> * (C) 2009-2014 by Holger Hans Peter Freyther * (C) 2015 by sysmocom - s.f.m.c. GmbH * All Rights Reserved @@ -16,16 +16,12 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdlib.h> #include <string.h> -#include "../../config.h" +#include "config.h" #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h> @@ -37,11 +33,15 @@ #include <osmocom/core/stats.h> #include <osmocom/core/counter.h> #include <osmocom/core/rate_ctr.h> +#include <osmocom/core/stats_tcp.h> #define CFG_STATS_STR "Configure stats sub-system\n" #define CFG_REPORTER_STR "Configure a stats reporter\n" #define SHOW_STATS_STR "Show statistical values\n" +#define SKIP_ZERO_STR "Skip items with total count zero\n" + +#define STATS_STR "Stats related commands\n" /*! \file stats_vty.c * VTY interface for statsd / statistic items @@ -267,14 +267,20 @@ DEFUN(cfg_stats_reporter_flush_period, cfg_stats_reporter_flush_period_cmd, } DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd, - "stats reporter statsd", - CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") + "stats reporter statsd [NAME]", + CFG_STATS_STR CFG_REPORTER_STR + "Report to a STATSD server\n" + "Name of the reporter\n") { struct osmo_stats_reporter *srep; + const char *name = NULL; - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); + if (argc > 0) + name = argv[0]; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name); if (!srep) { - srep = osmo_stats_reporter_create_statsd(NULL); + srep = osmo_stats_reporter_create_statsd(name); if (!srep) { vty_out(vty, "%% Unable to create statsd reporter%s", VTY_NEWLINE); @@ -291,15 +297,21 @@ DEFUN(cfg_stats_reporter_statsd, cfg_stats_reporter_statsd_cmd, } DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd, - "no stats reporter statsd", - NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to a STATSD server\n") + "no stats reporter statsd [NAME]", + NO_STR CFG_STATS_STR CFG_REPORTER_STR + "Report to a STATSD server\n" + "Name of the reporter\n") { struct osmo_stats_reporter *srep; + const char *name = NULL; + + if (argc > 0) + name = argv[0]; - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, name); if (!srep) { - vty_out(vty, "%% No statsd logging active%s", - VTY_NEWLINE); + vty_out(vty, "%% There is no such statsd reporter with name '%s'%s", + name ? name : "", VTY_NEWLINE); return CMD_WARNING; } @@ -309,14 +321,20 @@ DEFUN(cfg_no_stats_reporter_statsd, cfg_no_stats_reporter_statsd_cmd, } DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd, - "stats reporter log", - CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") + "stats reporter log [NAME]", + CFG_STATS_STR CFG_REPORTER_STR + "Report to the logger\n" + "Name of the reporter\n") { struct osmo_stats_reporter *srep; + const char *name = NULL; + + if (argc > 0) + name = argv[0]; - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name); if (!srep) { - srep = osmo_stats_reporter_create_log(NULL); + srep = osmo_stats_reporter_create_log(name); if (!srep) { vty_out(vty, "%% Unable to create log reporter%s", VTY_NEWLINE); @@ -333,15 +351,21 @@ DEFUN(cfg_stats_reporter_log, cfg_stats_reporter_log_cmd, } DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd, - "no stats reporter log", - NO_STR CFG_STATS_STR CFG_REPORTER_STR "Report to the logger\n") + "no stats reporter log [NAME]", + NO_STR CFG_STATS_STR CFG_REPORTER_STR + "Report to the logger\n" + "Name of the reporter\n") { struct osmo_stats_reporter *srep; + const char *name = NULL; - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); + if (argc > 0) + name = argv[0]; + + srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, name); if (!srep) { - vty_out(vty, "%% No log reporting active%s", - VTY_NEWLINE); + vty_out(vty, "%% There is no such log reporter with name '%s'%s", + name ? name : "", VTY_NEWLINE); return CMD_WARNING; } @@ -351,9 +375,9 @@ DEFUN(cfg_no_stats_reporter_log, cfg_no_stats_reporter_log_cmd, } DEFUN(cfg_stats_interval, cfg_stats_interval_cmd, - "stats interval <1-65535>", + "stats interval <0-65535>", CFG_STATS_STR "Set the reporting interval\n" - "Interval in seconds\n") + "Interval in seconds (0 disables the reporting interval)\n") { int rc; int interval = atoi(argv[0]); @@ -367,27 +391,60 @@ DEFUN(cfg_stats_interval, cfg_stats_interval_cmd, return CMD_SUCCESS; } +DEFUN(cfg_tcp_stats_interval, cfg_tcp_stats_interval_cmd, + "stats-tcp interval <0-65535>", + CFG_STATS_STR "Set the tcp socket stats polling interval\n" + "Interval in seconds (0 disables the polling interval)\n") +{ + int rc; + int interval = atoi(argv[0]); + rc = osmo_stats_tcp_set_interval(interval); + if (rc < 0) { + vty_out(vty, "%% Unable to set interval: %s%s", + strerror(-rc), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN(cfg_tcp_stats_batch_size, cfg_tcp_stats_batch_size_cmd, + "stats-tcp batch-size <1-65535>", + CFG_STATS_STR "Set the number of tcp sockets that are processed per stats polling interval\n" + "Number of sockets per interval\n") +{ + osmo_tcp_stats_config->batch_size = atoi(argv[0]); + return CMD_SUCCESS; +} + DEFUN(show_stats, show_stats_cmd, - "show stats", - SHOW_STR SHOW_STATS_STR) + "show stats [skip-zero]", + SHOW_STR SHOW_STATS_STR SKIP_ZERO_STR) { - vty_out_statistics_full(vty, ""); + bool skip_zero = false; + if (argc > 0) + skip_zero = true; + + vty_out_statistics_full2(vty, "", skip_zero); return CMD_SUCCESS; } DEFUN(show_stats_level, show_stats_level_cmd, - "show stats level (global|peer|subscriber)", + "show stats level (global|peer|subscriber) [skip-zero]", SHOW_STR SHOW_STATS_STR "Set the maximum group level\n" "Show global groups only\n" "Show global and network peer related groups\n" - "Show global, peer, and subscriber groups\n") + "Show global, peer, and subscriber groups\n" SKIP_ZERO_STR) { int level = get_string_value(stats_class_strs, argv[0]); - vty_out_statistics_partial(vty, "", level); + bool skip_zero = false; + if (argc > 1) + skip_zero = true; + vty_out_statistics_partial2(vty, "", level, skip_zero); return CMD_SUCCESS; } @@ -413,13 +470,6 @@ static int asciidoc_handle_counter(struct osmo_counter *counter, void *sctx_) static void asciidoc_counter_generate(struct vty *vty) { - if (osmo_counters_count() == 0) - { - vty_out(vty, "// there are no ungrouped osmo_counters%s", - VTY_NEWLINE); - return; - } - vty_out(vty, "// ungrouped osmo_counters%s", VTY_NEWLINE); vty_out(vty, ".ungrouped osmo counters%s", VTY_NEWLINE); vty_out(vty, "[options=\"header\"]%s", VTY_NEWLINE); @@ -480,10 +530,11 @@ static int asciidoc_osmo_stat_item_handler( { struct vty *vty = sctx_; - char *name = osmo_asciidoc_escape(item->desc->name); - char *description = osmo_asciidoc_escape(item->desc->description); + const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item); + char *name = osmo_asciidoc_escape(desc->name); + char *description = osmo_asciidoc_escape(desc->description); char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix); - char *unit = osmo_asciidoc_escape(item->desc->unit); + char *unit = osmo_asciidoc_escape(desc->unit); /* | Name | Reference | Description | Unit | */ vty_out(vty, "| %s | <<%s_%s>> | %s | %s%s", @@ -540,48 +591,97 @@ DEFUN(show_stats_asciidoc_table, vty_out(vty, "// generating tables for rate_ctr_group%s", VTY_NEWLINE); rate_ctr_for_each_group(asciidoc_rate_ctr_group_handler, vty); - vty_out(vty, "== Osmo Stat Items%s%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "=== Osmo Stat Items%s%s", VTY_NEWLINE, VTY_NEWLINE); vty_out(vty, "// generating tables for osmo_stat_items%s", VTY_NEWLINE); osmo_stat_item_for_each_group(asciidoc_osmo_stat_item_group_handler, vty); - vty_out(vty, "== Osmo Counters%s%s", VTY_NEWLINE, VTY_NEWLINE); - vty_out(vty, "// generating tables for osmo_counters%s", VTY_NEWLINE); - asciidoc_counter_generate(vty); + if (osmo_counters_count() == 0) + { + vty_out(vty, "// there are no ungrouped osmo_counters%s", + VTY_NEWLINE); + } else { + vty_out(vty, "=== Osmo Counters%s%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "// generating tables for osmo_counters%s", VTY_NEWLINE); + asciidoc_counter_generate(vty); + } return CMD_SUCCESS; } +struct rctr_vty_ctx { + struct vty *vty; + bool skip_zero; +}; + static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_) { - struct vty *vty = sctx_; - vty_out(vty, "%s %u:%s", ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE); - vty_out_rate_ctr_group_fmt(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg); + struct rctr_vty_ctx *sctx = sctx_; + struct vty *vty = sctx->vty; + vty_out(vty, "%s %u", ctrg->desc->group_description, ctrg->idx); + if (ctrg->name != NULL) + vty_out(vty, " (%s)", ctrg->name); + vty_out(vty, ":%s", VTY_NEWLINE); + vty_out_rate_ctr_group_fmt2(vty, "%25n: %10c (%S/s %M/m %H/h %D/d) %d", ctrg, sctx->skip_zero); return 0; } DEFUN(show_rate_counters, show_rate_counters_cmd, - "show rate-counters", - SHOW_STR "Show all rate counters\n") + "show rate-counters [skip-zero]", + SHOW_STR "Show all rate counters\n" SKIP_ZERO_STR) +{ + struct rctr_vty_ctx rctx = { .vty = vty, .skip_zero = false }; + if (argc > 0) + rctx.skip_zero = true; + rate_ctr_for_each_group(rate_ctr_group_handler, &rctx); + return CMD_SUCCESS; +} + +DEFUN(stats_report, + stats_report_cmd, + "stats report", + STATS_STR "Manurally trigger reporting of stats\n") +{ + osmo_stats_report(); + return CMD_SUCCESS; +} + +static int reset_rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *sctx_) +{ + rate_ctr_group_reset(ctrg); + return 0; +} + +DEFUN(stats_reset, + stats_reset_cmd, + "stats reset", + STATS_STR "Reset all rate counter stats\n") { - rate_ctr_for_each_group(rate_ctr_group_handler, vty); + rate_ctr_for_each_group(reset_rate_ctr_group_handler, NULL); return CMD_SUCCESS; } static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_reporter *srep) { - if (srep == NULL) - return 0; + const char *type = NULL; switch (srep->type) { case OSMO_STATS_REPORTER_STATSD: - vty_out(vty, "stats reporter statsd%s", VTY_NEWLINE); + type = "statsd"; break; case OSMO_STATS_REPORTER_LOG: - vty_out(vty, "stats reporter log%s", VTY_NEWLINE); + type = "log"; break; + default: + /* don't try to save unknown stats reporters to the VTY. Imagine some + * application registering a new application specific stats reporter that + * this VTY code knows nothing about! */ + return 0; } - vty_out(vty, " disable%s", VTY_NEWLINE); + vty_out(vty, "stats reporter %s", type); + if (srep->name != NULL) + vty_out(vty, " %s", srep->name); + vty_out(vty, "%s", VTY_NEWLINE); if (srep->have_net_config) { if (srep->dest_addr_str) @@ -615,6 +715,8 @@ static int config_write_stats_reporter(struct vty *vty, struct osmo_stats_report if (srep->enabled) vty_out(vty, " enable%s", VTY_NEWLINE); + else + vty_out(vty, " disable%s", VTY_NEWLINE); return 1; } @@ -623,13 +725,15 @@ static int config_write_stats(struct vty *vty) { struct osmo_stats_reporter *srep; - /* TODO: loop through all reporters */ - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_STATSD, NULL); - config_write_stats_reporter(vty, srep); - srep = osmo_stats_reporter_find(OSMO_STATS_REPORTER_LOG, NULL); - config_write_stats_reporter(vty, srep); - vty_out(vty, "stats interval %d%s", osmo_stats_config->interval, VTY_NEWLINE); + if (osmo_tcp_stats_config->interval != TCP_STATS_DEFAULT_INTERVAL) + vty_out(vty, "stats-tcp interval %d%s", osmo_tcp_stats_config->interval, VTY_NEWLINE); + if (osmo_tcp_stats_config->batch_size != TCP_STATS_DEFAULT_BATCH_SIZE) + vty_out(vty, "stats-tcp batch-size %d%s", osmo_tcp_stats_config->batch_size, VTY_NEWLINE); + + /* Loop through all reporters */ + llist_for_each_entry(srep, &osmo_stats_reporter_list, list) + config_write_stats_reporter(vty, srep); return 1; } @@ -638,32 +742,37 @@ static int config_write_stats(struct vty *vty) * Call this once during your application initialization if you would * like to have stats VTY commands enabled. */ -void osmo_stats_vty_add_cmds() +void osmo_stats_vty_add_cmds(void) { - install_element_ve(&show_stats_cmd); - install_element_ve(&show_stats_level_cmd); + install_lib_element_ve(&show_stats_cmd); + install_lib_element_ve(&show_stats_level_cmd); - install_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd); - install_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd); - install_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd); - install_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd); - install_element(CONFIG_NODE, &cfg_stats_interval_cmd); + install_lib_element(CONFIG_NODE, &cfg_stats_reporter_statsd_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_statsd_cmd); + install_lib_element(CONFIG_NODE, &cfg_stats_reporter_log_cmd); + install_lib_element(CONFIG_NODE, &cfg_no_stats_reporter_log_cmd); + install_lib_element(CONFIG_NODE, &cfg_stats_interval_cmd); + install_lib_element(CONFIG_NODE, &cfg_tcp_stats_interval_cmd); + install_lib_element(CONFIG_NODE, &cfg_tcp_stats_batch_size_cmd); install_node(&cfg_stats_node, config_write_stats); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd); - install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd); - install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd); - install_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd); - install_element(CFG_STATS_NODE, &cfg_stats_reporter_flush_period_cmd); - - install_element_ve(&show_stats_asciidoc_table_cmd); - install_element_ve(&show_rate_counters_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_local_ip_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_local_ip_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_ip_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_remote_port_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_mtu_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_mtu_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_prefix_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_no_stats_reporter_prefix_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_level_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_enable_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_disable_cmd); + install_lib_element(CFG_STATS_NODE, &cfg_stats_reporter_flush_period_cmd); + + install_lib_element_ve(&show_stats_asciidoc_table_cmd); + install_lib_element_ve(&show_rate_counters_cmd); + + install_lib_element(ENABLE_NODE, &stats_report_cmd); + install_lib_element(ENABLE_NODE, &stats_reset_cmd); } diff --git a/src/vty/talloc_ctx_vty.c b/src/vty/talloc_ctx_vty.c index 16cb7635..ea8ebe70 100644 --- a/src/vty/talloc_ctx_vty.c +++ b/src/vty/talloc_ctx_vty.c @@ -17,17 +17,13 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdio.h> #include <regex.h> #include <string.h> -#include <talloc.h> +#include <osmocom/core/talloc.h> #include <osmocom/vty/command.h> #include <osmocom/vty/vty.h> @@ -151,6 +147,8 @@ static void talloc_ctx_walk(const char *ctx, const char *depth, /* Determine a context for report */ if (!strncmp(ctx, "app", 3)) talloc_ctx = host.app_info->tall_ctx; + else if (!strcmp(ctx, "global")) + talloc_ctx = OTC_GLOBAL; else if (!strncmp(ctx, "all", 3)) talloc_ctx = NULL; @@ -167,11 +165,12 @@ static void talloc_ctx_walk(const char *ctx, const char *depth, } #define BASE_CMD_STR \ - "show talloc-context (application|all) (full|brief|DEPTH)" + "show talloc-context (application|global|all) (full|brief|DEPTH)" #define BASE_CMD_DESCR \ SHOW_STR "Show talloc memory hierarchy\n" \ "Application's context\n" \ + "Global context (OTC_GLOBAL)\n" \ "All contexts, if NULL-context tracking is enabled\n" \ "Display a full talloc memory hierarchy\n" \ "Display a brief talloc memory hierarchy\n" \ @@ -247,7 +246,7 @@ DEFUN(show_talloc_ctx_tree, show_talloc_ctx_tree_cmd, */ void osmo_talloc_vty_add_cmds(void) { - install_element_ve(&show_talloc_ctx_cmd); - install_element_ve(&show_talloc_ctx_tree_cmd); - install_element_ve(&show_talloc_ctx_filter_cmd); + install_lib_element_ve(&show_talloc_ctx_cmd); + install_lib_element_ve(&show_talloc_ctx_tree_cmd); + install_lib_element_ve(&show_talloc_ctx_filter_cmd); } diff --git a/src/vty/tdef_vty.c b/src/vty/tdef_vty.c index fe6d48ba..bd209ae0 100644 --- a/src/vty/tdef_vty.c +++ b/src/vty/tdef_vty.c @@ -50,10 +50,9 @@ */ struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *tdefs, const char *T_str) { - long l; + int l; int T; struct osmo_tdef *t; - char *endptr; const char *T_nr_str; int sign = 1; @@ -77,9 +76,7 @@ struct osmo_tdef *osmo_tdef_vty_parse_T_arg(struct vty *vty, struct osmo_tdef *t return NULL; } - errno = 0; - l = strtol(T_nr_str, &endptr, 10); - if (errno || *endptr || l > INT_MAX || l < 0) { + if (osmo_str_to_int(&l, T_nr_str, 10, 0, INT_MAX)) { vty_out(vty, "%% Invalid T timer argument (should be 'T1234' or 'X1234'): '%s'%s", T_str, VTY_NEWLINE); return NULL; } @@ -245,7 +242,7 @@ void osmo_tdef_vty_out_all(struct vty *vty, struct osmo_tdef *tdefs, const char /*! Write current timer configuration arguments to the vty. Skip all entries that reflect their default value. * The passed prefix string must contain both necessary indent and the VTY command the specific implementation is using. - * See tdef_vty_test_config_subnode.c and tdef_vty_test_dynamic.c for examples. + * See tdef_vty_config_subnode_test.c and tdef_vty_dynamic_test.c for examples. * \param[in] vty VTY context. * \param[in] tdefs Array of timers to print, ended with a fully zero-initialized entry. * \param[in] prefix_fmt Arbitrary string to start each line with, with variable printf like arguments. @@ -379,8 +376,8 @@ void osmo_tdef_vty_groups_init(unsigned int parent_cfg_node, struct osmo_tdef_gr cfg_timer_cmd.string = timer_command_string("timer", OSMO_TDEF_VTY_ARG_SET_OPTIONAL); cfg_timer_cmd.doc = timer_doc_string("Configure or show timers\n", OSMO_TDEF_VTY_DOC_SET); - install_element_ve(&show_timer_cmd); - install_element(parent_cfg_node, &cfg_timer_cmd); + install_lib_element_ve(&show_timer_cmd); + install_lib_element(parent_cfg_node, &cfg_timer_cmd); } /*! Write the global osmo_tdef_group configuration to VTY, as previously passed to osmo_tdef_vty_groups_init(). diff --git a/src/vty/telnet_interface.c b/src/vty/telnet_interface.c index 9aa36fe4..8fa5dbff 100644 --- a/src/vty/telnet_interface.c +++ b/src/vty/telnet_interface.c @@ -14,10 +14,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <sys/socket.h> @@ -46,7 +42,7 @@ * process in order to enable interactive command-line introspection, * interaction and configuration. * - * You typically call \ref telnet_init or \ref telnet_init_dynif once + * You typically call telnet_init_default once * from your application code to enable this. */ @@ -64,26 +60,14 @@ static struct osmo_fd server_socket = { .priv_nr = 0, }; -/*! Initialize telnet based VTY interface listening to 127.0.0.1 - * \param[in] tall_ctx \ref talloc context - * \param[in] priv private data to be passed to callback - * \param[in] port TCP port number to bind to - */ -int telnet_init(void *tall_ctx, void *priv, int port) -{ - return telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); -} - -/*! Initialize telnet based VTY interface - * \param[in] tall_ctx \ref talloc context - * \param[in] priv private data to be passed to callback - * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...) - * \param[in] port TCP port number to bind to - */ -int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) +/* Helper for deprecating telnet_init_dynif(), which previously held this code */ +static int _telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) { int rc; + if (port < 0) + return -EINVAL; + tall_telnet_ctx = talloc_named_const(tall_ctx, 1, "telnet_connection"); @@ -98,22 +82,45 @@ int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) if (rc < 0) { LOGP(DLGLOBAL, LOGL_ERROR, "Cannot bind telnet at %s %d\n", ip, port); - return -1; + return rc; } LOGP(DLGLOBAL, LOGL_NOTICE, "Available via telnet %s %d\n", ip, port); return 0; } +/*! Initialize telnet based VTY interface listening to 127.0.0.1 + * \param[in] tall_ctx \ref talloc context + * \param[in] priv private data to be passed to callback + * \param[in] port TCP port number to bind to + * \deprecated use telnet_init_default() instead + */ +int telnet_init(void *tall_ctx, void *priv, int port) +{ + return _telnet_init_dynif(tall_ctx, priv, "127.0.0.1", port); +} + +/*! Initialize telnet based VTY interface + * \param[in] tall_ctx \ref talloc context + * \param[in] priv private data to be passed to callback + * \param[in] ip IP to listen to ('::1' for localhost, '::0' for all, ...) + * \param[in] port TCP port number to bind to + * \deprecated use telnet_init_default() instead + */ +int telnet_init_dynif(void *tall_ctx, void *priv, const char *ip, int port) +{ + return _telnet_init_dynif(tall_ctx, priv, ip, port); +} + /*! Initializes telnet based VTY interface using the configured bind addr/port. * \param[in] tall_ctx \ref talloc context * \param[in] priv private data to be passed to callback - * \param[in] default_port TCP port number to bind to if not explicitely configured + * \param[in] default_port TCP port number to bind to if not explicitly configured */ int telnet_init_default(void *tall_ctx, void *priv, int default_port) { - return telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(), - vty_get_bind_port(default_port)); + return _telnet_init_dynif(tall_ctx, priv, vty_get_bind_addr(), + vty_get_bind_port(default_port)); } @@ -256,7 +263,7 @@ void vty_event(enum event event, int sock, struct vty *vty) } /*! Close all telnet connections and release the telnet socket */ -void telnet_exit(void) +void telnet_exit(void) { struct telnet_connection *tc, *tc2; diff --git a/src/vty/utils.c b/src/vty/utils.c index 0358d9bd..0f5a34e4 100644 --- a/src/vty/utils.c +++ b/src/vty/utils.c @@ -18,13 +18,10 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * */ #include <stdint.h> +#include <stdbool.h> #include <inttypes.h> #include <string.h> #include <ctype.h> @@ -48,6 +45,7 @@ struct vty_out_context { struct vty *vty; const char *prefix; int max_level; + bool skip_zero; }; static int rate_ctr_handler( @@ -57,6 +55,9 @@ static int rate_ctr_handler( struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; + if (vctx->skip_zero && ctr->current == 0) + return 0; + vty_out(vty, " %s%s: %8" PRIu64 " " "(%" PRIu64 "/s %" PRIu64 "/m %" PRIu64 "/h %" PRIu64 "/d)%s", vctx->prefix, desc->description, ctr->current, @@ -73,17 +74,24 @@ static int rate_ctr_handler( * \param[in] vty The VTY to which it should be printed * \param[in] prefix Any additional log prefix ahead of each line * \param[in] ctrg Rate counter group to be printed + * \param[in] skip_zero Skip all zero-valued counters */ -void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, - struct rate_ctr_group *ctrg) +void vty_out_rate_ctr_group2(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg, bool skip_zero) { - struct vty_out_context vctx = {vty, prefix}; + struct vty_out_context vctx = {vty, prefix, 0, skip_zero}; vty_out(vty, "%s%s:%s", prefix, ctrg->desc->group_description, VTY_NEWLINE); rate_ctr_for_each_counter(ctrg, rate_ctr_handler, &vctx); } +void vty_out_rate_ctr_group(struct vty *vty, const char *prefix, + struct rate_ctr_group *ctrg) +{ + vty_out_rate_ctr_group2(vty, prefix, ctrg, false); +} + static char * pad_append_str(char *s, const char *a, int minwidth) { @@ -107,7 +115,12 @@ static int rate_ctr_handler_fmt( struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; const char *fmt = vctx->prefix; - char *s = talloc_strdup(vty, ""); + char *s; + + if (vctx->skip_zero && ctr->current == 0) + return 0; + + s = talloc_strdup(vty, ""); OSMO_ASSERT(s); while (*fmt) { @@ -209,14 +222,20 @@ static int rate_ctr_handler_fmt( * \param[in] vty The VTY to which it should be printed * \param[in] ctrg Rate counter group to be printed * \param[in] fmt A format which may contain the above directives. + * \param[in] skip_zero Skip all zero-valued counters */ -void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt, - struct rate_ctr_group *ctrg) +void vty_out_rate_ctr_group_fmt2(struct vty *vty, const char *fmt, + struct rate_ctr_group *ctrg, bool skip_zero) { - struct vty_out_context vctx = {vty, fmt}; + struct vty_out_context vctx = {vty, fmt, 0, skip_zero}; rate_ctr_for_each_counter(ctrg, rate_ctr_handler_fmt, &vctx); } +void vty_out_rate_ctr_group_fmt(struct vty *vty, const char *fmt, + struct rate_ctr_group *ctrg) +{ + vty_out_rate_ctr_group_fmt2(vty, fmt, ctrg, false); +} static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_) { struct vty_out_context *vctx = vctx_; @@ -225,12 +244,10 @@ static int rate_ctr_group_handler(struct rate_ctr_group *ctrg, void *vctx_) if (ctrg->desc->class_id > vctx->max_level) return 0; - if (ctrg->idx) - vty_out(vty, "%s%s (%d):%s", vctx->prefix, - ctrg->desc->group_description, ctrg->idx, VTY_NEWLINE); - else - vty_out(vty, "%s%s:%s", vctx->prefix, - ctrg->desc->group_description, VTY_NEWLINE); + vty_out(vty, "%s%s (%d)", vctx->prefix, ctrg->desc->group_description, ctrg->idx); + if (ctrg->name) + vty_out(vty, "('%s')", ctrg->name); + vty_out(vty, ":%s", VTY_NEWLINE); rate_ctr_for_each_counter(ctrg, rate_ctr_handler, vctx); @@ -249,14 +266,15 @@ static int osmo_stat_item_handler( { struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; - const char *unit = - item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ? - item->desc->unit : ""; + const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item); + int32_t value = osmo_stat_item_get_last(item); + const char *unit = (desc->unit != OSMO_STAT_ITEM_NO_UNIT) ? desc->unit : ""; + + if (vctx->skip_zero && value == 0) + return 0; vty_out(vty, " %s%s: %8" PRIi32 " %s%s", - vctx->prefix, item->desc->description, - osmo_stat_item_get_last(item), - unit, VTY_NEWLINE); + vctx->prefix, desc->description, value, unit, VTY_NEWLINE); return 0; } @@ -265,17 +283,24 @@ static int osmo_stat_item_handler( * \param[in] vty The VTY to which it should be printed * \param[in] prefix Any additional log prefix ahead of each line * \param[in] statg Stat item group to be printed + * \param[in] skip_zero Skip all zero-valued counters */ -void vty_out_stat_item_group(struct vty *vty, const char *prefix, - struct osmo_stat_item_group *statg) +void vty_out_stat_item_group2(struct vty *vty, const char *prefix, + struct osmo_stat_item_group *statg, bool skip_zero) { - struct vty_out_context vctx = {vty, prefix}; + struct vty_out_context vctx = {vty, prefix, 0, skip_zero}; vty_out(vty, "%s%s:%s", prefix, statg->desc->group_description, VTY_NEWLINE); osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, &vctx); } +void vty_out_stat_item_group(struct vty *vty, const char *prefix, + struct osmo_stat_item_group *statg) +{ + return vty_out_stat_item_group2(vty, prefix, statg, false); +} + static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void *vctx_) { struct vty_out_context *vctx = vctx_; @@ -284,13 +309,10 @@ static int osmo_stat_item_group_handler(struct osmo_stat_item_group *statg, void if (statg->desc->class_id > vctx->max_level) return 0; - if (statg->idx) - vty_out(vty, "%s%s (%d):%s", vctx->prefix, - statg->desc->group_description, statg->idx, - VTY_NEWLINE); - else - vty_out(vty, "%s%s:%s", vctx->prefix, - statg->desc->group_description, VTY_NEWLINE); + vty_out(vty, "%s%s (%d)", vctx->prefix, statg->desc->group_description, statg->idx); + if (statg->name) + vty_out(vty, "('%s')", statg->name); + vty_out(vty, ":%s", VTY_NEWLINE); osmo_stat_item_for_each_item(statg, osmo_stat_item_handler, vctx); @@ -308,21 +330,22 @@ static int handle_counter(struct osmo_counter *counter, void *vctx_) struct vty_out_context *vctx = vctx_; struct vty *vty = vctx->vty; const char *description = counter->description; + unsigned long value = osmo_counter_get(counter); + + if (vctx->skip_zero && value == 0) + return 0; if (!counter->description) description = counter->name; - vty_out(vty, " %s%s: %8lu%s", - vctx->prefix, description, - osmo_counter_get(counter), VTY_NEWLINE); + vty_out(vty, " %s%s: %8lu%s", vctx->prefix, description, value, VTY_NEWLINE); return 0; } -void vty_out_statistics_partial(struct vty *vty, const char *prefix, - int max_level) +void vty_out_statistics_partial2(struct vty *vty, const char *prefix, int max_level, bool skip_zero) { - struct vty_out_context vctx = {vty, prefix, max_level}; + struct vty_out_context vctx = {vty, prefix, max_level, skip_zero}; vty_out(vty, "%sUngrouped counters:%s", prefix, VTY_NEWLINE); osmo_counters_for_each(handle_counter, &vctx); @@ -330,9 +353,19 @@ void vty_out_statistics_partial(struct vty *vty, const char *prefix, osmo_stat_item_for_each_group(osmo_stat_item_group_handler, &vctx); } +void vty_out_statistics_partial(struct vty *vty, const char *prefix, int max_level) +{ + return vty_out_statistics_partial2(vty, prefix, max_level, false); +} + +void vty_out_statistics_full2(struct vty *vty, const char *prefix, bool skip_zero) +{ + vty_out_statistics_partial2(vty, prefix, INT_MAX, skip_zero); +} + void vty_out_statistics_full(struct vty *vty, const char *prefix) { - vty_out_statistics_partial(vty, prefix, INT_MAX); + vty_out_statistics_full2(vty, prefix, false); } /*! Generate a VTY command string from value_string */ @@ -382,6 +415,7 @@ char *vty_cmd_string_from_valstr(void *ctx, const struct value_string *vals, if (ret < 0) goto err; OSMO_SNPRINTF_RET(ret, rem, offset, len); + (void)len; /* suppress warnings about len being set but not used */ err: str[size-1] = '\0'; return str; diff --git a/src/vty/vector.c b/src/vty/vector.c index f9e5ec3e..34b161d8 100644 --- a/src/vty/vector.c +++ b/src/vty/vector.c @@ -16,11 +16,6 @@ * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with GNU Zebra; see the file COPYING. If not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. */ #include <stdlib.h> diff --git a/src/vty/vty.c b/src/vty/vty.c index ebdf9fc9..3a549b43 100644 --- a/src/vty/vty.c +++ b/src/vty/vty.c @@ -66,6 +66,8 @@ #include <osmocom/vty/command.h> #include <osmocom/vty/buffer.h> #include <osmocom/core/talloc.h> +#include <osmocom/core/timer.h> +#include <osmocom/core/utils.h> #ifndef MAXPATHLEN #define MAXPATHLEN 4096 @@ -206,6 +208,12 @@ static void vty_auth(struct vty *vty) } } +void vty_flush(struct vty *vty) +{ + if (vty->obuf) + buffer_flush_all(vty->obuf, vty->fd); +} + /*! Close a given vty interface. */ void vty_close(struct vty *vty) { @@ -331,6 +339,25 @@ int vty_out_newline(struct vty *vty) return 0; } +/*! calculates the time difference of a give timespec to the current time + * and prints in a human readable format (days, hours, minutes, seconds). + */ +int vty_out_uptime(struct vty *vty, const struct timespec *starttime) +{ + struct timespec now; + struct timespec uptime; + + osmo_clock_gettime(CLOCK_MONOTONIC, &now); + timespecsub(&now, starttime, &uptime); + + int d = uptime.tv_sec / (3600 * 24); + int h = uptime.tv_sec / 3600 % 24; + int m = uptime.tv_sec / 60 % 60; + int s = uptime.tv_sec % 60; + + return vty_out(vty, "%dd %dh %dm %ds", d, h, m, s); +} + /*! return the current index of a given VTY */ void *vty_current_index(struct vty *vty) { @@ -458,6 +485,7 @@ static int vty_command(struct vty *vty) static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; +static const char telnet_escape_char = 0x1B; /* Basic function to write buffer to vty. */ static void vty_write(struct vty *vty, const char *buf, size_t nbytes) @@ -857,6 +885,19 @@ static void vty_down_level(struct vty *vty) vty->cp = 0; } +/* When '^L' is typed, clear all lines above the current one. */ +static void vty_clear_screen(struct vty *vty) +{ + vty_out(vty, "%c%s%c%s", + telnet_escape_char, + "[2J", /* Erase Screen */ + telnet_escape_char, + "[H" /* Cursor Home */ + ); + vty_prompt(vty); + vty_redraw_line(vty); +} + /* When '^Z' is received from vty, move down to the enable mode. */ static void vty_end_config(struct vty *vty) { @@ -1119,7 +1160,7 @@ static void vty_describe_command(struct vty *vty) int ret; vector vline; vector describe; - unsigned int i, width, desc_width; + unsigned int i, cmd_width, desc_width; struct desc *desc, *desc_cr = NULL; vline = cmd_make_strvec(vty->buf); @@ -1143,19 +1184,17 @@ static void vty_describe_command(struct vty *vty) vty_prompt(vty); vty_redraw_line(vty); return; - break; case CMD_ERR_NO_MATCH: cmd_free_strvec(vline); vty_out(vty, "%% There is no matched command.%s", VTY_NEWLINE); vty_prompt(vty); vty_redraw_line(vty); return; - break; } /* Get width of command string. */ - width = 0; - for (i = 0; i < vector_active(describe); i++) + cmd_width = 0; + for (i = 0; i < vector_active(describe); i++) { if ((desc = vector_slot(describe, i)) != NULL) { unsigned int len; @@ -1166,15 +1205,16 @@ static void vty_describe_command(struct vty *vty) if (desc->cmd[0] == '.') len--; - if (width < len) - width = len; + if (cmd_width < len) + cmd_width = len; } + } /* Get width of description string. */ - desc_width = vty->width - (width + 6); + desc_width = vty->width - (cmd_width + 6); /* Print out description. */ - for (i = 0; i < vector_active(describe); i++) + for (i = 0; i < vector_active(describe); i++) { if ((desc = vector_slot(describe, i)) != NULL) { if (desc->cmd[0] == '\0') continue; @@ -1190,19 +1230,20 @@ static void vty_describe_command(struct vty *vty) '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen(desc->str)) - vty_out(vty, " %-*s %s%s", width, + vty_out(vty, " %-*s %s%s", cmd_width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else - vty_describe_fold(vty, width, desc_width, desc); + vty_describe_fold(vty, cmd_width, desc_width, desc); #if 0 - vty_out(vty, " %-*s %s%s", width + vty_out(vty, " %-*s %s%s", cmd_width desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str ? desc->str : "", VTY_NEWLINE); #endif /* 0 */ } + } if ((desc = desc_cr)) { if (!desc->str) @@ -1210,11 +1251,11 @@ static void vty_describe_command(struct vty *vty) desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, VTY_NEWLINE); else if (desc_width >= strlen(desc->str)) - vty_out(vty, " %-*s %s%s", width, + vty_out(vty, " %-*s %s%s", cmd_width, desc->cmd[0] == '.' ? desc->cmd + 1 : desc->cmd, desc->str, VTY_NEWLINE); else - vty_describe_fold(vty, width, desc_width, desc); + vty_describe_fold(vty, cmd_width, desc_width, desc); } cmd_free_strvec(vline); @@ -1401,6 +1442,9 @@ int vty_read(struct vty *vty) case CONTROL('K'): vty_kill_line(vty); break; + case CONTROL('L'): + vty_clear_screen(vty); + break; case CONTROL('N'): vty_next_line(vty); break; @@ -1461,9 +1505,13 @@ int vty_read(struct vty *vty) return 0; } -/* Read up configuration file */ -static int -vty_read_file(FILE *confp, void *priv) +/* Read up configuration from a file stream */ +/*! Read up VTY configuration from a file stream + * \param[in] confp file pointer of the stream for the configuration file + * \param[in] priv private data to be passed to \ref vty_read_file + * \returns Zero on success, non-zero on error + */ +int vty_read_config_filep(FILE *confp, void *priv) { int ret; struct vty *vty; @@ -1794,6 +1842,8 @@ void vty_init_vtysh(void) /* Install vty's own commands like `who' command. */ void vty_init(struct vty_app_info *app_info) { + unsigned int i, j; + tall_vty_ctx = talloc_named_const(NULL, 0, "vty"); tall_vty_vec_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_vector"); tall_vty_cmd_ctx = talloc_named_const(tall_vty_ctx, 0, "vty_command"); @@ -1802,6 +1852,36 @@ void vty_init(struct vty_app_info *app_info) host.app_info = app_info; + /* Check for duplicate flags in application specific attributes (if any) */ + for (i = 0; i < ARRAY_SIZE(app_info->usr_attr_letters); i++) { + if (app_info->usr_attr_letters[i] == '\0') + continue; + + /* Some flag characters are reserved for global attributes */ + const char rafc[] = VTY_CMD_ATTR_FLAGS_RESERVED; + for (j = 0; j < ARRAY_SIZE(rafc); j++) { + if (app_info->usr_attr_letters[i] != rafc[j]) + continue; + fprintf(stderr, "Attribute flag character '%c' is reserved " + "for globals! Please fix.\n", app_info->usr_attr_letters[i]); + } + + /* Upper case flag letters are reserved for libraries */ + if (app_info->usr_attr_letters[i] >= 'A' && + app_info->usr_attr_letters[i] <= 'Z') { + fprintf(stderr, "Attribute flag letter '%c' is reserved " + "for libraries! Please fix.\n", app_info->usr_attr_letters[i]); + } + + for (j = i + 1; j < ARRAY_SIZE(app_info->usr_attr_letters); j++) { + if (app_info->usr_attr_letters[j] != app_info->usr_attr_letters[i]) + continue; + fprintf(stderr, "Found duplicate flag letter '%c' in application " + "specific attributes (index %u vs %u)! Please fix.\n", + app_info->usr_attr_letters[i], i, j); + } + } + /* For further configuration read, preserve current directory. */ vty_save_cwd(); @@ -1810,18 +1890,18 @@ void vty_init(struct vty_app_info *app_info) /* Install bgp top node. */ install_node(&vty_node, vty_config_write); - install_element_ve(&config_who_cmd); - install_element_ve(&show_history_cmd); - install_element(CONFIG_NODE, &line_vty_cmd); - install_element(CONFIG_NODE, &service_advanced_vty_cmd); - install_element(CONFIG_NODE, &no_service_advanced_vty_cmd); - install_element(CONFIG_NODE, &show_history_cmd); - install_element(ENABLE_NODE, &terminal_monitor_cmd); - install_element(ENABLE_NODE, &terminal_no_monitor_cmd); + install_lib_element_ve(&config_who_cmd); + install_lib_element_ve(&show_history_cmd); + install_lib_element(CONFIG_NODE, &line_vty_cmd); + install_lib_element(CONFIG_NODE, &service_advanced_vty_cmd); + install_lib_element(CONFIG_NODE, &no_service_advanced_vty_cmd); + install_lib_element(CONFIG_NODE, &show_history_cmd); + install_lib_element(ENABLE_NODE, &terminal_monitor_cmd); + install_lib_element(ENABLE_NODE, &terminal_no_monitor_cmd); - install_element(VTY_NODE, &vty_login_cmd); - install_element(VTY_NODE, &no_vty_login_cmd); - install_element(VTY_NODE, &vty_bind_cmd); + install_lib_element(VTY_NODE, &vty_login_cmd); + install_lib_element(VTY_NODE, &no_vty_login_cmd); + install_lib_element(VTY_NODE, &vty_bind_cmd); } /*! Read the configuration file using the VTY code @@ -1837,7 +1917,7 @@ int vty_read_config_file(const char *file_name, void *priv) if (!cfile) return -ENOENT; - rc = vty_read_file(cfile, priv); + rc = vty_read_config_filep(cfile, priv); fclose(cfile); host_config_set(file_name); |