summaryrefslogtreecommitdiffstats
path: root/lib/lib_vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/lib_vty.c')
-rw-r--r--lib/lib_vty.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/lib/lib_vty.c b/lib/lib_vty.c
new file mode 100644
index 0000000..c13d88a
--- /dev/null
+++ b/lib/lib_vty.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Assorted library VTY commands
+ *
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ * Copyright (C) 2016-2017 David Lamparter for NetDEF, Inc.
+ */
+
+#include <zebra.h>
+/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#ifdef HAVE_MALLOC_MALLOC_H
+#include <malloc/malloc.h>
+#endif
+#include <dlfcn.h>
+#ifdef HAVE_LINK_H
+#include <link.h>
+#endif
+
+#include "log.h"
+#include "memory.h"
+#include "module.h"
+#include "defaults.h"
+#include "lib_vty.h"
+#include "northbound_cli.h"
+
+/* Looking up memory status from vty interface. */
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+
+#if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO)
+static int show_memory_mallinfo(struct vty *vty)
+{
+#if defined(HAVE_MALLINFO2)
+ struct mallinfo2 minfo = mallinfo2();
+#elif defined(HAVE_MALLINFO)
+ struct mallinfo minfo = mallinfo();
+#endif
+ char buf[MTYPE_MEMSTR_LEN];
+
+ vty_out(vty, "System allocator statistics:\n");
+ vty_out(vty, " Total heap allocated: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
+ vty_out(vty, " Holding block headers: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
+ vty_out(vty, " Used small blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
+ vty_out(vty, " Used ordinary blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
+ vty_out(vty, " Free small blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
+ vty_out(vty, " Free ordinary blocks: %s\n",
+ mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
+ vty_out(vty, " Ordinary blocks: %ld\n",
+ (unsigned long)minfo.ordblks);
+ vty_out(vty, " Small blocks: %ld\n",
+ (unsigned long)minfo.smblks);
+ vty_out(vty, " Holding blocks: %ld\n",
+ (unsigned long)minfo.hblks);
+ vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
+ return 1;
+}
+#endif /* HAVE_MALLINFO */
+
+static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
+{
+ struct vty *vty = arg;
+ if (!mt) {
+ vty_out(vty, "--- qmem %s ---\n", mg->name);
+ vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
+ "Type", "Current#", " Size",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+ " Total",
+#else
+ "",
+#endif
+ "Max#",
+#ifdef HAVE_MALLOC_USABLE_SIZE
+ "MaxBytes"
+#else
+ ""
+#endif
+ );
+ } else {
+ if (mt->n_max != 0) {
+ char size[32];
+ snprintf(size, sizeof(size), "%6zu", mt->size);
+#ifdef HAVE_MALLOC_USABLE_SIZE
+#define TSTR " %9zu"
+#define TARG , mt->total
+#define TARG2 , mt->max_size
+#else
+#define TSTR ""
+#define TARG
+#define TARG2
+#endif
+ vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
+ mt->name,
+ mt->n_alloc,
+ mt->size == 0 ? ""
+ : mt->size == SIZE_VAR
+ ? "variable"
+ : size
+ TARG,
+ mt->n_max
+ TARG2);
+ }
+ }
+ return 0;
+}
+
+
+DEFUN_NOSH (show_memory,
+ show_memory_cmd,
+ "show memory",
+ "Show running system information\n"
+ "Memory statistics\n")
+{
+#ifdef HAVE_MALLINFO
+ show_memory_mallinfo(vty);
+#endif /* HAVE_MALLINFO */
+
+ qmem_walk(qmem_walker, vty);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_modules,
+ show_modules_cmd,
+ "show modules",
+ "Show running system information\n"
+ "Loaded modules\n")
+{
+ struct frrmod_runtime *plug = frrmod_list;
+
+ vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
+ "Description");
+ while (plug) {
+ const struct frrmod_info *i = plug->info;
+
+ vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
+ i->description);
+ if (plug->dl_handle) {
+#ifdef HAVE_DLINFO_ORIGIN
+ char origin[MAXPATHLEN] = "";
+ dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
+#ifdef HAVE_DLINFO_LINKMAP
+ const char *name;
+ struct link_map *lm = NULL;
+ dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
+ if (lm) {
+ name = strrchr(lm->l_name, '/');
+ name = name ? name + 1 : lm->l_name;
+ vty_out(vty, "\tfrom: %s/%s\n", origin, name);
+ }
+#else
+ vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
+#endif
+#else
+ vty_out(vty, "\tfrom: %s\n", plug->load_name);
+#endif
+ }
+ plug = plug->next;
+ }
+
+ vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (frr_defaults,
+ frr_defaults_cmd,
+ "frr defaults PROFILE...",
+ "FRRouting global parameters\n"
+ "set of configuration defaults used\n"
+ "profile string\n")
+{
+ char *profile = argv_concat(argv, argc, 2);
+ int rv = CMD_SUCCESS;
+
+ if (!frr_defaults_profile_valid(profile)) {
+ vty_out(vty, "%% WARNING: profile %s is not known in this version\n",
+ profile);
+ rv = CMD_WARNING;
+ }
+ frr_defaults_profile_set(profile);
+ XFREE(MTYPE_TMP, profile);
+ return rv;
+}
+
+DEFUN (frr_version,
+ frr_version_cmd,
+ "frr version VERSION...",
+ "FRRouting global parameters\n"
+ "version configuration was written by\n"
+ "version string\n")
+{
+ char *version = argv_concat(argv, argc, 2);
+
+ frr_defaults_version_set(version);
+ XFREE(MTYPE_TMP, version);
+ return CMD_SUCCESS;
+}
+
+static struct call_back {
+ time_t readin_time;
+
+ void (*start_config)(void);
+ void (*end_config)(void);
+} callback;
+
+
+DEFUN_NOSH(start_config, start_config_cmd, "XFRR_start_configuration",
+ "The Beginning of Configuration\n")
+{
+ callback.readin_time = monotime(NULL);
+
+ vty->pending_allowed = 1;
+
+ if (callback.start_config)
+ (*callback.start_config)();
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH(end_config, end_config_cmd, "XFRR_end_configuration",
+ "The End of Configuration\n")
+{
+ time_t readin_time;
+ char readin_time_str[MONOTIME_STRLEN];
+ int ret;
+
+ readin_time = monotime(NULL);
+ readin_time -= callback.readin_time;
+
+ frrtime_to_interval(readin_time, readin_time_str,
+ sizeof(readin_time_str));
+
+ vty->pending_allowed = 0;
+ ret = nb_cli_pending_commit_check(vty);
+
+ zlog_info("Configuration Read in Took: %s", readin_time_str);
+ zlog_debug("%s: VTY:%p, pending SET-CFG: %u", __func__, vty,
+ (uint32_t)vty->mgmt_num_pending_setcfg);
+
+ /*
+ * If (and only if) we have sent any CLI config commands to MGMTd
+ * FE interface using vty_mgmt_send_config_data() without implicit
+ * commit before, should we need to send an explicit COMMIT-REQ now
+ * to apply all those commands at once.
+ */
+ if (vty->mgmt_num_pending_setcfg && vty_mgmt_fe_enabled())
+ vty_mgmt_send_commit_config(vty, false, false);
+
+ if (callback.end_config)
+ (*callback.end_config)();
+
+ return ret;
+}
+
+void cmd_init_config_callbacks(void (*start_config_cb)(void),
+ void (*end_config_cb)(void))
+{
+ callback.start_config = start_config_cb;
+ callback.end_config = end_config_cb;
+}
+
+
+static void defaults_autocomplete(vector comps, struct cmd_token *token)
+{
+ const char **p;
+
+ for (p = frr_defaults_profiles; *p; p++)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p));
+}
+
+static const struct cmd_variable_handler default_var_handlers[] = {
+ {.tokenname = "PROFILE", .completions = defaults_autocomplete},
+ {.completions = NULL},
+};
+
+void lib_cmd_init(void)
+{
+ cmd_variable_handler_register(default_var_handlers);
+
+ install_element(CONFIG_NODE, &frr_defaults_cmd);
+ install_element(CONFIG_NODE, &frr_version_cmd);
+
+ install_element(VIEW_NODE, &show_memory_cmd);
+ install_element(VIEW_NODE, &show_modules_cmd);
+
+ install_element(CONFIG_NODE, &start_config_cmd);
+ install_element(CONFIG_NODE, &end_config_cmd);
+}
+
+/* Stats querying from users */
+/* Return a pointer to a human friendly string describing
+ * the byte count passed in. E.g:
+ * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
+ * Up to 4 significant figures will be given.
+ * The pointer returned may be NULL (indicating an error)
+ * or point to the given buffer, or point to static storage.
+ */
+const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
+{
+ unsigned int m, k;
+
+ /* easy cases */
+ if (!bytes)
+ return "0 bytes";
+ if (bytes == 1)
+ return "1 byte";
+
+ /*
+ * When we pass the 2gb barrier mallinfo() can no longer report
+ * correct data so it just does something odd...
+ * Reporting like Terrabytes of data. Which makes users...
+ * edgy.. yes edgy that's the term for it.
+ * So let's just give up gracefully
+ */
+ if (bytes > 0x7fffffff)
+ return "> 2GB";
+
+ m = bytes >> 20;
+ k = bytes >> 10;
+
+ if (m > 10) {
+ if (bytes & (1 << 19))
+ m++;
+ snprintf(buf, len, "%d MiB", m);
+ } else if (k > 10) {
+ if (bytes & (1 << 9))
+ k++;
+ snprintf(buf, len, "%d KiB", k);
+ } else
+ snprintf(buf, len, "%ld bytes", bytes);
+
+ return buf;
+}