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