aboutsummaryrefslogtreecommitdiffstats
path: root/src/vty/command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/vty/command.c')
-rw-r--r--src/vty/command.c235
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, &copy_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)
{