Coverage Report

Created: 2026-01-21 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/frr/lib/lib_vty.c
Line
Count
Source
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
 * Assorted library VTY commands
4
 *
5
 * Copyright (C) 1998 Kunihiro Ishiguro
6
 * Copyright (C) 2016-2017  David Lamparter for NetDEF, Inc.
7
 */
8
9
#include <zebra.h>
10
/* malloc.h is generally obsolete, however GNU Libc mallinfo wants it. */
11
#ifdef HAVE_MALLOC_H
12
#include <malloc.h>
13
#endif
14
#ifdef HAVE_MALLOC_MALLOC_H
15
#include <malloc/malloc.h>
16
#endif
17
#include <dlfcn.h>
18
#ifdef HAVE_LINK_H
19
#include <link.h>
20
#endif
21
22
#include "log.h"
23
#include "memory.h"
24
#include "module.h"
25
#include "defaults.h"
26
#include "lib_vty.h"
27
#include "northbound_cli.h"
28
29
/* Looking up memory status from vty interface. */
30
#include "vector.h"
31
#include "vty.h"
32
#include "command.h"
33
34
#if defined(HAVE_MALLINFO2) || defined(HAVE_MALLINFO)
35
static int show_memory_mallinfo(struct vty *vty)
36
0
{
37
#if defined(HAVE_MALLINFO2)
38
  struct mallinfo2 minfo = mallinfo2();
39
#elif defined(HAVE_MALLINFO)
40
  struct mallinfo minfo = mallinfo();
41
0
#endif
42
0
  char buf[MTYPE_MEMSTR_LEN];
43
44
0
  vty_out(vty, "System allocator statistics:\n");
45
0
  vty_out(vty, "  Total heap allocated:  %s\n",
46
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.arena));
47
0
  vty_out(vty, "  Holding block headers: %s\n",
48
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.hblkhd));
49
0
  vty_out(vty, "  Used small blocks:     %s\n",
50
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.usmblks));
51
0
  vty_out(vty, "  Used ordinary blocks:  %s\n",
52
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.uordblks));
53
0
  vty_out(vty, "  Free small blocks:     %s\n",
54
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fsmblks));
55
0
  vty_out(vty, "  Free ordinary blocks:  %s\n",
56
0
    mtype_memstr(buf, MTYPE_MEMSTR_LEN, minfo.fordblks));
57
0
  vty_out(vty, "  Ordinary blocks:       %ld\n",
58
0
    (unsigned long)minfo.ordblks);
59
0
  vty_out(vty, "  Small blocks:          %ld\n",
60
0
    (unsigned long)minfo.smblks);
61
0
  vty_out(vty, "  Holding blocks:        %ld\n",
62
0
    (unsigned long)minfo.hblks);
63
0
  vty_out(vty, "(see system documentation for 'mallinfo' for meaning)\n");
64
0
  return 1;
65
0
}
66
#endif /* HAVE_MALLINFO */
67
68
static int qmem_walker(void *arg, struct memgroup *mg, struct memtype *mt)
69
0
{
70
0
  struct vty *vty = arg;
71
0
  if (!mt) {
72
0
    vty_out(vty, "--- qmem %s ---\n", mg->name);
73
0
    vty_out(vty, "%-30s: %8s %-8s%s %8s %9s\n",
74
0
      "Type", "Current#", "  Size",
75
#ifdef HAVE_MALLOC_USABLE_SIZE
76
      "     Total",
77
#else
78
0
      "",
79
0
#endif
80
0
      "Max#",
81
#ifdef HAVE_MALLOC_USABLE_SIZE
82
      "MaxBytes"
83
#else
84
0
      ""
85
0
#endif
86
0
      );
87
0
  } else {
88
0
    if (mt->n_max != 0) {
89
0
      char size[32];
90
0
      snprintf(size, sizeof(size), "%6zu", mt->size);
91
#ifdef HAVE_MALLOC_USABLE_SIZE
92
#define TSTR " %9zu"
93
#define TARG , mt->total
94
#define TARG2 , mt->max_size
95
#else
96
0
#define TSTR ""
97
0
#define TARG
98
0
#define TARG2
99
0
#endif
100
0
      vty_out(vty, "%-30s: %8zu %-8s"TSTR" %8zu"TSTR"\n",
101
0
        mt->name,
102
0
        mt->n_alloc,
103
0
        mt->size == 0 ? ""
104
0
                : mt->size == SIZE_VAR
105
0
              ? "variable"
106
0
              : size
107
0
        TARG,
108
0
        mt->n_max
109
0
        TARG2);
110
0
    }
111
0
  }
112
0
  return 0;
113
0
}
114
115
116
DEFUN_NOSH (show_memory,
117
      show_memory_cmd,
118
      "show memory",
119
      "Show running system information\n"
120
      "Memory statistics\n")
