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 | } |