aboutsummaryrefslogtreecommitdiffstats
path: root/src/vty
diff options
context:
space:
mode:
Diffstat (limited to 'src/vty')
-rw-r--r--src/vty/Makefile.am10
-rw-r--r--src/vty/command.c847
-rw-r--r--src/vty/cpu_sched_vty.c682
-rw-r--r--src/vty/fsm_vty.c99
-rw-r--r--src/vty/logging_vty.c296
-rw-r--r--src/vty/stats_vty.c281
-rw-r--r--src/vty/talloc_ctx_vty.c17
-rw-r--r--src/vty/tdef_vty.c13
-rw-r--r--src/vty/telnet_interface.c61
-rw-r--r--src/vty/utils.c114
-rw-r--r--src/vty/vector.c5
-rw-r--r--src/vty/vty.c138
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, &copy_runningconfig_startupconfig_cmd);
+ 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_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(&param, 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, &param);
+ 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);