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