121
0
{
122
0
#ifdef HAVE_MALLINFO
123
0
  show_memory_mallinfo(vty);
124
0
#endif /* HAVE_MALLINFO */
125
126
0
  qmem_walk(qmem_walker, vty);
127
0
  return CMD_SUCCESS;
128
0
}
129
130
DEFUN_NOSH (show_modules,
131
      show_modules_cmd,
132
      "show modules",
133
      "Show running system information\n"
134
      "Loaded modules\n")
135
0
{
136
0
  struct frrmod_runtime *plug = frrmod_list;
137
138
0
  vty_out(vty, "%-12s %-25s %s\n\n", "Module Name", "Version",
139
0
    "Description");
140
0
  while (plug) {
141
0
    const struct frrmod_info *i = plug->info;
142
143
0
    vty_out(vty, "%-12s %-25s %s\n", i->name, i->version,
144
0
      i->description);
145
0
    if (plug->dl_handle) {
146
0
#ifdef HAVE_DLINFO_ORIGIN
147
0
      char origin[MAXPATHLEN] = "";
148
0
      dlinfo(plug->dl_handle, RTLD_DI_ORIGIN, &origin);
149
0
#ifdef HAVE_DLINFO_LINKMAP
150
0
      const char *name;
151
0
      struct link_map *lm = NULL;
152
0
      dlinfo(plug->dl_handle, RTLD_DI_LINKMAP, &lm);
153
0
      if (lm) {
154
0
        name = strrchr(lm->l_name, '/');
155
0
        name = name ? name + 1 : lm->l_name;
156
0
        vty_out(vty, "\tfrom: %s/%s\n", origin, name);
157
0
      }
158
#else
159
      vty_out(vty, "\tfrom: %s \n", origin, plug->load_name);
160
#endif
161
#else
162
      vty_out(vty, "\tfrom: %s\n", plug->load_name);
163
#endif
164
0
    }
165
0
    plug = plug->next;
166
0
  }
167
168
0
  vty_out(vty, "pid: %u\n", (uint32_t)(getpid()));
169
170
0
  return CMD_SUCCESS;
171
0
}
172
173
DEFUN (frr_defaults,
174
       frr_defaults_cmd,
175
       "frr defaults PROFILE...",
176
       "FRRouting global parameters\n"
177
       "set of configuration defaults used\n"
178
       "profile string\n")
179
0
{
180
0
  char *profile = argv_concat(argv, argc, 2);
181
0
  int rv = CMD_SUCCESS;
182
183
0
  if (!frr_defaults_profile_valid(profile)) {
184
0
    vty_out(vty, "%% WARNING: profile %s is not known in this version\n",
185
0
      profile);
186
0
    rv = CMD_WARNING;
187
0
  }
188
0
  frr_defaults_profile_set(profile);
189
0
  XFREE(MTYPE_TMP, profile);
190
0
  return rv;
191
0
}
192
193
DEFUN (frr_version,
194
       frr_version_cmd,
195
       "frr version VERSION...",
196
       "FRRouting global parameters\n"
197
       "version configuration was written by\n"
198
       "version string\n")
199
0
{
200
0
  char *version = argv_concat(argv, argc, 2);
201
202
0
  frr_defaults_version_set(version);
203
0
  XFREE(MTYPE_TMP, version);
204
0
  return CMD_SUCCESS;
205
0
}
206
207
static struct call_back {
208
  time_t readin_time;
209
210
  void (*start_config)(void);
211
  void (*end_config)(void);
212
} callback;
213
214
215
DEFUN_NOSH(start_config, start_config_cmd, "XFRR_start_configuration",
216
     "The Beginning of Configuration\n")
217
0
{
218
0
  callback.readin_time = monotime(NULL);
219
220
0
  vty->pending_allowed = 1;
221
222
0
  if (callback.start_config)
223
0
    (*callback.start_config)();
224
225
0
  return CMD_SUCCESS;
226
0
}
227
228
DEFUN_NOSH(end_config, end_config_cmd, "XFRR_end_configuration",
229
     "The End of Configuration\n")
