]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
*: move CLI node names to cmd_node->name
[mirror_frr.git] / lib / command.c
1 /*
2 * CLI backend interface.
3 *
4 * --
5 * Copyright (C) 2016 Cumulus Networks, Inc.
6 * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
7 * Copyright (C) 2013 by Open Source Routing.
8 * Copyright (C) 2013 by Internet Systems Consortium, Inc. ("ISC")
9 *
10 * This file is part of GNU Zebra.
11 *
12 * GNU Zebra is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2, or (at your option) any
15 * later version.
16 *
17 * GNU Zebra is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along
23 * with this program; see the file COPYING; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 */
26
27 #include <zebra.h>
28 #include <lib/version.h>
29
30 #include "command.h"
31 #include "frrstr.h"
32 #include "memory.h"
33 #include "log.h"
34 #include "log_int.h"
35 #include "thread.h"
36 #include "vector.h"
37 #include "linklist.h"
38 #include "vty.h"
39 #include "workqueue.h"
40 #include "vrf.h"
41 #include "command_match.h"
42 #include "command_graph.h"
43 #include "qobj.h"
44 #include "defaults.h"
45 #include "libfrr.h"
46 #include "jhash.h"
47 #include "hook.h"
48 #include "lib_errors.h"
49 #include "northbound_cli.h"
50
51 DEFINE_MTYPE_STATIC(LIB, HOST, "Host config")
52 DEFINE_MTYPE(LIB, COMPLETION, "Completion item")
53
54 #define item(x) \
55 { \
56 x, #x \
57 }
58
59 /* clang-format off */
60 const struct message tokennames[] = {
61 item(WORD_TKN),
62 item(VARIABLE_TKN),
63 item(RANGE_TKN),
64 item(IPV4_TKN),
65 item(IPV4_PREFIX_TKN),
66 item(IPV6_TKN),
67 item(IPV6_PREFIX_TKN),
68 item(MAC_TKN),
69 item(MAC_PREFIX_TKN),
70 item(FORK_TKN),
71 item(JOIN_TKN),
72 item(START_TKN),
73 item(END_TKN),
74 {0},
75 };
76 /* clang-format on */
77
78 /* Command vector which includes some level of command lists. Normally
79 each daemon maintains each own cmdvec. */
80 vector cmdvec = NULL;
81
82 /* Host information structure. */
83 struct host host;
84
85 /*
86 * Returns host.name if any, otherwise
87 * it returns the system hostname.
88 */
89 const char *cmd_hostname_get(void)
90 {
91 return host.name;
92 }
93
94 /*
95 * Returns unix domainname
96 */
97 const char *cmd_domainname_get(void)
98 {
99 return host.domainname;
100 }
101
102 /* Standard command node structures. */
103 static struct cmd_node auth_node = {
104 .name = "auth",
105 .node = AUTH_NODE,
106 .prompt = "Password: ",
107 };
108
109 static struct cmd_node view_node = {
110 .name = "view",
111 .node = VIEW_NODE,
112 .prompt = "%s> ",
113 };
114
115 static struct cmd_node auth_enable_node = {
116 .name = "auth enable",
117 .node = AUTH_ENABLE_NODE,
118 .prompt = "Password: ",
119 };
120
121 static struct cmd_node enable_node = {
122 .name = "enable",
123 .node = ENABLE_NODE,
124 .prompt = "%s# ",
125 };
126
127 static int config_write_host(struct vty *vty);
128 static struct cmd_node config_node = {
129 .name = "config",
130 .node = CONFIG_NODE,
131 .parent_node = ENABLE_NODE,
132 .prompt = "%s(config)# ",
133 .config_write = config_write_host,
134 };
135
136 static const struct facility_map {
137 int facility;
138 const char *name;
139 size_t match;
140 } syslog_facilities[] = {
141 {LOG_KERN, "kern", 1},
142 {LOG_USER, "user", 2},
143 {LOG_MAIL, "mail", 1},
144 {LOG_DAEMON, "daemon", 1},
145 {LOG_AUTH, "auth", 1},
146 {LOG_SYSLOG, "syslog", 1},
147 {LOG_LPR, "lpr", 2},
148 {LOG_NEWS, "news", 1},
149 {LOG_UUCP, "uucp", 2},
150 {LOG_CRON, "cron", 1},
151 #ifdef LOG_FTP
152 {LOG_FTP, "ftp", 1},
153 #endif
154 {LOG_LOCAL0, "local0", 6},
155 {LOG_LOCAL1, "local1", 6},
156 {LOG_LOCAL2, "local2", 6},
157 {LOG_LOCAL3, "local3", 6},
158 {LOG_LOCAL4, "local4", 6},
159 {LOG_LOCAL5, "local5", 6},
160 {LOG_LOCAL6, "local6", 6},
161 {LOG_LOCAL7, "local7", 6},
162 {0, NULL, 0},
163 };
164
165 static const char *facility_name(int facility)
166 {
167 const struct facility_map *fm;
168
169 for (fm = syslog_facilities; fm->name; fm++)
170 if (fm->facility == facility)
171 return fm->name;
172 return "";
173 }
174
175 static int facility_match(const char *str)
176 {
177 const struct facility_map *fm;
178
179 for (fm = syslog_facilities; fm->name; fm++)
180 if (!strncmp(str, fm->name, fm->match))
181 return fm->facility;
182 return -1;
183 }
184
185 static int level_match(const char *s)
186 {
187 int level;
188
189 for (level = 0; zlog_priority[level] != NULL; level++)
190 if (!strncmp(s, zlog_priority[level], 2))
191 return level;
192 return ZLOG_DISABLED;
193 }
194
195 /* This is called from main when a daemon is invoked with -v or --version. */
196 void print_version(const char *progname)
197 {
198 printf("%s version %s\n", progname, FRR_VERSION);
199 printf("%s\n", FRR_COPYRIGHT);
200 printf("configured with:\n\t%s\n", FRR_CONFIG_ARGS);
201 }
202
203 char *argv_concat(struct cmd_token **argv, int argc, int shift)
204 {
205 int cnt = MAX(argc - shift, 0);
206 const char *argstr[cnt + 1];
207
208 if (!cnt)
209 return NULL;
210
211 for (int i = 0; i < cnt; i++)
212 argstr[i] = argv[i + shift]->arg;
213
214 return frrstr_join(argstr, cnt, " ");
215 }
216
217 vector cmd_make_strvec(const char *string)
218 {
219 if (!string)
220 return NULL;
221
222 const char *copy = string;
223
224 /* skip leading whitespace */
225 while (isspace((unsigned char)*copy) && *copy != '\0')
226 copy++;
227
228 /* if the entire string was whitespace or a comment, return */
229 if (*copy == '\0' || *copy == '!' || *copy == '#')
230 return NULL;
231
232 vector result = frrstr_split_vec(copy, "\n\r\t ");
233
234 for (unsigned int i = 0; i < vector_active(result); i++) {
235 if (strlen(vector_slot(result, i)) == 0) {
236 XFREE(MTYPE_TMP, vector_slot(result, i));
237 vector_unset(result, i);
238 }
239 }
240
241 vector_compact(result);
242
243 return result;
244 }
245
246 void cmd_free_strvec(vector v)
247 {
248 frrstr_strvec_free(v);
249 }
250
251 /**
252 * Convenience function for accessing argv data.
253 *
254 * @param argc
255 * @param argv
256 * @param text definition snippet of the desired token
257 * @param index the starting index, and where to store the
258 * index of the found token if it exists
259 * @return 1 if found, 0 otherwise
260 */
261 int argv_find(struct cmd_token **argv, int argc, const char *text, int *index)
262 {
263 int found = 0;
264 for (int i = *index; i < argc && found == 0; i++)
265 if ((found = strmatch(text, argv[i]->text)))
266 *index = i;
267 return found;
268 }
269
270 static unsigned int cmd_hash_key(const void *p)
271 {
272 int size = sizeof(p);
273
274 return jhash(p, size, 0);
275 }
276
277 static bool cmd_hash_cmp(const void *a, const void *b)
278 {
279 return a == b;
280 }
281
282 /* Install top node of command vector. */
283 void install_node(struct cmd_node *node)
284 {
285 vector_set_index(cmdvec, node->node, node);
286 node->cmdgraph = graph_new();
287 node->cmd_vector = vector_init(VECTOR_MIN_SIZE);
288 // add start node
289 struct cmd_token *token =
290 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
291 graph_new_node(node->cmdgraph, token,
292 (void (*)(void *)) & cmd_token_del);
293 node->cmd_hash = hash_create_size(16, cmd_hash_key, cmd_hash_cmp,
294 "Command Hash");
295 }
296
297 /* Return prompt character of specified node. */
298 const char *cmd_prompt(enum node_type node)
299 {
300 struct cmd_node *cnode;
301
302 cnode = vector_slot(cmdvec, node);
303 return cnode->prompt;
304 }
305
306 /* Install a command into a node. */
307 void install_element(enum node_type ntype, const struct cmd_element *cmd)
308 {
309 struct cmd_node *cnode;
310
311 /* cmd_init hasn't been called */
312 if (!cmdvec) {
313 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
314 __func__);
315 return;
316 }
317
318 cnode = vector_lookup(cmdvec, ntype);
319
320 if (cnode == NULL) {
321 fprintf(stderr,
322 "%s[%s]:\n"
323 "\tnode %d does not exist.\n"
324 "\tplease call install_node() before install_element()\n",
325 cmd->name, cmd->string, ntype);
326 exit(EXIT_FAILURE);
327 }
328
329 if (hash_lookup(cnode->cmd_hash, (void *)cmd) != NULL) {
330 fprintf(stderr,
331 "%s[%s]:\n"
332 "\tnode %d (%s) already has this command installed.\n"
333 "\tduplicate install_element call?\n",
334 cmd->name, cmd->string, ntype, cnode->name);
335 return;
336 }
337
338 assert(hash_get(cnode->cmd_hash, (void *)cmd, hash_alloc_intern));
339
340 struct graph *graph = graph_new();
341 struct cmd_token *token =
342 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
343 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
344
345 cmd_graph_parse(graph, cmd);
346 cmd_graph_names(graph);
347 cmd_graph_merge(cnode->cmdgraph, graph, +1);
348 graph_delete_graph(graph);
349
350 vector_set(cnode->cmd_vector, (void *)cmd);
351
352 if (ntype == VIEW_NODE)
353 install_element(ENABLE_NODE, cmd);
354 }
355
356 void uninstall_element(enum node_type ntype, const struct cmd_element *cmd)
357 {
358 struct cmd_node *cnode;
359
360 /* cmd_init hasn't been called */
361 if (!cmdvec) {
362 fprintf(stderr, "%s called before cmd_init, breakage likely\n",
363 __func__);
364 return;
365 }
366
367 cnode = vector_lookup(cmdvec, ntype);
368
369 if (cnode == NULL) {
370 fprintf(stderr,
371 "%s[%s]:\n"
372 "\tnode %d does not exist.\n"
373 "\tplease call install_node() before uninstall_element()\n",
374 cmd->name, cmd->string, ntype);
375 exit(EXIT_FAILURE);
376 }
377
378 if (hash_release(cnode->cmd_hash, (void *)cmd) == NULL) {
379 fprintf(stderr,
380 "%s[%s]:\n"
381 "\tnode %d (%s) does not have this command installed.\n"
382 "\tduplicate uninstall_element call?\n",
383 cmd->name, cmd->string, ntype, cnode->name);
384 return;
385 }
386
387 vector_unset_value(cnode->cmd_vector, (void *)cmd);
388
389 struct graph *graph = graph_new();
390 struct cmd_token *token =
391 cmd_token_new(START_TKN, CMD_ATTR_NORMAL, NULL, NULL);
392 graph_new_node(graph, token, (void (*)(void *)) & cmd_token_del);
393
394 cmd_graph_parse(graph, cmd);
395 cmd_graph_names(graph);
396 cmd_graph_merge(cnode->cmdgraph, graph, -1);
397 graph_delete_graph(graph);
398
399 if (ntype == VIEW_NODE)
400 uninstall_element(ENABLE_NODE, cmd);
401 }
402
403
404 static const unsigned char itoa64[] =
405 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
406
407 static void to64(char *s, long v, int n)
408 {
409 while (--n >= 0) {
410 *s++ = itoa64[v & 0x3f];
411 v >>= 6;
412 }
413 }
414
415 static char *zencrypt(const char *passwd)
416 {
417 char salt[6];
418 struct timeval tv;
419 char *crypt(const char *, const char *);
420
421 gettimeofday(&tv, 0);
422
423 to64(&salt[0], random(), 3);
424 to64(&salt[3], tv.tv_usec, 3);
425 salt[5] = '\0';
426
427 return crypt(passwd, salt);
428 }
429
430 /* This function write configuration of this host. */
431 static int config_write_host(struct vty *vty)
432 {
433 if (cmd_hostname_get())
434 vty_out(vty, "hostname %s\n", cmd_hostname_get());
435
436 if (cmd_domainname_get())
437 vty_out(vty, "domainname %s\n", cmd_domainname_get());
438
439 /* The following are all configuration commands that are not sent to
440 * watchfrr. For instance watchfrr is hardcoded to log to syslog so
441 * we would always display 'log syslog informational' in the config
442 * which would cause other daemons to then switch to syslog when they
443 * parse frr.conf.
444 */
445 if (strcmp(zlog_default->protoname, "WATCHFRR")) {
446 if (host.encrypt) {
447 if (host.password_encrypt)
448 vty_out(vty, "password 8 %s\n",
449 host.password_encrypt);
450 if (host.enable_encrypt)
451 vty_out(vty, "enable password 8 %s\n",
452 host.enable_encrypt);
453 } else {
454 if (host.password)
455 vty_out(vty, "password %s\n", host.password);
456 if (host.enable)
457 vty_out(vty, "enable password %s\n",
458 host.enable);
459 }
460
461 if (host.logfile
462 && (zlog_default->maxlvl[ZLOG_DEST_FILE]
463 != ZLOG_DISABLED)) {
464 vty_out(vty, "log file %s", host.logfile);
465 if (zlog_default->maxlvl[ZLOG_DEST_FILE]
466 != zlog_default->default_lvl)
467 vty_out(vty, " %s",
468 zlog_priority
469 [zlog_default->maxlvl
470 [ZLOG_DEST_FILE]]);
471 vty_out(vty, "\n");
472 }
473
474 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT] != ZLOG_DISABLED) {
475 vty_out(vty, "log stdout");
476 if (zlog_default->maxlvl[ZLOG_DEST_STDOUT]
477 != zlog_default->default_lvl)
478 vty_out(vty, " %s",
479 zlog_priority
480 [zlog_default->maxlvl
481 [ZLOG_DEST_STDOUT]]);
482 vty_out(vty, "\n");
483 }
484
485 if (zlog_default->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
486 vty_out(vty, "no log monitor\n");
487 else if (zlog_default->maxlvl[ZLOG_DEST_MONITOR]
488 != zlog_default->default_lvl)
489 vty_out(vty, "log monitor %s\n",
490 zlog_priority[zlog_default->maxlvl
491 [ZLOG_DEST_MONITOR]]);
492
493 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED) {
494 vty_out(vty, "log syslog");
495 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG]
496 != zlog_default->default_lvl)
497 vty_out(vty, " %s",
498 zlog_priority[zlog_default->maxlvl
499 [ZLOG_DEST_SYSLOG]]);
500 vty_out(vty, "\n");
501 }
502
503 if (zlog_default->facility != LOG_DAEMON)
504 vty_out(vty, "log facility %s\n",
505 facility_name(zlog_default->facility));
506
507 if (zlog_default->record_priority == 1)
508 vty_out(vty, "log record-priority\n");
509
510 if (zlog_default->timestamp_precision > 0)
511 vty_out(vty, "log timestamp precision %d\n",
512 zlog_default->timestamp_precision);
513
514 if (host.advanced)
515 vty_out(vty, "service advanced-vty\n");
516
517 if (host.encrypt)
518 vty_out(vty, "service password-encryption\n");
519
520 if (host.lines >= 0)
521 vty_out(vty, "service terminal-length %d\n",
522 host.lines);
523
524 if (host.motdfile)
525 vty_out(vty, "banner motd file %s\n", host.motdfile);
526 else if (host.motd
527 && strncmp(host.motd, FRR_DEFAULT_MOTD,
528 strlen(host.motd)))
529 vty_out(vty, "banner motd line %s\n", host.motd);
530 else if (!host.motd)
531 vty_out(vty, "no banner motd\n");
532 }
533
534 if (debug_memstats_at_exit)
535 vty_out(vty, "!\ndebug memstats-at-exit\n");
536
537 return 1;
538 }
539
540 /* Utility function for getting command graph. */
541 static struct graph *cmd_node_graph(vector v, enum node_type ntype)
542 {
543 struct cmd_node *cnode = vector_slot(v, ntype);
544 return cnode->cmdgraph;
545 }
546
547 static int cmd_try_do_shortcut(enum node_type node, char *first_word)
548 {
549 if (first_word != NULL && node != AUTH_NODE && node != VIEW_NODE
550 && node != AUTH_ENABLE_NODE && 0 == strcmp("do", first_word))
551 return 1;
552 return 0;
553 }
554
555 /**
556 * Compare function for cmd_token.
557 * Used with qsort to sort command completions.
558 */
559 static int compare_completions(const void *fst, const void *snd)
560 {
561 const struct cmd_token *first = *(const struct cmd_token * const *)fst,
562 *secnd = *(const struct cmd_token * const *)snd;
563 return strcmp(first->text, secnd->text);
564 }
565
566 /**
567 * Takes a list of completions returned by command_complete,
568 * dedeuplicates them based on both text and description,
569 * sorts them, and returns them as a vector.
570 *
571 * @param completions linked list of cmd_token
572 * @return deduplicated and sorted vector with
573 */
574 vector completions_to_vec(struct list *completions)
575 {
576 vector comps = vector_init(VECTOR_MIN_SIZE);
577
578 struct listnode *ln;
579 struct cmd_token *token, *cr = NULL;
580 unsigned int i, exists;
581 for (ALL_LIST_ELEMENTS_RO(completions, ln, token)) {
582 if (token->type == END_TKN && (cr = token))
583 continue;
584
585 // linear search for token in completions vector
586 exists = 0;
587 for (i = 0; i < vector_active(comps) && !exists; i++) {
588 struct cmd_token *curr = vector_slot(comps, i);
589 #ifdef VTYSH_DEBUG
590 exists = !strcmp(curr->text, token->text)
591 && !strcmp(curr->desc, token->desc);
592 #else
593 exists = !strcmp(curr->text, token->text);
594 #endif /* VTYSH_DEBUG */
595 }
596
597 if (!exists)
598 vector_set(comps, token);
599 }
600
601 // sort completions
602 qsort(comps->index, vector_active(comps), sizeof(void *),
603 &compare_completions);
604
605 // make <cr> the first element, if it is present
606 if (cr) {
607 vector_set_index(comps, vector_active(comps), NULL);
608 memmove(comps->index + 1, comps->index,
609 (comps->alloced - 1) * sizeof(void *));
610 vector_set_index(comps, 0, cr);
611 }
612
613 return comps;
614 }
615 /**
616 * Generates a vector of cmd_token representing possible completions
617 * on the current input.
618 *
619 * @param vline the vectorized input line
620 * @param vty the vty with the node to match on
621 * @param status pointer to matcher status code
622 * @return vector of struct cmd_token * with possible completions
623 */
624 static vector cmd_complete_command_real(vector vline, struct vty *vty,
625 int *status)
626 {
627 struct list *completions;
628 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
629
630 enum matcher_rv rv = command_complete(cmdgraph, vline, &completions);
631
632 if (MATCHER_ERROR(rv)) {
633 *status = CMD_ERR_NO_MATCH;
634 return NULL;
635 }
636
637 vector comps = completions_to_vec(completions);
638 list_delete(&completions);
639
640 // set status code appropriately
641 switch (vector_active(comps)) {
642 case 0:
643 *status = CMD_ERR_NO_MATCH;
644 break;
645 case 1:
646 *status = CMD_COMPLETE_FULL_MATCH;
647 break;
648 default:
649 *status = CMD_COMPLETE_LIST_MATCH;
650 }
651
652 return comps;
653 }
654
655 vector cmd_describe_command(vector vline, struct vty *vty, int *status)
656 {
657 vector ret;
658
659 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
660 enum node_type onode;
661 int orig_xpath_index;
662 vector shifted_vline;
663 unsigned int index;
664
665 onode = vty->node;
666 orig_xpath_index = vty->xpath_index;
667 vty->node = ENABLE_NODE;
668 vty->xpath_index = 0;
669 /* We can try it on enable node, cos' the vty is authenticated
670 */
671
672 shifted_vline = vector_init(vector_count(vline));
673 /* use memcpy? */
674 for (index = 1; index < vector_active(vline); index++) {
675 vector_set_index(shifted_vline, index - 1,
676 vector_lookup(vline, index));
677 }
678
679 ret = cmd_complete_command_real(shifted_vline, vty, status);
680
681 vector_free(shifted_vline);
682 vty->node = onode;
683 vty->xpath_index = orig_xpath_index;
684 return ret;
685 }
686
687 return cmd_complete_command_real(vline, vty, status);
688 }
689
690 static struct list *varhandlers = NULL;
691
692 void cmd_variable_complete(struct cmd_token *token, const char *arg,
693 vector comps)
694 {
695 struct listnode *ln;
696 const struct cmd_variable_handler *cvh;
697 size_t i, argsz;
698 vector tmpcomps;
699
700 tmpcomps = arg ? vector_init(VECTOR_MIN_SIZE) : comps;
701
702 for (ALL_LIST_ELEMENTS_RO(varhandlers, ln, cvh)) {
703 if (cvh->tokenname && strcmp(cvh->tokenname, token->text))
704 continue;
705 if (cvh->varname && (!token->varname
706 || strcmp(cvh->varname, token->varname)))
707 continue;
708 cvh->completions(tmpcomps, token);
709 break;
710 }
711
712 if (!arg)
713 return;
714
715 argsz = strlen(arg);
716 for (i = vector_active(tmpcomps); i; i--) {
717 char *item = vector_slot(tmpcomps, i - 1);
718 if (strlen(item) >= argsz && !strncmp(item, arg, argsz))
719 vector_set(comps, item);
720 else
721 XFREE(MTYPE_COMPLETION, item);
722 }
723 vector_free(tmpcomps);
724 }
725
726 #define AUTOCOMP_INDENT 5
727
728 char *cmd_variable_comp2str(vector comps, unsigned short cols)
729 {
730 size_t bsz = 16;
731 char *buf = XCALLOC(MTYPE_TMP, bsz);
732 int lc = AUTOCOMP_INDENT;
733 size_t cs = AUTOCOMP_INDENT;
734 size_t itemlen;
735 snprintf(buf, bsz, "%*s", AUTOCOMP_INDENT, "");
736 for (size_t j = 0; j < vector_active(comps); j++) {
737 char *item = vector_slot(comps, j);
738 itemlen = strlen(item);
739
740 if (cs + itemlen + AUTOCOMP_INDENT + 3 >= bsz)
741 buf = XREALLOC(MTYPE_TMP, buf, (bsz *= 2));
742
743 if (lc + itemlen + 1 >= cols) {
744 cs += snprintf(&buf[cs], bsz - cs, "\n%*s",
745 AUTOCOMP_INDENT, "");
746 lc = AUTOCOMP_INDENT;
747 }
748
749 size_t written = snprintf(&buf[cs], bsz - cs, "%s ", item);
750 lc += written;
751 cs += written;
752 XFREE(MTYPE_COMPLETION, item);
753 vector_set_index(comps, j, NULL);
754 }
755 return buf;
756 }
757
758 void cmd_variable_handler_register(const struct cmd_variable_handler *cvh)
759 {
760 if (!varhandlers)
761 return;
762
763 for (; cvh->completions; cvh++)
764 listnode_add(varhandlers, (void *)cvh);
765 }
766
767 DEFUN_HIDDEN (autocomplete,
768 autocomplete_cmd,
769 "autocomplete TYPE TEXT VARNAME",
770 "Autocompletion handler (internal, for vtysh)\n"
771 "cmd_token->type\n"
772 "cmd_token->text\n"
773 "cmd_token->varname\n")
774 {
775 struct cmd_token tok;
776 vector comps = vector_init(32);
777 size_t i;
778
779 memset(&tok, 0, sizeof(tok));
780 tok.type = atoi(argv[1]->arg);
781 tok.text = argv[2]->arg;
782 tok.varname = argv[3]->arg;
783 if (!strcmp(tok.varname, "-"))
784 tok.varname = NULL;
785
786 cmd_variable_complete(&tok, NULL, comps);
787
788 for (i = 0; i < vector_active(comps); i++) {
789 char *text = vector_slot(comps, i);
790 vty_out(vty, "%s\n", text);
791 XFREE(MTYPE_COMPLETION, text);
792 }
793
794 vector_free(comps);
795 return CMD_SUCCESS;
796 }
797
798 /**
799 * Generate possible tab-completions for the given input. This function only
800 * returns results that would result in a valid command if used as Readline
801 * completions (as is the case in vtysh). For instance, if the passed vline ends
802 * with '4.3.2', the strings 'A.B.C.D' and 'A.B.C.D/M' will _not_ be returned.
803 *
804 * @param vline vectorized input line
805 * @param vty the vty
806 * @param status location to store matcher status code in
807 * @return set of valid strings for use with Readline as tab-completions.
808 */
809
810 char **cmd_complete_command(vector vline, struct vty *vty, int *status)
811 {
812 char **ret = NULL;
813 int original_node = vty->node;
814 vector input_line = vector_init(vector_count(vline));
815
816 // if the first token is 'do' we'll want to execute the command in the
817 // enable node
818 int do_shortcut = cmd_try_do_shortcut(vty->node, vector_slot(vline, 0));
819 vty->node = do_shortcut ? ENABLE_NODE : original_node;
820
821 // construct the input line we'll be matching on
822 unsigned int offset = (do_shortcut) ? 1 : 0;
823 for (unsigned index = 0; index + offset < vector_active(vline); index++)
824 vector_set_index(input_line, index,
825 vector_lookup(vline, index + offset));
826
827 // get token completions -- this is a copying operation
828 vector comps = NULL, initial_comps;
829 initial_comps = cmd_complete_command_real(input_line, vty, status);
830
831 if (!MATCHER_ERROR(*status)) {
832 assert(initial_comps);
833 // filter out everything that is not suitable for a
834 // tab-completion
835 comps = vector_init(VECTOR_MIN_SIZE);
836 for (unsigned int i = 0; i < vector_active(initial_comps);
837 i++) {
838 struct cmd_token *token = vector_slot(initial_comps, i);
839 if (token->type == WORD_TKN)
840 vector_set(comps, XSTRDUP(MTYPE_COMPLETION,
841 token->text));
842 else if (IS_VARYING_TOKEN(token->type)) {
843 const char *ref = vector_lookup(
844 vline, vector_active(vline) - 1);
845 cmd_variable_complete(token, ref, comps);
846 }
847 }
848 vector_free(initial_comps);
849
850 // since we filtered results, we need to re-set status code
851 switch (vector_active(comps)) {
852 case 0:
853 *status = CMD_ERR_NO_MATCH;
854 break;
855 case 1:
856 *status = CMD_COMPLETE_FULL_MATCH;
857 break;
858 default:
859 *status = CMD_COMPLETE_LIST_MATCH;
860 }
861
862 // copy completions text into an array of char*
863 ret = XMALLOC(MTYPE_TMP,
864 (vector_active(comps) + 1) * sizeof(char *));
865 unsigned int i;
866 for (i = 0; i < vector_active(comps); i++) {
867 ret[i] = vector_slot(comps, i);
868 }
869 // set the last element to NULL, because this array is used in
870 // a Readline completion_generator function which expects NULL
871 // as a sentinel value
872 ret[i] = NULL;
873 vector_free(comps);
874 comps = NULL;
875 } else if (initial_comps)
876 vector_free(initial_comps);
877
878 // comps should always be null here
879 assert(!comps);
880
881 // free the adjusted input line
882 vector_free(input_line);
883
884 // reset vty->node to its original value
885 vty->node = original_node;
886
887 return ret;
888 }
889
890 /* return parent node */
891 /* MUST eventually converge on CONFIG_NODE */
892 enum node_type node_parent(enum node_type node)
893 {
894 enum node_type ret;
895
896 assert(node > CONFIG_NODE);
897
898 switch (node) {
899 case BGP_VPNV4_NODE:
900 case BGP_VPNV6_NODE:
901 case BGP_FLOWSPECV4_NODE:
902 case BGP_FLOWSPECV6_NODE:
903 case BGP_VRF_POLICY_NODE:
904 case BGP_VNC_DEFAULTS_NODE:
905 case BGP_VNC_NVE_GROUP_NODE:
906 case BGP_VNC_L2_GROUP_NODE:
907 case BGP_IPV4_NODE:
908 case BGP_IPV4M_NODE:
909 case BGP_IPV4L_NODE:
910 case BGP_IPV6_NODE:
911 case BGP_IPV6M_NODE:
912 case BGP_EVPN_NODE:
913 case BGP_IPV6L_NODE:
914 case BMP_NODE:
915 ret = BGP_NODE;
916 break;
917 case BGP_EVPN_VNI_NODE:
918 ret = BGP_EVPN_NODE;
919 break;
920 case KEYCHAIN_KEY_NODE:
921 ret = KEYCHAIN_NODE;
922 break;
923 case LINK_PARAMS_NODE:
924 ret = INTERFACE_NODE;
925 break;
926 case LDP_IPV4_NODE:
927 case LDP_IPV6_NODE:
928 ret = LDP_NODE;
929 break;
930 case LDP_IPV4_IFACE_NODE:
931 ret = LDP_IPV4_NODE;
932 break;
933 case LDP_IPV6_IFACE_NODE:
934 ret = LDP_IPV6_NODE;
935 break;
936 case LDP_PSEUDOWIRE_NODE:
937 ret = LDP_L2VPN_NODE;
938 break;
939 case BFD_PEER_NODE:
940 ret = BFD_NODE;
941 break;
942 default:
943 ret = CONFIG_NODE;
944 break;
945 }
946
947 return ret;
948 }
949
950 /* Execute command by argument vline vector. */
951 static int cmd_execute_command_real(vector vline, enum cmd_filter_type filter,
952 struct vty *vty,
953 const struct cmd_element **cmd)
954 {
955 struct list *argv_list;
956 enum matcher_rv status;
957 const struct cmd_element *matched_element = NULL;
958
959 struct graph *cmdgraph = cmd_node_graph(cmdvec, vty->node);
960 status = command_match(cmdgraph, vline, &argv_list, &matched_element);
961
962 if (cmd)
963 *cmd = matched_element;
964
965 // if matcher error, return corresponding CMD_ERR
966 if (MATCHER_ERROR(status)) {
967 if (argv_list)
968 list_delete(&argv_list);
969 switch (status) {
970 case MATCHER_INCOMPLETE:
971 return CMD_ERR_INCOMPLETE;
972 case MATCHER_AMBIGUOUS:
973 return CMD_ERR_AMBIGUOUS;
974 default:
975 return CMD_ERR_NO_MATCH;
976 }
977 }
978
979 // build argv array from argv list
980 struct cmd_token **argv = XMALLOC(
981 MTYPE_TMP, argv_list->count * sizeof(struct cmd_token *));
982 struct listnode *ln;
983 struct cmd_token *token;
984 unsigned int i = 0;
985 for (ALL_LIST_ELEMENTS_RO(argv_list, ln, token))
986 argv[i++] = token;
987
988 int argc = argv_list->count;
989
990 int ret;
991 if (matched_element->daemon)
992 ret = CMD_SUCCESS_DAEMON;
993 else {
994 if (vty->config) {
995 /* Clear array of enqueued configuration changes. */
996 vty->num_cfg_changes = 0;
997 memset(&vty->cfg_changes, 0, sizeof(vty->cfg_changes));
998
999 /* Regenerate candidate configuration if necessary. */
1000 if (frr_get_cli_mode() == FRR_CLI_CLASSIC
1001 && running_config->version
1002 > vty->candidate_config->version)
1003 nb_config_replace(vty->candidate_config,
1004 running_config, true);
1005 }
1006
1007 ret = matched_element->func(matched_element, vty, argc, argv);
1008 }
1009
1010 // delete list and cmd_token's in it
1011 list_delete(&argv_list);
1012 XFREE(MTYPE_TMP, argv);
1013
1014 return ret;
1015 }
1016
1017 /**
1018 * Execute a given command, handling things like "do ..." and checking
1019 * whether the given command might apply at a parent node if doesn't
1020 * apply for the current node.
1021 *
1022 * @param vline Command line input, vector of char* where each element is
1023 * one input token.
1024 * @param vty The vty context in which the command should be executed.
1025 * @param cmd Pointer where the struct cmd_element of the matched command
1026 * will be stored, if any. May be set to NULL if this info is
1027 * not needed.
1028 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1029 * @return The status of the command that has been executed or an error code
1030 * as to why no command could be executed.
1031 */
1032 int cmd_execute_command(vector vline, struct vty *vty,
1033 const struct cmd_element **cmd, int vtysh)
1034 {
1035 int ret, saved_ret = 0;
1036 enum node_type onode, try_node;
1037 int orig_xpath_index;
1038
1039 onode = try_node = vty->node;
1040 orig_xpath_index = vty->xpath_index;
1041
1042 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0))) {
1043 vector shifted_vline;
1044 unsigned int index;
1045
1046 vty->node = ENABLE_NODE;
1047 vty->xpath_index = 0;
1048 /* We can try it on enable node, cos' the vty is authenticated
1049 */
1050
1051 shifted_vline = vector_init(vector_count(vline));
1052 /* use memcpy? */
1053 for (index = 1; index < vector_active(vline); index++)
1054 vector_set_index(shifted_vline, index - 1,
1055 vector_lookup(vline, index));
1056
1057 ret = cmd_execute_command_real(shifted_vline, FILTER_RELAXED,
1058 vty, cmd);
1059
1060 vector_free(shifted_vline);
1061 vty->node = onode;
1062 vty->xpath_index = orig_xpath_index;
1063 return ret;
1064 }
1065
1066 saved_ret = ret =
1067 cmd_execute_command_real(vline, FILTER_RELAXED, vty, cmd);
1068
1069 if (vtysh)
1070 return saved_ret;
1071
1072 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1073 && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
1074 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED) {
1075 /* This assumes all nodes above CONFIG_NODE are childs of
1076 * CONFIG_NODE */
1077 while (vty->node > CONFIG_NODE) {
1078 try_node = node_parent(try_node);
1079 vty->node = try_node;
1080 if (vty->xpath_index > 0)
1081 vty->xpath_index--;
1082 ret = cmd_execute_command_real(vline, FILTER_RELAXED,
1083 vty, cmd);
1084 if (ret == CMD_SUCCESS || ret == CMD_WARNING
1085 || ret == CMD_ERR_AMBIGUOUS || ret == CMD_ERR_INCOMPLETE
1086 || ret == CMD_NOT_MY_INSTANCE
1087 || ret == CMD_WARNING_CONFIG_FAILED)
1088 return ret;
1089 }
1090 /* no command succeeded, reset the vty to the original node */
1091 vty->node = onode;
1092 vty->xpath_index = orig_xpath_index;
1093 }
1094
1095 /* return command status for original node */
1096 return saved_ret;
1097 }
1098
1099 /**
1100 * Execute a given command, matching it strictly against the current node.
1101 * This mode is used when reading config files.
1102 *
1103 * @param vline Command line input, vector of char* where each element is
1104 * one input token.
1105 * @param vty The vty context in which the command should be executed.
1106 * @param cmd Pointer where the struct cmd_element* of the matched command
1107 * will be stored, if any. May be set to NULL if this info is
1108 * not needed.
1109 * @return The status of the command that has been executed or an error code
1110 * as to why no command could be executed.
1111 */
1112 int cmd_execute_command_strict(vector vline, struct vty *vty,
1113 const struct cmd_element **cmd)
1114 {
1115 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
1116 }
1117
1118 /*
1119 * Hook for preprocessing command string before executing.
1120 *
1121 * All subscribers are called with the raw command string that is to be
1122 * executed. If any changes are to be made, a new string should be allocated
1123 * with MTYPE_TMP and *cmd_out updated to point to this new string. The caller
1124 * is then responsible for freeing this string.
1125 *
1126 * All processing functions must be mutually exclusive in their action, i.e. if
1127 * one subscriber decides to modify the command, all others must not modify it
1128 * when called. Feeding the output of one processing command into a subsequent
1129 * one is not supported.
1130 *
1131 * This hook is intentionally internal to the command processing system.
1132 *
1133 * cmd_in
1134 * The raw command string.
1135 *
1136 * cmd_out
1137 * The result of any processing.
1138 */
1139 DECLARE_HOOK(cmd_execute,
1140 (struct vty *vty, const char *cmd_in, char **cmd_out),
1141 (vty, cmd_in, cmd_out));
1142 DEFINE_HOOK(cmd_execute, (struct vty *vty, const char *cmd_in, char **cmd_out),
1143 (vty, cmd_in, cmd_out));
1144
1145 /* Hook executed after a CLI command. */
1146 DECLARE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1147 (vty, cmd_exec));
1148 DEFINE_KOOH(cmd_execute_done, (struct vty *vty, const char *cmd_exec),
1149 (vty, cmd_exec));
1150
1151 /*
1152 * cmd_execute hook subscriber to handle `|` actions.
1153 */
1154 static int handle_pipe_action(struct vty *vty, const char *cmd_in,
1155 char **cmd_out)
1156 {
1157 /* look for `|` */
1158 char *orig, *working, *token, *u;
1159 char *pipe = strstr(cmd_in, "| ");
1160
1161 if (!pipe)
1162 return 0;
1163
1164 /* duplicate string for processing purposes, not including pipe */
1165 orig = working = XSTRDUP(MTYPE_TMP, pipe + 2);
1166
1167 /* retrieve action */
1168 token = strsep(&working, " ");
1169 assert(token);
1170
1171 /* match result to known actions */
1172 if (strmatch(token, "include")) {
1173 /* the remaining text should be a regexp */
1174 char *regexp = working;
1175
1176 if (!regexp) {
1177 vty_out(vty, "%% Need a regexp to filter with\n");
1178 goto fail;
1179 }
1180
1181 bool succ = vty_set_include(vty, regexp);
1182
1183 if (!succ) {
1184 vty_out(vty, "%% Bad regexp '%s'\n", regexp);
1185 goto fail;
1186 }
1187 *cmd_out = XSTRDUP(MTYPE_TMP, cmd_in);
1188 u = *cmd_out;
1189 strsep(&u, "|");
1190 } else {
1191 vty_out(vty, "%% Unknown action '%s'\n", token);
1192 goto fail;
1193 }
1194
1195 fail:
1196 XFREE(MTYPE_TMP, orig);
1197 return 0;
1198 }
1199
1200 static int handle_pipe_action_done(struct vty *vty, const char *cmd_exec)
1201 {
1202 if (vty->filter)
1203 vty_set_include(vty, NULL);
1204
1205 return 0;
1206 }
1207
1208 int cmd_execute(struct vty *vty, const char *cmd,
1209 const struct cmd_element **matched, int vtysh)
1210 {
1211 int ret;
1212 char *cmd_out = NULL;
1213 const char *cmd_exec;
1214 vector vline;
1215
1216 hook_call(cmd_execute, vty, cmd, &cmd_out);
1217 cmd_exec = cmd_out ? (const char *)cmd_out : cmd;
1218
1219 vline = cmd_make_strvec(cmd_exec);
1220
1221 if (vline) {
1222 ret = cmd_execute_command(vline, vty, matched, vtysh);
1223 cmd_free_strvec(vline);
1224 } else {
1225 ret = CMD_SUCCESS;
1226 }
1227
1228 hook_call(cmd_execute_done, vty, cmd_exec);
1229
1230 XFREE(MTYPE_TMP, cmd_out);
1231
1232 return ret;
1233 }
1234
1235
1236 /**
1237 * Parse one line of config, walking up the parse tree attempting to find a
1238 * match
1239 *
1240 * @param vty The vty context in which the command should be executed.
1241 * @param cmd Pointer where the struct cmd_element* of the match command
1242 * will be stored, if any. May be set to NULL if this info is
1243 * not needed.
1244 * @param use_daemon Boolean to control whether or not we match on
1245 * CMD_SUCCESS_DAEMON
1246 * or not.
1247 * @return The status of the command that has been executed or an error code
1248 * as to why no command could be executed.
1249 */
1250 int command_config_read_one_line(struct vty *vty,
1251 const struct cmd_element **cmd,
1252 uint32_t line_num, int use_daemon)
1253 {
1254 vector vline;
1255 int ret;
1256
1257 vline = cmd_make_strvec(vty->buf);
1258
1259 /* In case of comment line */
1260 if (vline == NULL)
1261 return CMD_SUCCESS;
1262
1263 /* Execute configuration command : this is strict match */
1264 ret = cmd_execute_command_strict(vline, vty, cmd);
1265
1266 // Climb the tree and try the command again at each node
1267 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1268 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1269 && ret != CMD_SUCCESS && ret != CMD_WARNING
1270 && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
1271 && ret != CMD_NOT_MY_INSTANCE && ret != CMD_WARNING_CONFIG_FAILED
1272 && vty->node != CONFIG_NODE) {
1273 int saved_node = vty->node;
1274 int saved_xpath_index = vty->xpath_index;
1275
1276 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1277 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1278 && ret != CMD_SUCCESS && ret != CMD_WARNING
1279 && ret != CMD_ERR_AMBIGUOUS && ret != CMD_ERR_INCOMPLETE
1280 && vty->node > CONFIG_NODE) {
1281 vty->node = node_parent(vty->node);
1282 if (vty->xpath_index > 0)
1283 vty->xpath_index--;
1284 ret = cmd_execute_command_strict(vline, vty, cmd);
1285 }
1286
1287 // If climbing the tree did not work then ignore the command and
1288 // stay at the same node
1289 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON)
1290 && !(!use_daemon && ret == CMD_ERR_NOTHING_TODO)
1291 && ret != CMD_SUCCESS && ret != CMD_WARNING) {
1292 vty->node = saved_node;
1293 vty->xpath_index = saved_xpath_index;
1294 }
1295 }
1296
1297 if (ret != CMD_SUCCESS &&
1298 ret != CMD_WARNING &&
1299 ret != CMD_SUCCESS_DAEMON) {
1300 struct vty_error *ve = XCALLOC(MTYPE_TMP, sizeof(*ve));
1301
1302 memcpy(ve->error_buf, vty->buf, VTY_BUFSIZ);
1303 ve->line_num = line_num;
1304 if (!vty->error)
1305 vty->error = list_new();
1306
1307 listnode_add(vty->error, ve);
1308 }
1309
1310 cmd_free_strvec(vline);
1311
1312 return ret;
1313 }
1314
1315 /* Configuration make from file. */
1316 int config_from_file(struct vty *vty, FILE *fp, unsigned int *line_num)
1317 {
1318 int ret, error_ret = 0;
1319 *line_num = 0;
1320
1321 while (fgets(vty->buf, VTY_BUFSIZ, fp)) {
1322 ++(*line_num);
1323
1324 ret = command_config_read_one_line(vty, NULL, *line_num, 0);
1325
1326 if (ret != CMD_SUCCESS && ret != CMD_WARNING
1327 && ret != CMD_ERR_NOTHING_TODO)
1328 error_ret = ret;
1329 }
1330
1331 if (error_ret) {
1332 return error_ret;
1333 }
1334
1335 return CMD_SUCCESS;
1336 }
1337
1338 /* Configuration from terminal */
1339 DEFUN (config_terminal,
1340 config_terminal_cmd,
1341 "configure [terminal]",
1342 "Configuration from vty interface\n"
1343 "Configuration terminal\n")
1344 {
1345 return vty_config_enter(vty, false, false);
1346 }
1347
1348 /* Enable command */
1349 DEFUN (enable,
1350 config_enable_cmd,
1351 "enable",
1352 "Turn on privileged mode command\n")
1353 {
1354 /* If enable password is NULL, change to ENABLE_NODE */
1355 if ((host.enable == NULL && host.enable_encrypt == NULL)
1356 || vty->type == VTY_SHELL_SERV)
1357 vty->node = ENABLE_NODE;
1358 else
1359 vty->node = AUTH_ENABLE_NODE;
1360
1361 return CMD_SUCCESS;
1362 }
1363
1364 /* Disable command */
1365 DEFUN (disable,
1366 config_disable_cmd,
1367 "disable",
1368 "Turn off privileged mode command\n")
1369 {
1370 if (vty->node == ENABLE_NODE)
1371 vty->node = VIEW_NODE;
1372 return CMD_SUCCESS;
1373 }
1374
1375 /* Down vty node level. */
1376 DEFUN (config_exit,
1377 config_exit_cmd,
1378 "exit",
1379 "Exit current mode and down to previous mode\n")
1380 {
1381 cmd_exit(vty);
1382 return CMD_SUCCESS;
1383 }
1384
1385 void cmd_exit(struct vty *vty)
1386 {
1387 struct cmd_node *cnode = vector_lookup(cmdvec, vty->node);
1388
1389 switch (vty->node) {
1390 case VIEW_NODE:
1391 case ENABLE_NODE:
1392 if (vty_shell(vty))
1393 exit(0);
1394 else
1395 vty->status = VTY_CLOSE;
1396 break;
1397 case CONFIG_NODE:
1398 vty->node = ENABLE_NODE;
1399 vty_config_exit(vty);
1400 break;
1401 default:
1402 if (cnode->parent_node)
1403 vty->node = cnode->parent_node;
1404 break;
1405 }
1406
1407 if (vty->xpath_index > 0)
1408 vty->xpath_index--;
1409 }
1410
1411 /* ALIAS_FIXME */
1412 DEFUN (config_quit,
1413 config_quit_cmd,
1414 "quit",
1415 "Exit current mode and down to previous mode\n")
1416 {
1417 return config_exit(self, vty, argc, argv);
1418 }
1419
1420
1421 /* End of configuration. */
1422 DEFUN (config_end,
1423 config_end_cmd,
1424 "end",
1425 "End current mode and change to enable mode.\n")
1426 {
1427 if (vty->config) {
1428 vty_config_exit(vty);
1429 vty->node = ENABLE_NODE;
1430 }
1431 return CMD_SUCCESS;
1432 }
1433
1434 /* Show version. */
1435 DEFUN (show_version,
1436 show_version_cmd,
1437 "show version",
1438 SHOW_STR
1439 "Displays zebra version\n")
1440 {
1441 vty_out(vty, "%s %s (%s).\n", FRR_FULL_NAME, FRR_VERSION,
1442 cmd_hostname_get() ? cmd_hostname_get() : "");
1443 vty_out(vty, "%s%s\n", FRR_COPYRIGHT, GIT_INFO);
1444 vty_out(vty, "configured with:\n %s\n", FRR_CONFIG_ARGS);
1445
1446 return CMD_SUCCESS;
1447 }
1448
1449 /* Help display function for all node. */
1450 DEFUN (config_help,
1451 config_help_cmd,
1452 "help",
1453 "Description of the interactive help system\n")
1454 {
1455 vty_out(vty,
1456 "Quagga VTY provides advanced help feature. When you need help,\n\
1457 anytime at the command line please press '?'.\n\
1458 \n\
1459 If nothing matches, the help list will be empty and you must backup\n\
1460 until entering a '?' shows the available options.\n\
1461 Two styles of help are provided:\n\
1462 1. Full help is available when you are ready to enter a\n\
1463 command argument (e.g. 'show ?') and describes each possible\n\
1464 argument.\n\
1465 2. Partial help is provided when an abbreviated argument is entered\n\
1466 and you want to know what arguments match the input\n\
1467 (e.g. 'show me?'.)\n\n");
1468 return CMD_SUCCESS;
1469 }
1470
1471 static void permute(struct graph_node *start, struct vty *vty)
1472 {
1473 static struct list *position = NULL;
1474 if (!position)
1475 position = list_new();
1476
1477 struct cmd_token *stok = start->data;
1478 struct graph_node *gnn;
1479 struct listnode *ln;
1480
1481 // recursive dfs
1482 listnode_add(position, start);
1483 for (unsigned int i = 0; i < vector_active(start->to); i++) {
1484 struct graph_node *gn = vector_slot(start->to, i);
1485 struct cmd_token *tok = gn->data;
1486 if (tok->attr == CMD_ATTR_HIDDEN
1487 || tok->attr == CMD_ATTR_DEPRECATED)
1488 continue;
1489 else if (tok->type == END_TKN || gn == start) {
1490 vty_out(vty, " ");
1491 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn)) {
1492 struct cmd_token *tt = gnn->data;
1493 if (tt->type < SPECIAL_TKN)
1494 vty_out(vty, " %s", tt->text);
1495 }
1496 if (gn == start)
1497 vty_out(vty, "...");
1498 vty_out(vty, "\n");
1499 } else {
1500 bool skip = false;
1501 if (stok->type == FORK_TKN && tok->type != FORK_TKN)
1502 for (ALL_LIST_ELEMENTS_RO(position, ln, gnn))
1503 if (gnn == gn) {
1504 skip = true;
1505 break;
1506 }
1507 if (!skip)
1508 permute(gn, vty);
1509 }
1510 }
1511 list_delete_node(position, listtail(position));
1512 }
1513
1514 int cmd_list_cmds(struct vty *vty, int do_permute)
1515 {
1516 struct cmd_node *node = vector_slot(cmdvec, vty->node);
1517
1518 if (do_permute)
1519 permute(vector_slot(node->cmdgraph->nodes, 0), vty);
1520 else {
1521 /* loop over all commands at this node */
1522 const struct cmd_element *element = NULL;
1523 for (unsigned int i = 0; i < vector_active(node->cmd_vector);
1524 i++)
1525 if ((element = vector_slot(node->cmd_vector, i))
1526 && element->attr != CMD_ATTR_DEPRECATED
1527 && element->attr != CMD_ATTR_HIDDEN)
1528 vty_out(vty, " %s\n", element->string);
1529 }
1530 return CMD_SUCCESS;
1531 }
1532
1533 /* Help display function for all node. */
1534 DEFUN (config_list,
1535 config_list_cmd,
1536 "list [permutations]",
1537 "Print command list\n"
1538 "Print all possible command permutations\n")
1539 {
1540 return cmd_list_cmds(vty, argc == 2);
1541 }
1542
1543 DEFUN (show_commandtree,
1544 show_commandtree_cmd,
1545 "show commandtree [permutations]",
1546 SHOW_STR
1547 "Show command tree\n"
1548 "Permutations that we are interested in\n")
1549 {
1550 return cmd_list_cmds(vty, argc == 3);
1551 }
1552
1553 DEFUN_HIDDEN(show_cli_graph,
1554 show_cli_graph_cmd,
1555 "show cli graph",
1556 SHOW_STR
1557 "CLI reflection\n"
1558 "Dump current command space as DOT graph\n")
1559 {
1560 struct cmd_node *cn = vector_slot(cmdvec, vty->node);
1561 char *dot = cmd_graph_dump_dot(cn->cmdgraph);
1562
1563 vty_out(vty, "%s\n", dot);
1564 XFREE(MTYPE_TMP, dot);
1565 return CMD_SUCCESS;
1566 }
1567
1568 static int vty_write_config(struct vty *vty)
1569 {
1570 size_t i;
1571 struct cmd_node *node;
1572
1573 if (host.noconfig)
1574 return CMD_SUCCESS;
1575
1576 nb_cli_show_config_prepare(running_config, false);
1577
1578 if (vty->type == VTY_TERM) {
1579 vty_out(vty, "\nCurrent configuration:\n");
1580 vty_out(vty, "!\n");
1581 }
1582
1583 if (strcmp(frr_defaults_version(), FRR_VER_SHORT))
1584 vty_out(vty, "! loaded from %s\n", frr_defaults_version());
1585 vty_out(vty, "frr version %s\n", FRR_VER_SHORT);
1586 vty_out(vty, "frr defaults %s\n", frr_defaults_profile());
1587 vty_out(vty, "!\n");
1588
1589 for (i = 0; i < vector_active(cmdvec); i++)
1590 if ((node = vector_slot(cmdvec, i)) && node->config_write) {
1591 if ((*node->config_write)(vty))
1592 vty_out(vty, "!\n");
1593 }
1594
1595 if (vty->type == VTY_TERM) {
1596 vty_out(vty, "end\n");
1597 }
1598
1599 return CMD_SUCCESS;
1600 }
1601
1602 static int file_write_config(struct vty *vty)
1603 {
1604 int fd, dirfd;
1605 char *config_file, *slash;
1606 char *config_file_tmp = NULL;
1607 char *config_file_sav = NULL;
1608 int ret = CMD_WARNING;
1609 struct vty *file_vty;
1610 struct stat conf_stat;
1611
1612 if (host.noconfig)
1613 return CMD_SUCCESS;
1614
1615 /* Check and see if we are operating under vtysh configuration */
1616 if (host.config == NULL) {
1617 vty_out(vty,
1618 "Can't save to configuration file, using vtysh.\n");
1619 return CMD_WARNING;
1620 }
1621
1622 /* Get filename. */
1623 config_file = host.config;
1624
1625 #ifndef O_DIRECTORY
1626 #define O_DIRECTORY 0
1627 #endif
1628 slash = strrchr(config_file, '/');
1629 if (slash) {
1630 char *config_dir = XSTRDUP(MTYPE_TMP, config_file);
1631 config_dir[slash - config_file] = '\0';
1632 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1633 XFREE(MTYPE_TMP, config_dir);
1634 } else
1635 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1636 /* if dirfd is invalid, directory sync fails, but we're still OK */
1637
1638 size_t config_file_sav_sz = strlen(config_file) + strlen(CONF_BACKUP_EXT) + 1;
1639 config_file_sav = XMALLOC(MTYPE_TMP, config_file_sav_sz);
1640 strlcpy(config_file_sav, config_file, config_file_sav_sz);
1641 strlcat(config_file_sav, CONF_BACKUP_EXT, config_file_sav_sz);
1642
1643
1644 config_file_tmp = XMALLOC(MTYPE_TMP, strlen(config_file) + 8);
1645 sprintf(config_file_tmp, "%s.XXXXXX", config_file);
1646
1647 /* Open file to configuration write. */
1648 fd = mkstemp(config_file_tmp);
1649 if (fd < 0) {
1650 vty_out(vty, "Can't open configuration file %s.\n",
1651 config_file_tmp);
1652 goto finished;
1653 }
1654 if (fchmod(fd, CONFIGFILE_MASK) != 0) {
1655 vty_out(vty, "Can't chmod configuration file %s: %s (%d).\n",
1656 config_file_tmp, safe_strerror(errno), errno);
1657 goto finished;
1658 }
1659
1660 /* Make vty for configuration file. */
1661 file_vty = vty_new();
1662 file_vty->wfd = fd;
1663 file_vty->type = VTY_FILE;
1664
1665 /* Config file header print. */
1666 vty_out(file_vty, "!\n! Zebra configuration saved from vty\n! ");
1667 vty_time_print(file_vty, 1);
1668 vty_out(file_vty, "!\n");
1669 vty_write_config(file_vty);
1670 vty_close(file_vty);
1671
1672 if (stat(config_file, &conf_stat) >= 0) {
1673 if (unlink(config_file_sav) != 0)
1674 if (errno != ENOENT) {
1675 vty_out(vty,
1676 "Can't unlink backup configuration file %s.\n",
1677 config_file_sav);
1678 goto finished;
1679 }
1680 if (link(config_file, config_file_sav) != 0) {
1681 vty_out(vty,
1682 "Can't backup old configuration file %s.\n",
1683 config_file_sav);
1684 goto finished;
1685 }
1686 if (dirfd >= 0)
1687 fsync(dirfd);
1688 }
1689 if (rename(config_file_tmp, config_file) != 0) {
1690 vty_out(vty, "Can't save configuration file %s.\n",
1691 config_file);
1692 goto finished;
1693 }
1694 if (dirfd >= 0)
1695 fsync(dirfd);
1696
1697 vty_out(vty, "Configuration saved to %s\n", config_file);
1698 ret = CMD_SUCCESS;
1699
1700 finished:
1701 if (ret != CMD_SUCCESS)
1702 unlink(config_file_tmp);
1703 if (dirfd >= 0)
1704 close(dirfd);
1705 XFREE(MTYPE_TMP, config_file_tmp);
1706 XFREE(MTYPE_TMP, config_file_sav);
1707 return ret;
1708 }
1709
1710 /* Write current configuration into file. */
1711
1712 DEFUN (config_write,
1713 config_write_cmd,
1714 "write [<file|memory|terminal>]",
1715 "Write running configuration to memory, network, or terminal\n"
1716 "Write to configuration file\n"
1717 "Write configuration currently in memory\n"
1718 "Write configuration to terminal\n")
1719 {
1720 const int idx_type = 1;
1721
1722 // if command was 'write terminal' or 'write memory'
1723 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal"))) {
1724 return vty_write_config(vty);
1725 }
1726
1727 return file_write_config(vty);
1728 }
1729
1730 /* ALIAS_FIXME for 'write <terminal|memory>' */
1731 DEFUN (show_running_config,
1732 show_running_config_cmd,
1733 "show running-config",
1734 SHOW_STR
1735 "running configuration (same as write terminal)\n")
1736 {
1737 return vty_write_config(vty);
1738 }
1739
1740 /* ALIAS_FIXME for 'write file' */
1741 DEFUN (copy_runningconf_startupconf,
1742 copy_runningconf_startupconf_cmd,
1743 "copy running-config startup-config",
1744 "Copy configuration\n"
1745 "Copy running config to... \n"
1746 "Copy running config to startup config (same as write file/memory)\n")
1747 {
1748 return file_write_config(vty);
1749 }
1750 /** -- **/
1751
1752 /* Write startup configuration into the terminal. */
1753 DEFUN (show_startup_config,
1754 show_startup_config_cmd,
1755 "show startup-config",
1756 SHOW_STR
1757 "Contents of startup configuration\n")
1758 {
1759 char buf[BUFSIZ];
1760 FILE *confp;
1761
1762 if (host.noconfig)
1763 return CMD_SUCCESS;
1764 if (host.config == NULL)
1765 return CMD_WARNING;
1766
1767 confp = fopen(host.config, "r");
1768 if (confp == NULL) {
1769 vty_out(vty, "Can't open configuration file [%s] due to '%s'\n",
1770 host.config, safe_strerror(errno));
1771 return CMD_WARNING;
1772 }
1773
1774 while (fgets(buf, BUFSIZ, confp)) {
1775 char *cp = buf;
1776
1777 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1778 cp++;
1779 *cp = '\0';
1780
1781 vty_out(vty, "%s\n", buf);
1782 }
1783
1784 fclose(confp);
1785
1786 return CMD_SUCCESS;
1787 }
1788
1789 int cmd_domainname_set(const char *domainname)
1790 {
1791 XFREE(MTYPE_HOST, host.domainname);
1792 host.domainname = domainname ? XSTRDUP(MTYPE_HOST, domainname) : NULL;
1793 return CMD_SUCCESS;
1794 }
1795
1796 /* Hostname configuration */
1797 DEFUN(config_domainname,
1798 domainname_cmd,
1799 "domainname WORD",
1800 "Set system's domain name\n"
1801 "This system's domain name\n")
1802 {
1803 struct cmd_token *word = argv[1];
1804
1805 if (!isalpha((unsigned char)word->arg[0])) {
1806 vty_out(vty, "Please specify string starting with alphabet\n");
1807 return CMD_WARNING_CONFIG_FAILED;
1808 }
1809
1810 return cmd_domainname_set(word->arg);
1811 }
1812
1813 DEFUN(config_no_domainname,
1814 no_domainname_cmd,
1815 "no domainname [DOMAINNAME]",
1816 NO_STR
1817 "Reset system's domain name\n"
1818 "domain name of this router\n")
1819 {
1820 return cmd_domainname_set(NULL);
1821 }
1822
1823 int cmd_hostname_set(const char *hostname)
1824 {
1825 XFREE(MTYPE_HOST, host.name);
1826 host.name = hostname ? XSTRDUP(MTYPE_HOST, hostname) : NULL;
1827 return CMD_SUCCESS;
1828 }
1829
1830 /* Hostname configuration */
1831 DEFUN (config_hostname,
1832 hostname_cmd,
1833 "hostname WORD",
1834 "Set system's network name\n"
1835 "This system's network name\n")
1836 {
1837 struct cmd_token *word = argv[1];
1838
1839 if (!isalnum((unsigned char)word->arg[0])) {
1840 vty_out(vty,
1841 "Please specify string starting with alphabet or number\n");
1842 return CMD_WARNING_CONFIG_FAILED;
1843 }
1844
1845 /* With reference to RFC 1123 Section 2.1 */
1846 if (strlen(word->arg) > HOSTNAME_LEN) {
1847 vty_out(vty, "Hostname length should be less than %d chars\n",
1848 HOSTNAME_LEN);
1849 return CMD_WARNING_CONFIG_FAILED;
1850 }
1851
1852 return cmd_hostname_set(word->arg);
1853 }
1854
1855 DEFUN (config_no_hostname,
1856 no_hostname_cmd,
1857 "no hostname [HOSTNAME]",
1858 NO_STR
1859 "Reset system's network name\n"
1860 "Host name of this router\n")
1861 {
1862 return cmd_hostname_set(NULL);
1863 }
1864
1865 /* VTY interface password set. */
1866 DEFUN (config_password,
1867 password_cmd,
1868 "password [(8-8)] WORD",
1869 "Modify the terminal connection password\n"
1870 "Specifies a HIDDEN password will follow\n"
1871 "The password string\n")
1872 {
1873 int idx_8 = 1;
1874 int idx_word = 2;
1875 if (argc == 3) // '8' was specified
1876 {
1877 if (host.password)
1878 XFREE(MTYPE_HOST, host.password);
1879 host.password = NULL;
1880 if (host.password_encrypt)
1881 XFREE(MTYPE_HOST, host.password_encrypt);
1882 host.password_encrypt =
1883 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1884 return CMD_SUCCESS;
1885 }
1886
1887 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1888 vty_out(vty,
1889 "Please specify string starting with alphanumeric\n");
1890 return CMD_WARNING_CONFIG_FAILED;
1891 }
1892
1893 if (host.password)
1894 XFREE(MTYPE_HOST, host.password);
1895 host.password = NULL;
1896
1897 if (host.encrypt) {
1898 if (host.password_encrypt)
1899 XFREE(MTYPE_HOST, host.password_encrypt);
1900 host.password_encrypt =
1901 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1902 } else
1903 host.password = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1904
1905 return CMD_SUCCESS;
1906 }
1907
1908 /* VTY interface password delete. */
1909 DEFUN (no_config_password,
1910 no_password_cmd,
1911 "no password",
1912 NO_STR
1913 "Modify the terminal connection password\n")
1914 {
1915 bool warned = false;
1916
1917 if (host.password) {
1918 if (!vty_shell_serv(vty)) {
1919 vty_out(vty, NO_PASSWD_CMD_WARNING);
1920 warned = true;
1921 }
1922 XFREE(MTYPE_HOST, host.password);
1923 }
1924 host.password = NULL;
1925
1926 if (host.password_encrypt) {
1927 if (!warned && !vty_shell_serv(vty))
1928 vty_out(vty, NO_PASSWD_CMD_WARNING);
1929 XFREE(MTYPE_HOST, host.password_encrypt);
1930 }
1931 host.password_encrypt = NULL;
1932
1933 return CMD_SUCCESS;
1934 }
1935
1936 /* VTY enable password set. */
1937 DEFUN (config_enable_password,
1938 enable_password_cmd,
1939 "enable password [(8-8)] WORD",
1940 "Modify enable password parameters\n"
1941 "Assign the privileged level password\n"
1942 "Specifies a HIDDEN password will follow\n"
1943 "The HIDDEN 'enable' password string\n")
1944 {
1945 int idx_8 = 2;
1946 int idx_word = 3;
1947
1948 /* Crypt type is specified. */
1949 if (argc == 4) {
1950 if (argv[idx_8]->arg[0] == '8') {
1951 if (host.enable)
1952 XFREE(MTYPE_HOST, host.enable);
1953 host.enable = NULL;
1954
1955 if (host.enable_encrypt)
1956 XFREE(MTYPE_HOST, host.enable_encrypt);
1957 host.enable_encrypt =
1958 XSTRDUP(MTYPE_HOST, argv[idx_word]->arg);
1959
1960 return CMD_SUCCESS;
1961 } else {
1962 vty_out(vty, "Unknown encryption type.\n");
1963 return CMD_WARNING_CONFIG_FAILED;
1964 }
1965 }
1966
1967 if (!isalnum((unsigned char)argv[idx_8]->arg[0])) {
1968 vty_out(vty,
1969 "Please specify string starting with alphanumeric\n");
1970 return CMD_WARNING_CONFIG_FAILED;
1971 }
1972
1973 if (host.enable)
1974 XFREE(MTYPE_HOST, host.enable);
1975 host.enable = NULL;
1976
1977 /* Plain password input. */
1978 if (host.encrypt) {
1979 if (host.enable_encrypt)
1980 XFREE(MTYPE_HOST, host.enable_encrypt);
1981 host.enable_encrypt =
1982 XSTRDUP(MTYPE_HOST, zencrypt(argv[idx_8]->arg));
1983 } else
1984 host.enable = XSTRDUP(MTYPE_HOST, argv[idx_8]->arg);
1985
1986 return CMD_SUCCESS;
1987 }
1988
1989 /* VTY enable password delete. */
1990 DEFUN (no_config_enable_password,
1991 no_enable_password_cmd,
1992 "no enable password",
1993 NO_STR
1994 "Modify enable password parameters\n"
1995 "Assign the privileged level password\n")
1996 {
1997 bool warned = false;
1998
1999 if (host.enable) {
2000 if (!vty_shell_serv(vty)) {
2001 vty_out(vty, NO_PASSWD_CMD_WARNING);
2002 warned = true;
2003 }
2004 XFREE(MTYPE_HOST, host.enable);
2005 }
2006 host.enable = NULL;
2007
2008 if (host.enable_encrypt) {
2009 if (!warned && !vty_shell_serv(vty))
2010 vty_out(vty, NO_PASSWD_CMD_WARNING);
2011 XFREE(MTYPE_HOST, host.enable_encrypt);
2012 }
2013 host.enable_encrypt = NULL;
2014
2015 return CMD_SUCCESS;
2016 }
2017
2018 DEFUN (service_password_encrypt,
2019 service_password_encrypt_cmd,
2020 "service password-encryption",
2021 "Set up miscellaneous service\n"
2022 "Enable encrypted passwords\n")
2023 {
2024 if (host.encrypt)
2025 return CMD_SUCCESS;
2026
2027 host.encrypt = 1;
2028
2029 if (host.password) {
2030 if (host.password_encrypt)
2031 XFREE(MTYPE_HOST, host.password_encrypt);
2032 host.password_encrypt =
2033 XSTRDUP(MTYPE_HOST, zencrypt(host.password));
2034 }
2035 if (host.enable) {
2036 if (host.enable_encrypt)
2037 XFREE(MTYPE_HOST, host.enable_encrypt);
2038 host.enable_encrypt =
2039 XSTRDUP(MTYPE_HOST, zencrypt(host.enable));
2040 }
2041
2042 return CMD_SUCCESS;
2043 }
2044
2045 DEFUN (no_service_password_encrypt,
2046 no_service_password_encrypt_cmd,
2047 "no service password-encryption",
2048 NO_STR
2049 "Set up miscellaneous service\n"
2050 "Enable encrypted passwords\n")
2051 {
2052 if (!host.encrypt)
2053 return CMD_SUCCESS;
2054
2055 host.encrypt = 0;
2056
2057 if (host.password_encrypt)
2058 XFREE(MTYPE_HOST, host.password_encrypt);
2059 host.password_encrypt = NULL;
2060
2061 if (host.enable_encrypt)
2062 XFREE(MTYPE_HOST, host.enable_encrypt);
2063 host.enable_encrypt = NULL;
2064
2065 return CMD_SUCCESS;
2066 }
2067
2068 DEFUN (config_terminal_length,
2069 config_terminal_length_cmd,
2070 "terminal length (0-512)",
2071 "Set terminal line parameters\n"
2072 "Set number of lines on a screen\n"
2073 "Number of lines on screen (0 for no pausing)\n")
2074 {
2075 int idx_number = 2;
2076
2077 vty->lines = atoi(argv[idx_number]->arg);
2078
2079 return CMD_SUCCESS;
2080 }
2081
2082 DEFUN (config_terminal_no_length,
2083 config_terminal_no_length_cmd,
2084 "terminal no length",
2085 "Set terminal line parameters\n"
2086 NO_STR
2087 "Set number of lines on a screen\n")
2088 {
2089 vty->lines = -1;
2090 return CMD_SUCCESS;
2091 }
2092
2093 DEFUN (service_terminal_length,
2094 service_terminal_length_cmd,
2095 "service terminal-length (0-512)",
2096 "Set up miscellaneous service\n"
2097 "System wide terminal length configuration\n"
2098 "Number of lines of VTY (0 means no line control)\n")
2099 {
2100 int idx_number = 2;
2101
2102 host.lines = atoi(argv[idx_number]->arg);
2103
2104 return CMD_SUCCESS;
2105 }
2106
2107 DEFUN (no_service_terminal_length,
2108 no_service_terminal_length_cmd,
2109 "no service terminal-length [(0-512)]",
2110 NO_STR
2111 "Set up miscellaneous service\n"
2112 "System wide terminal length configuration\n"
2113 "Number of lines of VTY (0 means no line control)\n")
2114 {
2115 host.lines = -1;
2116 return CMD_SUCCESS;
2117 }
2118
2119 DEFUN_HIDDEN (do_echo,
2120 echo_cmd,
2121 "echo MESSAGE...",
2122 "Echo a message back to the vty\n"
2123 "The message to echo\n")
2124 {
2125 char *message;
2126
2127 vty_out(vty, "%s\n",
2128 ((message = argv_concat(argv, argc, 1)) ? message : ""));
2129 if (message)
2130 XFREE(MTYPE_TMP, message);
2131 return CMD_SUCCESS;
2132 }
2133
2134 DEFUN (config_logmsg,
2135 config_logmsg_cmd,
2136 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2137 "Send a message to enabled logging destinations\n"
2138 LOG_LEVEL_DESC
2139 "The message to send\n")
2140 {
2141 int idx_log_level = 1;
2142 int idx_message = 2;
2143 int level;
2144 char *message;
2145
2146 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2147 return CMD_ERR_NO_MATCH;
2148
2149 zlog(level, "%s",
2150 ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2151 if (message)
2152 XFREE(MTYPE_TMP, message);
2153
2154 return CMD_SUCCESS;
2155 }
2156
2157 DEFUN (show_logging,
2158 show_logging_cmd,
2159 "show logging",
2160 SHOW_STR
2161 "Show current logging configuration\n")
2162 {
2163 struct zlog *zl = zlog_default;
2164
2165 vty_out(vty, "Syslog logging: ");
2166 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2167 vty_out(vty, "disabled");
2168 else
2169 vty_out(vty, "level %s, facility %s, ident %s",
2170 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2171 facility_name(zl->facility), zl->ident);
2172 vty_out(vty, "\n");
2173
2174 vty_out(vty, "Stdout logging: ");
2175 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
2176 vty_out(vty, "disabled");
2177 else
2178 vty_out(vty, "level %s",
2179 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
2180 vty_out(vty, "\n");
2181
2182 vty_out(vty, "Monitor logging: ");
2183 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
2184 vty_out(vty, "disabled");
2185 else
2186 vty_out(vty, "level %s",
2187 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
2188 vty_out(vty, "\n");
2189
2190 vty_out(vty, "File logging: ");
2191 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) || !zl->fp)
2192 vty_out(vty, "disabled");
2193 else
2194 vty_out(vty, "level %s, filename %s",
2195 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
2196 zl->filename);
2197 vty_out(vty, "\n");
2198
2199 vty_out(vty, "Protocol name: %s\n", zl->protoname);
2200 vty_out(vty, "Record priority: %s\n",
2201 (zl->record_priority ? "enabled" : "disabled"));
2202 vty_out(vty, "Timestamp precision: %d\n", zl->timestamp_precision);
2203
2204 return CMD_SUCCESS;
2205 }
2206
2207 DEFUN (config_log_stdout,
2208 config_log_stdout_cmd,
2209 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2210 "Logging control\n"
2211 "Set stdout logging level\n"
2212 LOG_LEVEL_DESC)
2213 {
2214 int idx_log_level = 2;
2215
2216 if (argc == idx_log_level) {
2217 zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2218 return CMD_SUCCESS;
2219 }
2220 int level;
2221
2222 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2223 return CMD_ERR_NO_MATCH;
2224 zlog_set_level(ZLOG_DEST_STDOUT, level);
2225 return CMD_SUCCESS;
2226 }
2227
2228 DEFUN (no_config_log_stdout,
2229 no_config_log_stdout_cmd,
2230 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2231 NO_STR
2232 "Logging control\n"
2233 "Cancel logging to stdout\n"
2234 LOG_LEVEL_DESC)
2235 {
2236 zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
2237 return CMD_SUCCESS;
2238 }
2239
2240 DEFUN (config_log_monitor,
2241 config_log_monitor_cmd,
2242 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2243 "Logging control\n"
2244 "Set terminal line (monitor) logging level\n"
2245 LOG_LEVEL_DESC)
2246 {
2247 int idx_log_level = 2;
2248
2249 if (argc == idx_log_level) {
2250 zlog_set_level(ZLOG_DEST_MONITOR, zlog_default->default_lvl);
2251 return CMD_SUCCESS;
2252 }
2253 int level;
2254
2255 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2256 return CMD_ERR_NO_MATCH;
2257 zlog_set_level(ZLOG_DEST_MONITOR, level);
2258 return CMD_SUCCESS;
2259 }
2260
2261 DEFUN (no_config_log_monitor,
2262 no_config_log_monitor_cmd,
2263 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2264 NO_STR
2265 "Logging control\n"
2266 "Disable terminal line (monitor) logging\n"
2267 LOG_LEVEL_DESC)
2268 {
2269 zlog_set_level(ZLOG_DEST_MONITOR, ZLOG_DISABLED);
2270 return CMD_SUCCESS;
2271 }
2272
2273 static int set_log_file(struct vty *vty, const char *fname, int loglevel)
2274 {
2275 int ret;
2276 char *p = NULL;
2277 const char *fullpath;
2278
2279 /* Path detection. */
2280 if (!IS_DIRECTORY_SEP(*fname)) {
2281 char cwd[MAXPATHLEN + 1];
2282 cwd[MAXPATHLEN] = '\0';
2283
2284 if (getcwd(cwd, MAXPATHLEN) == NULL) {
2285 flog_err_sys(EC_LIB_SYSTEM_CALL,
2286 "config_log_file: Unable to alloc mem!");
2287 return CMD_WARNING_CONFIG_FAILED;
2288 }
2289
2290 p = XMALLOC(MTYPE_TMP, strlen(cwd) + strlen(fname) + 2);
2291 sprintf(p, "%s/%s", cwd, fname);
2292 fullpath = p;
2293 } else
2294 fullpath = fname;
2295
2296 ret = zlog_set_file(fullpath, loglevel);
2297
2298 XFREE(MTYPE_TMP, p);
2299
2300 if (!ret) {
2301 if (vty)
2302 vty_out(vty, "can't open logfile %s\n", fname);
2303 return CMD_WARNING_CONFIG_FAILED;
2304 }
2305
2306 XFREE(MTYPE_HOST, host.logfile);
2307
2308 host.logfile = XSTRDUP(MTYPE_HOST, fname);
2309
2310 #if defined(HAVE_CUMULUS)
2311 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
2312 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2313 #endif
2314 return CMD_SUCCESS;
2315 }
2316
2317 void command_setup_early_logging(const char *dest, const char *level)
2318 {
2319 char *token;
2320
2321 if (level) {
2322 int nlevel = level_match(level);
2323
2324 if (nlevel != ZLOG_DISABLED)
2325 zlog_default->default_lvl = nlevel;
2326 }
2327
2328 if (!dest)
2329 return;
2330
2331 if (strcmp(dest, "stdout") == 0) {
2332 zlog_set_level(ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2333 return;
2334 }
2335
2336 if (strcmp(dest, "syslog") == 0) {
2337 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2338 return;
2339 }
2340
2341 token = strstr(dest, ":");
2342 if (token == NULL)
2343 return;
2344
2345 token++;
2346
2347 set_log_file(NULL, token, zlog_default->default_lvl);
2348 }
2349
2350 DEFUN (config_log_file,
2351 config_log_file_cmd,
2352 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2353 "Logging control\n"
2354 "Logging to file\n"
2355 "Logging filename\n"
2356 LOG_LEVEL_DESC)
2357 {
2358 int idx_filename = 2;
2359 int idx_log_levels = 3;
2360 if (argc == 4) {
2361 int level;
2362 if ((level = level_match(argv[idx_log_levels]->arg))
2363 == ZLOG_DISABLED)
2364 return CMD_ERR_NO_MATCH;
2365 return set_log_file(vty, argv[idx_filename]->arg, level);
2366 } else
2367 return set_log_file(vty, argv[idx_filename]->arg,
2368 zlog_default->default_lvl);
2369 }
2370
2371 static void disable_log_file(void)
2372 {
2373 zlog_reset_file();
2374
2375 XFREE(MTYPE_HOST, host.logfile);
2376 }
2377
2378 DEFUN (no_config_log_file,
2379 no_config_log_file_cmd,
2380 "no log file [FILENAME [LEVEL]]",
2381 NO_STR
2382 "Logging control\n"
2383 "Cancel logging to file\n"
2384 "Logging file name\n"
2385 "Logging level\n")
2386 {
2387 disable_log_file();
2388 return CMD_SUCCESS;
2389 }
2390
2391 DEFUN (config_log_syslog,
2392 config_log_syslog_cmd,
2393 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2394 "Logging control\n"
2395 "Set syslog logging level\n"
2396 LOG_LEVEL_DESC)
2397 {
2398 int idx_log_levels = 2;
2399
2400 if (argc == 3) {
2401 int level;
2402 if ((level = level_match(argv[idx_log_levels]->arg))
2403 == ZLOG_DISABLED)
2404 return CMD_ERR_NO_MATCH;
2405 zlog_set_level(ZLOG_DEST_SYSLOG, level);
2406 return CMD_SUCCESS;
2407 } else {
2408 zlog_set_level(ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2409 return CMD_SUCCESS;
2410 }
2411 }
2412
2413 DEFUN (no_config_log_syslog,
2414 no_config_log_syslog_cmd,
2415 "no log syslog [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>] [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2416 NO_STR
2417 "Logging control\n"
2418 "Cancel logging to syslog\n"
2419 LOG_FACILITY_DESC
2420 LOG_LEVEL_DESC)
2421 {
2422 zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2423 return CMD_SUCCESS;
2424 }
2425
2426 DEFUN (config_log_facility,
2427 config_log_facility_cmd,
2428 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2429 "Logging control\n"
2430 "Facility parameter for syslog messages\n"
2431 LOG_FACILITY_DESC)
2432 {
2433 int idx_target = 2;
2434 int facility = facility_match(argv[idx_target]->arg);
2435
2436 zlog_default->facility = facility;
2437 return CMD_SUCCESS;
2438 }
2439
2440 DEFUN (no_config_log_facility,
2441 no_config_log_facility_cmd,
2442 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2443 NO_STR
2444 "Logging control\n"
2445 "Reset syslog facility to default (daemon)\n"
2446 LOG_FACILITY_DESC)
2447 {
2448 zlog_default->facility = LOG_DAEMON;
2449 return CMD_SUCCESS;
2450 }
2451
2452 DEFUN (config_log_record_priority,
2453 config_log_record_priority_cmd,
2454 "log record-priority",
2455 "Logging control\n"
2456 "Log the priority of the message within the message\n")
2457 {
2458 zlog_default->record_priority = 1;
2459 return CMD_SUCCESS;
2460 }
2461
2462 DEFUN (no_config_log_record_priority,
2463 no_config_log_record_priority_cmd,
2464 "no log record-priority",
2465 NO_STR
2466 "Logging control\n"
2467 "Do not log the priority of the message within the message\n")
2468 {
2469 zlog_default->record_priority = 0;
2470 return CMD_SUCCESS;
2471 }
2472
2473 DEFUN (config_log_timestamp_precision,
2474 config_log_timestamp_precision_cmd,
2475 "log timestamp precision (0-6)",
2476 "Logging control\n"
2477 "Timestamp configuration\n"
2478 "Set the timestamp precision\n"
2479 "Number of subsecond digits\n")
2480 {
2481 int idx_number = 3;
2482 zlog_default->timestamp_precision =
2483 strtoul(argv[idx_number]->arg, NULL, 10);
2484 return CMD_SUCCESS;
2485 }
2486
2487 DEFUN (no_config_log_timestamp_precision,
2488 no_config_log_timestamp_precision_cmd,
2489 "no log timestamp precision",
2490 NO_STR
2491 "Logging control\n"
2492 "Timestamp configuration\n"
2493 "Reset the timestamp precision to the default value of 0\n")
2494 {
2495 zlog_default->timestamp_precision = 0;
2496 return CMD_SUCCESS;
2497 }
2498
2499 DEFUN (debug_memstats,
2500 debug_memstats_cmd,
2501 "[no] debug memstats-at-exit",
2502 NO_STR
2503 DEBUG_STR
2504 "Print memory type statistics at exit\n")
2505 {
2506 debug_memstats_at_exit = !!strcmp(argv[0]->text, "no");
2507 return CMD_SUCCESS;
2508 }
2509
2510 int cmd_banner_motd_file(const char *file)
2511 {
2512 int success = CMD_SUCCESS;
2513 char p[PATH_MAX];
2514 char *rpath;
2515 char *in;
2516
2517 rpath = realpath(file, p);
2518 if (!rpath)
2519 return CMD_ERR_NO_FILE;
2520 in = strstr(rpath, SYSCONFDIR);
2521 if (in == rpath) {
2522 XFREE(MTYPE_HOST, host.motdfile);
2523 host.motdfile = XSTRDUP(MTYPE_HOST, file);
2524 } else
2525 success = CMD_WARNING_CONFIG_FAILED;
2526
2527 return success;
2528 }
2529
2530 void cmd_banner_motd_line(const char *line)
2531 {
2532 XFREE(MTYPE_HOST, host.motd);
2533 host.motd = XSTRDUP(MTYPE_HOST, line);
2534 }
2535
2536 DEFUN (banner_motd_file,
2537 banner_motd_file_cmd,
2538 "banner motd file FILE",
2539 "Set banner\n"
2540 "Banner for motd\n"
2541 "Banner from a file\n"
2542 "Filename\n")
2543 {
2544 int idx_file = 3;
2545 const char *filename = argv[idx_file]->arg;
2546 int cmd = cmd_banner_motd_file(filename);
2547
2548 if (cmd == CMD_ERR_NO_FILE)
2549 vty_out(vty, "%s does not exist", filename);
2550 else if (cmd == CMD_WARNING_CONFIG_FAILED)
2551 vty_out(vty, "%s must be in %s", filename, SYSCONFDIR);
2552
2553 return cmd;
2554 }
2555
2556 DEFUN (banner_motd_line,
2557 banner_motd_line_cmd,
2558 "banner motd line LINE...",
2559 "Set banner\n"
2560 "Banner for motd\n"
2561 "Banner from an input\n"
2562 "Text\n")
2563 {
2564 int idx = 0;
2565 char *motd;
2566
2567 argv_find(argv, argc, "LINE", &idx);
2568 motd = argv_concat(argv, argc, idx);
2569
2570 cmd_banner_motd_line(motd);
2571 XFREE(MTYPE_TMP, motd);
2572
2573 return CMD_SUCCESS;
2574 }
2575
2576 DEFUN (banner_motd_default,
2577 banner_motd_default_cmd,
2578 "banner motd default",
2579 "Set banner string\n"
2580 "Strings for motd\n"
2581 "Default string\n")
2582 {
2583 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2584 return CMD_SUCCESS;
2585 }
2586
2587 DEFUN (no_banner_motd,
2588 no_banner_motd_cmd,
2589 "no banner motd",
2590 NO_STR
2591 "Set banner string\n"
2592 "Strings for motd\n")
2593 {
2594 host.motd = NULL;
2595 if (host.motdfile)
2596 XFREE(MTYPE_HOST, host.motdfile);
2597 host.motdfile = NULL;
2598 return CMD_SUCCESS;
2599 }
2600
2601 DEFUN(find,
2602 find_cmd,
2603 "find REGEX",
2604 "Find CLI command matching a regular expression\n"
2605 "Search pattern (POSIX regex)\n")
2606 {
2607 char *pattern = argv[1]->arg;
2608 const struct cmd_node *node;
2609 const struct cmd_element *cli;
2610 vector clis;
2611
2612 regex_t exp = {};
2613
2614 int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED);
2615
2616 if (cr != 0) {
2617 switch (cr) {
2618 case REG_BADBR:
2619 vty_out(vty, "%% Invalid {...} expression\n");
2620 break;
2621 case REG_BADRPT:
2622 vty_out(vty, "%% Bad repetition operator\n");
2623 break;
2624 case REG_BADPAT:
2625 vty_out(vty, "%% Regex syntax error\n");
2626 break;
2627 case REG_ECOLLATE:
2628 vty_out(vty, "%% Invalid collating element\n");
2629 break;
2630 case REG_ECTYPE:
2631 vty_out(vty, "%% Invalid character class name\n");
2632 break;
2633 case REG_EESCAPE:
2634 vty_out(vty,
2635 "%% Regex ended with escape character (\\)\n");
2636 break;
2637 case REG_ESUBREG:
2638 vty_out(vty,
2639 "%% Invalid number in \\digit construction\n");
2640 break;
2641 case REG_EBRACK:
2642 vty_out(vty, "%% Unbalanced square brackets\n");
2643 break;
2644 case REG_EPAREN:
2645 vty_out(vty, "%% Unbalanced parentheses\n");
2646 break;
2647 case REG_EBRACE:
2648 vty_out(vty, "%% Unbalanced braces\n");
2649 break;
2650 case REG_ERANGE:
2651 vty_out(vty,
2652 "%% Invalid endpoint in range expression\n");
2653 break;
2654 case REG_ESPACE:
2655 vty_out(vty, "%% Failed to compile (out of memory)\n");
2656 break;
2657 }
2658
2659 goto done;
2660 }
2661
2662
2663 for (unsigned int i = 0; i < vector_active(cmdvec); i++) {
2664 node = vector_slot(cmdvec, i);
2665 if (!node)
2666 continue;
2667 clis = node->cmd_vector;
2668 for (unsigned int j = 0; j < vector_active(clis); j++) {
2669 cli = vector_slot(clis, j);
2670
2671 if (regexec(&exp, cli->string, 0, NULL, 0) == 0)
2672 vty_out(vty, " (%s) %s\n",
2673 node->name, cli->string);
2674 }
2675 }
2676
2677 done:
2678 regfree(&exp);
2679 return CMD_SUCCESS;
2680 }
2681
2682 /* Set config filename. Called from vty.c */
2683 void host_config_set(const char *filename)
2684 {
2685 XFREE(MTYPE_HOST, host.config);
2686 host.config = XSTRDUP(MTYPE_HOST, filename);
2687 }
2688
2689 const char *host_config_get(void)
2690 {
2691 return host.config;
2692 }
2693
2694 void install_default(enum node_type node)
2695 {
2696 install_element(node, &config_exit_cmd);
2697 install_element(node, &config_quit_cmd);
2698 install_element(node, &config_end_cmd);
2699 install_element(node, &config_help_cmd);
2700 install_element(node, &config_list_cmd);
2701 install_element(node, &show_cli_graph_cmd);
2702 install_element(node, &find_cmd);
2703
2704 install_element(node, &config_write_cmd);
2705 install_element(node, &show_running_config_cmd);
2706
2707 install_element(node, &autocomplete_cmd);
2708
2709 nb_cli_install_default(node);
2710 }
2711
2712 /* Initialize command interface. Install basic nodes and commands.
2713 *
2714 * terminal = 0 -- vtysh / no logging, no config control
2715 * terminal = 1 -- normal daemon
2716 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2717 void cmd_init(int terminal)
2718 {
2719 struct utsname names;
2720
2721 uname(&names);
2722 qobj_init();
2723
2724 /* register command preprocessors */
2725 hook_register(cmd_execute, handle_pipe_action);
2726 hook_register(cmd_execute_done, handle_pipe_action_done);
2727
2728 varhandlers = list_new();
2729
2730 /* Allocate initial top vector of commands. */
2731 cmdvec = vector_init(VECTOR_MIN_SIZE);
2732
2733 /* Default host value settings. */
2734 host.name = XSTRDUP(MTYPE_HOST, names.nodename);
2735 #ifdef HAVE_STRUCT_UTSNAME_DOMAINNAME
2736 if ((strcmp(names.domainname, "(none)") == 0))
2737 host.domainname = NULL;
2738 else
2739 host.domainname = XSTRDUP(MTYPE_HOST, names.domainname);
2740 #else
2741 host.domainname = NULL;
2742 #endif
2743 host.password = NULL;
2744 host.enable = NULL;
2745 host.logfile = NULL;
2746 host.config = NULL;
2747 host.noconfig = (terminal < 0);
2748 host.lines = -1;
2749 cmd_banner_motd_line(FRR_DEFAULT_MOTD);
2750 host.motdfile = NULL;
2751
2752 /* Install top nodes. */
2753 install_node(&view_node);
2754 install_node(&enable_node);
2755 install_node(&auth_node);
2756 install_node(&auth_enable_node);
2757 install_node(&config_node);
2758
2759 /* Each node's basic commands. */
2760 install_element(VIEW_NODE, &show_version_cmd);
2761 install_element(ENABLE_NODE, &show_startup_config_cmd);
2762
2763 if (terminal) {
2764 install_element(ENABLE_NODE, &debug_memstats_cmd);
2765
2766 install_element(VIEW_NODE, &config_list_cmd);
2767 install_element(VIEW_NODE, &config_exit_cmd);
2768 install_element(VIEW_NODE, &config_quit_cmd);
2769 install_element(VIEW_NODE, &config_help_cmd);
2770 install_element(VIEW_NODE, &config_enable_cmd);
2771 install_element(VIEW_NODE, &config_terminal_length_cmd);
2772 install_element(VIEW_NODE, &config_terminal_no_length_cmd);
2773 install_element(VIEW_NODE, &show_logging_cmd);
2774 install_element(VIEW_NODE, &show_commandtree_cmd);
2775 install_element(VIEW_NODE, &echo_cmd);
2776 install_element(VIEW_NODE, &autocomplete_cmd);
2777 install_element(VIEW_NODE, &find_cmd);
2778
2779 install_element(ENABLE_NODE, &config_end_cmd);
2780 install_element(ENABLE_NODE, &config_disable_cmd);
2781 install_element(ENABLE_NODE, &config_terminal_cmd);
2782 install_element(ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2783 install_element(ENABLE_NODE, &config_write_cmd);
2784 install_element(ENABLE_NODE, &show_running_config_cmd);
2785 install_element(ENABLE_NODE, &config_logmsg_cmd);
2786
2787 install_default(CONFIG_NODE);
2788
2789 thread_cmd_init();
2790 workqueue_cmd_init();
2791 hash_cmd_init();
2792 }
2793
2794 install_element(CONFIG_NODE, &hostname_cmd);
2795 install_element(CONFIG_NODE, &no_hostname_cmd);
2796 install_element(CONFIG_NODE, &domainname_cmd);
2797 install_element(CONFIG_NODE, &no_domainname_cmd);
2798
2799 if (terminal > 0) {
2800 install_element(CONFIG_NODE, &debug_memstats_cmd);
2801
2802 install_element(CONFIG_NODE, &password_cmd);
2803 install_element(CONFIG_NODE, &no_password_cmd);
2804 install_element(CONFIG_NODE, &enable_password_cmd);
2805 install_element(CONFIG_NODE, &no_enable_password_cmd);
2806
2807 install_element(CONFIG_NODE, &config_log_stdout_cmd);
2808 install_element(CONFIG_NODE, &no_config_log_stdout_cmd);
2809 install_element(CONFIG_NODE, &config_log_monitor_cmd);
2810 install_element(CONFIG_NODE, &no_config_log_monitor_cmd);
2811 install_element(CONFIG_NODE, &config_log_file_cmd);
2812 install_element(CONFIG_NODE, &no_config_log_file_cmd);
2813 install_element(CONFIG_NODE, &config_log_syslog_cmd);
2814 install_element(CONFIG_NODE, &no_config_log_syslog_cmd);
2815 install_element(CONFIG_NODE, &config_log_facility_cmd);
2816 install_element(CONFIG_NODE, &no_config_log_facility_cmd);
2817 install_element(CONFIG_NODE, &config_log_record_priority_cmd);
2818 install_element(CONFIG_NODE,
2819 &no_config_log_record_priority_cmd);
2820 install_element(CONFIG_NODE,
2821 &config_log_timestamp_precision_cmd);
2822 install_element(CONFIG_NODE,
2823 &no_config_log_timestamp_precision_cmd);
2824 install_element(CONFIG_NODE, &service_password_encrypt_cmd);
2825 install_element(CONFIG_NODE, &no_service_password_encrypt_cmd);
2826 install_element(CONFIG_NODE, &banner_motd_default_cmd);
2827 install_element(CONFIG_NODE, &banner_motd_file_cmd);
2828 install_element(CONFIG_NODE, &banner_motd_line_cmd);
2829 install_element(CONFIG_NODE, &no_banner_motd_cmd);
2830 install_element(CONFIG_NODE, &service_terminal_length_cmd);
2831 install_element(CONFIG_NODE, &no_service_terminal_length_cmd);
2832
2833 vrf_install_commands();
2834 }
2835
2836 #ifdef DEV_BUILD
2837 grammar_sandbox_init();
2838 #endif
2839 }
2840
2841 void cmd_terminate(void)
2842 {
2843 struct cmd_node *cmd_node;
2844
2845 hook_unregister(cmd_execute, handle_pipe_action);
2846 hook_unregister(cmd_execute_done, handle_pipe_action_done);
2847
2848 if (cmdvec) {
2849 for (unsigned int i = 0; i < vector_active(cmdvec); i++)
2850 if ((cmd_node = vector_slot(cmdvec, i)) != NULL) {
2851 // deleting the graph delets the cmd_element as
2852 // well
2853 graph_delete_graph(cmd_node->cmdgraph);
2854 vector_free(cmd_node->cmd_vector);
2855 hash_clean(cmd_node->cmd_hash, NULL);
2856 hash_free(cmd_node->cmd_hash);
2857 cmd_node->cmd_hash = NULL;
2858 }
2859
2860 vector_free(cmdvec);
2861 cmdvec = NULL;
2862 }
2863
2864 XFREE(MTYPE_HOST, host.name);
2865 XFREE(MTYPE_HOST, host.domainname);
2866 XFREE(MTYPE_HOST, host.password);
2867 XFREE(MTYPE_HOST, host.password_encrypt);
2868 XFREE(MTYPE_HOST, host.enable);
2869 XFREE(MTYPE_HOST, host.enable_encrypt);
2870 XFREE(MTYPE_HOST, host.logfile);
2871 XFREE(MTYPE_HOST, host.motdfile);
2872 XFREE(MTYPE_HOST, host.config);
2873 XFREE(MTYPE_HOST, host.motd);
2874
2875 list_delete(&varhandlers);
2876 qobj_finish();
2877 }