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