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