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