diff options
Diffstat (limited to 'src/vty/command.c')
-rw-r--r-- | src/vty/command.c | 235 |
1 files changed, 179 insertions, 56 deletions
diff --git a/src/vty/command.c b/src/vty/command.c index 7ea19712..1719690b 100644 --- a/src/vty/command.c +++ b/src/vty/command.c @@ -38,14 +38,18 @@ Boston, MA 02110-1301, USA. */ #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 @@ -63,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; @@ -70,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, @@ -727,7 +749,7 @@ static int vty_dump_element(const struct cmd_element *cmd, print_func_t print_fu char flag; /* Skip attribute if *not* set */ - if (~cmd->usrattr & (1 << i)) + if (~cmd->usrattr & ((unsigned)1 << i)) continue; xml_att_desc = xml_escape(desc[i]); @@ -773,8 +795,23 @@ 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; @@ -797,9 +834,9 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j); if (!vty_command_is_common(elem)) continue; - if (elem->attr & CMD_ATTR_DEPRECATED) + if (!match && (elem->attr & gflag_mask) != 0x00) continue; - if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN)) + if (match && (elem->attr & gflag_mask) == 0x00) continue; vty_dump_element(elem, print_func, data, newline); } @@ -835,9 +872,9 @@ static int vty_dump_nodes(print_func_t print_func, void *data, const char *newli const struct cmd_element *elem = vector_slot(cnode->cmd_vector, j); if (vty_command_is_common(elem)) continue; - if (elem->attr & CMD_ATTR_DEPRECATED) + if (!match && (elem->attr & gflag_mask) != 0x00) continue; - if (!host.expert_mode && (elem->attr & CMD_ATTR_HIDDEN)) + if (match && (elem->attr & gflag_mask) == 0x00) continue; vty_dump_element(elem, print_func, data, newline); } @@ -863,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, ...) @@ -879,12 +919,14 @@ static int print_func_stream(void *data, const char *format, ...) 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 } }; @@ -895,17 +937,27 @@ const struct value_string vty_ref_gen_mode_desc[] = { */ 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: - host.expert_mode = true; + /* 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: - host.expert_mode = false; + /* All commands except deprecated and hidden */ + gflag_mask = CMD_ATTR_DEPRECATED | CMD_ATTR_HIDDEN; break; } - return vty_dump_nodes(print_func_stream, stream, "\n"); + return vty_dump_nodes(print_func_stream, stream, "\n", gflag_mask, match); } /*! Print the XML reference of all VTY nodes to the given stream. @@ -1055,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; } @@ -1312,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; @@ -1410,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. @@ -1447,19 +1495,52 @@ static enum match_type cmd_ipv6_prefix_match(const char *str) #error "LONG_MAX not defined!" #endif -static int cmd_range_match(const char *range, const char *str) +/* 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_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; @@ -1471,7 +1552,9 @@ static int cmd_range_match(const char *range, const char *str) 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; @@ -1483,7 +1566,9 @@ static int cmd_range_match(const char *range, const char *str) 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; @@ -1495,7 +1580,7 @@ static int cmd_range_match(const char *range, const char *str) if (str[0] == '-') return 0; - val = strtoul(str, &endptr, 10); + val = strtoul(str, &endptr, val_base); if (*endptr != '\0') return 0; @@ -1507,7 +1592,9 @@ static int cmd_range_match(const char *range, const char *str) 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; @@ -1519,7 +1606,9 @@ static int cmd_range_match(const char *range, const char *str) 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; @@ -1527,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; } @@ -1570,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 @@ -1767,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, @@ -1860,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; @@ -2051,7 +2148,7 @@ cmd_describe_command_real(vector vline, struct vty *vty, int *status) if (cmd_element->attr & CMD_ATTR_DEPRECATED) continue; - if (!host.expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN)) + if (!vty->expert_mode && (cmd_element->attr & CMD_ATTR_HIDDEN)) continue; strvec = cmd_element->strvec; @@ -2356,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; @@ -2566,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); @@ -2841,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, }; @@ -2897,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)) @@ -2923,7 +3023,7 @@ DEFUN(enable, config_enable_cmd, else vty->node = AUTH_ENABLE_NODE; - host.expert_mode = argc > 0; + vty->expert_mode = argc > 0; return CMD_SUCCESS; } @@ -2935,7 +3035,7 @@ DEFUN(disable, if (vty->node == ENABLE_NODE) vty->node = VIEW_NODE; - host.expert_mode = false; + vty->expert_mode = false; return CMD_SUCCESS; } @@ -2988,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") @@ -3007,6 +3119,23 @@ 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") @@ -3123,7 +3252,7 @@ gDEFUN(show_vty_attr, show_vty_attr_cmd, } /* Compose flag bit-mask for all commands within the given node */ -static unsigned int node_flag_mask(const struct cmd_node *cnode) +static unsigned int node_flag_mask(const struct cmd_node *cnode, bool expert_mode) { unsigned int flag_mask = 0x00; unsigned int f, i; @@ -3137,7 +3266,7 @@ static unsigned int node_flag_mask(const struct cmd_node *cnode) continue; if (cmd->attr & CMD_ATTR_DEPRECATED) continue; - if (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) + if (!expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) continue; if (~cmd->usrattr & ((unsigned)1 << f)) continue; @@ -3221,14 +3350,14 @@ gDEFUN(config_list, config_list_cmd, struct cmd_element *cmd; if (argc > 0) - flag_mask = node_flag_mask(cnode); + 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 (!host.expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) + if (!vty->expert_mode && (cmd->attr & CMD_ATTR_HIDDEN)) continue; if (!argc) vty_out(vty, " %s%s", cmd->string, VTY_NEWLINE); @@ -3728,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; } @@ -3756,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; } @@ -4186,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(). @@ -4302,7 +4418,6 @@ void cmd_init(int terminal) host.lines = -1; host.motd = default_motd; host.motdfile = NULL; - host.expert_mode = false; /* Install top nodes. */ install_node_bare(&view_node, NULL); @@ -4312,6 +4427,8 @@ void cmd_init(int terminal) install_node(&config_node, config_write_host); /* Each node's basic commands. */ + 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) { @@ -4330,6 +4447,7 @@ void cmd_init(int terminal) 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_lib_element(ENABLE_NODE, &show_startup_config_cmd); install_lib_element(ENABLE_NODE, &show_version_cmd); @@ -4365,6 +4483,11 @@ void cmd_init(int terminal) 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) { |