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