230
0
{
231
0
  time_t readin_time;
232
0
  char readin_time_str[MONOTIME_STRLEN];
233
0
  int ret;
234
235
0
  readin_time = monotime(NULL);
236
0
  readin_time -= callback.readin_time;
237
238
0
  frrtime_to_interval(readin_time, readin_time_str,
239
0
          sizeof(readin_time_str));
240
241
0
  vty->pending_allowed = 0;
242
0
  ret = nb_cli_pending_commit_check(vty);
243
244
0
  zlog_info("Configuration Read in Took: %s", readin_time_str);
245
0
  zlog_debug("%s: VTY:%p, pending SET-CFG: %u", __func__, vty,
246
0
       (uint32_t)vty->mgmt_num_pending_setcfg);
247
248
  /*
249
   * If (and only if) we have sent any CLI config commands to MGMTd
250
   * FE interface using vty_mgmt_send_config_data() without implicit
251
   * commit before, should we need to send an explicit COMMIT-REQ now
252
   * to apply all those commands at once.
253
   */
254
0
  if (vty->mgmt_num_pending_setcfg && vty_mgmt_fe_enabled())
255
0
    vty_mgmt_send_commit_config(vty, false, false);
256
257
0
  if (callback.end_config)
258
0
    (*callback.end_config)();
259
260
0
  return ret;
261
0
}
262
263
void cmd_init_config_callbacks(void (*start_config_cb)(void),
264
             void (*end_config_cb)(void))
265
0
{
266
0
  callback.start_config = start_config_cb;
267
0
  callback.end_config = end_config_cb;
268
0
}
269
270
271
static void defaults_autocomplete(vector comps, struct cmd_token *token)
272
0
{
273
0
  const char **p;
274
275
0
  for (p = frr_defaults_profiles; *p; p++)
276
0
    vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p));
277
0
}
278
279
static const struct cmd_variable_handler default_var_handlers[] = {
280
  {.tokenname = "PROFILE", .completions = defaults_autocomplete},
281
  {.completions = NULL},
282
};
283
284
void lib_cmd_init(void)
285
0
{
286
0
  cmd_variable_handler_register(default_var_handlers);
287
288
0
  install_element(CONFIG_NODE, &frr_defaults_cmd);
289
0
  install_element(CONFIG_NODE, &frr_version_cmd);
290
291
0
  install_element(VIEW_NODE, &show_memory_cmd);
292
0
  install_element(VIEW_NODE, &show_modules_cmd);
293
294
0
  install_element(CONFIG_NODE, &start_config_cmd);
295
0
  install_element(CONFIG_NODE, &end_config_cmd);
296
0
}
297
298
/* Stats querying from users */
299
/* Return a pointer to a human friendly string describing
300
 * the byte count passed in. E.g:
301
 * "0 bytes", "2048 bytes", "110kB", "500MiB", "11GiB", etc.
302
 * Up to 4 significant figures will be given.
303
 * The pointer returned may be NULL (indicating an error)
304
 * or point to the given buffer, or point to static storage.
305
 */
306
const char *mtype_memstr(char *buf, size_t len, unsigned long bytes)
307
0
{
308
0
  unsigned int m, k;
309
310
  /* easy cases */
311
0
  if (!bytes)
312
0
    return "0 bytes";
313
0
  if (bytes == 1)
314
0
    return "1 byte";
315
316
  /*
317
   * When we pass the 2gb barrier mallinfo() can no longer report
318
   * correct data so it just does something odd...
319
   * Reporting like Terrabytes of data.  Which makes users...
320
   * edgy.. yes edgy that's the term for it.
321
   * So let's just give up gracefully
322
   */
323
0
  if (bytes > 0x7fffffff)
324
0
    return "> 2GB";
325
326
0
  m = bytes >> 20;
327
0
  k = bytes >> 10;
328
329
0
  if (m > 10) {
330
0
    if (bytes & (1 << 19))
331
0
      m++;
332
0
    snprintf(buf, len, "%d MiB", m);
333
0
  } else if (k > 10) {
334
0
    if (bytes & (1 << 9))
335
0
      k++;
336
0
    snprintf(buf, len, "%d KiB", k);
337
0
  } else
338
0
    snprintf(buf, len, "%ld bytes", bytes);
339
340
0
  return buf;
341
0
}