]> git.proxmox.com Git - mirror_frr.git/blob - lib/command.c
lib: remove remaining struct zlog * args
[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 case BGP_EVPN_NODE:
1059 ret = BGP_NODE;
1060 break;
1061 case KEYCHAIN_KEY_NODE:
1062 ret = KEYCHAIN_NODE;
1063 break;
1064 case LINK_PARAMS_NODE:
1065 ret = INTERFACE_NODE;
1066 break;
1067 case LDP_IPV4_NODE:
1068 case LDP_IPV6_NODE:
1069 ret = LDP_NODE;
1070 break;
1071 case LDP_IPV4_IFACE_NODE:
1072 ret = LDP_IPV4_NODE;
1073 break;
1074 case LDP_IPV6_IFACE_NODE:
1075 ret = LDP_IPV6_NODE;
1076 break;
1077 case LDP_PSEUDOWIRE_NODE:
1078 ret = LDP_L2VPN_NODE;
1079 break;
1080 default:
1081 ret = CONFIG_NODE;
1082 break;
1083 }
1084
1085 return ret;
1086 }
1087
1088 /* Execute command by argument vline vector. */
1089 static int
1090 cmd_execute_command_real (vector vline,
1091 enum filter_type filter,
1092 struct vty *vty,
1093 const struct cmd_element **cmd)
1094 {
1095 struct list *argv_list;
1096 enum matcher_rv status;
1097 const struct cmd_element *matched_element = NULL;
1098
1099 struct graph *cmdgraph = cmd_node_graph (cmdvec, vty->node);
1100 status = command_match (cmdgraph, vline, &argv_list, &matched_element);
1101
1102 if (cmd)
1103 *cmd = matched_element;
1104
1105 // if matcher error, return corresponding CMD_ERR
1106 if (MATCHER_ERROR(status))
1107 {
1108 if (argv_list)
1109 list_delete (argv_list);
1110 switch (status)
1111 {
1112 case MATCHER_INCOMPLETE:
1113 return CMD_ERR_INCOMPLETE;
1114 case MATCHER_AMBIGUOUS:
1115 return CMD_ERR_AMBIGUOUS;
1116 default:
1117 return CMD_ERR_NO_MATCH;
1118 }
1119 }
1120
1121 // build argv array from argv list
1122 struct cmd_token **argv = XMALLOC (MTYPE_TMP, argv_list->count * sizeof (struct cmd_token *));
1123 struct listnode *ln;
1124 struct cmd_token *token;
1125 unsigned int i = 0;
1126 for (ALL_LIST_ELEMENTS_RO(argv_list,ln,token))
1127 argv[i++] = token;
1128
1129 int argc = argv_list->count;
1130
1131 int ret;
1132 if (matched_element->daemon)
1133 ret = CMD_SUCCESS_DAEMON;
1134 else
1135 ret = matched_element->func (matched_element, vty, argc, argv);
1136
1137 // delete list and cmd_token's in it
1138 list_delete (argv_list);
1139 XFREE (MTYPE_TMP, argv);
1140
1141 return ret;
1142 }
1143
1144 /**
1145 * Execute a given command, handling things like "do ..." and checking
1146 * whether the given command might apply at a parent node if doesn't
1147 * apply for the current node.
1148 *
1149 * @param vline Command line input, vector of char* where each element is
1150 * one input token.
1151 * @param vty The vty context in which the command should be executed.
1152 * @param cmd Pointer where the struct cmd_element of the matched command
1153 * will be stored, if any. May be set to NULL if this info is
1154 * not needed.
1155 * @param vtysh If set != 0, don't lookup the command at parent nodes.
1156 * @return The status of the command that has been executed or an error code
1157 * as to why no command could be executed.
1158 */
1159 int
1160 cmd_execute_command (vector vline, struct vty *vty,
1161 const struct cmd_element **cmd,
1162 int vtysh)
1163 {
1164 int ret, saved_ret = 0;
1165 enum node_type onode, try_node;
1166
1167 onode = try_node = vty->node;
1168
1169 if (cmd_try_do_shortcut(vty->node, vector_slot(vline, 0)))
1170 {
1171 vector shifted_vline;
1172 unsigned int index;
1173
1174 vty->node = ENABLE_NODE;
1175 /* We can try it on enable node, cos' the vty is authenticated */
1176
1177 shifted_vline = vector_init (vector_count(vline));
1178 /* use memcpy? */
1179 for (index = 1; index < vector_active (vline); index++)
1180 vector_set_index (shifted_vline, index-1, vector_lookup(vline, index));
1181
1182 ret = cmd_execute_command_real (shifted_vline, FILTER_RELAXED, vty, cmd);
1183
1184 vector_free(shifted_vline);
1185 vty->node = onode;
1186 return ret;
1187 }
1188
1189 saved_ret = ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
1190
1191 if (vtysh)
1192 return saved_ret;
1193
1194 if (ret != CMD_SUCCESS && ret != CMD_WARNING)
1195 {
1196 /* This assumes all nodes above CONFIG_NODE are childs of CONFIG_NODE */
1197 while (vty->node > CONFIG_NODE)
1198 {
1199 try_node = node_parent(try_node);
1200 vty->node = try_node;
1201 ret = cmd_execute_command_real (vline, FILTER_RELAXED, vty, cmd);
1202 if (ret == CMD_SUCCESS || ret == CMD_WARNING)
1203 return ret;
1204 }
1205 /* no command succeeded, reset the vty to the original node */
1206 vty->node = onode;
1207 }
1208
1209 /* return command status for original node */
1210 return saved_ret;
1211 }
1212
1213 /**
1214 * Execute a given command, matching it strictly against the current node.
1215 * This mode is used when reading config files.
1216 *
1217 * @param vline Command line input, vector of char* where each element is
1218 * one input token.
1219 * @param vty The vty context in which the command should be executed.
1220 * @param cmd Pointer where the struct cmd_element* of the matched command
1221 * will be stored, if any. May be set to NULL if this info is
1222 * not needed.
1223 * @return The status of the command that has been executed or an error code
1224 * as to why no command could be executed.
1225 */
1226 int
1227 cmd_execute_command_strict (vector vline, struct vty *vty,
1228 const struct cmd_element **cmd)
1229 {
1230 return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd);
1231 }
1232
1233 /**
1234 * Parse one line of config, walking up the parse tree attempting to find a match
1235 *
1236 * @param vty The vty context in which the command should be executed.
1237 * @param cmd Pointer where the struct cmd_element* of the match command
1238 * will be stored, if any. May be set to NULL if this info is
1239 * not needed.
1240 * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON
1241 * or not.
1242 * @return The status of the command that has been executed or an error code
1243 * as to why no command could be executed.
1244 */
1245 int
1246 command_config_read_one_line (struct vty *vty, const struct cmd_element **cmd, int use_daemon)
1247 {
1248 vector vline;
1249 int saved_node;
1250 int ret;
1251
1252 vline = cmd_make_strvec (vty->buf);
1253
1254 /* In case of comment line */
1255 if (vline == NULL)
1256 return CMD_SUCCESS;
1257
1258 /* Execute configuration command : this is strict match */
1259 ret = cmd_execute_command_strict (vline, vty, cmd);
1260
1261 // Climb the tree and try the command again at each node
1262 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
1263 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
1264 ret != CMD_SUCCESS &&
1265 ret != CMD_WARNING &&
1266 vty->node != CONFIG_NODE) {
1267
1268 saved_node = vty->node;
1269
1270 while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
1271 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
1272 ret != CMD_SUCCESS &&
1273 ret != CMD_WARNING &&
1274 vty->node > CONFIG_NODE) {
1275 vty->node = node_parent(vty->node);
1276 ret = cmd_execute_command_strict (vline, vty, cmd);
1277 }
1278
1279 // If climbing the tree did not work then ignore the command and
1280 // stay at the same node
1281 if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) &&
1282 !(!use_daemon && ret == CMD_ERR_NOTHING_TODO) &&
1283 ret != CMD_SUCCESS &&
1284 ret != CMD_WARNING)
1285 {
1286 vty->node = saved_node;
1287 }
1288 }
1289
1290 if (ret != CMD_SUCCESS && ret != CMD_WARNING)
1291 memcpy (vty->error_buf, vty->buf, VTY_BUFSIZ);
1292
1293 cmd_free_strvec (vline);
1294
1295 return ret;
1296 }
1297
1298 /* Configuration make from file. */
1299 int
1300 config_from_file (struct vty *vty, FILE *fp, unsigned int *line_num)
1301 {
1302 int ret, error_ret=0;
1303 *line_num = 0;
1304
1305 while (fgets (vty->buf, VTY_BUFSIZ, fp))
1306 {
1307 if (!error_ret)
1308 ++(*line_num);
1309
1310 ret = command_config_read_one_line (vty, NULL, 0);
1311
1312 if (ret != CMD_SUCCESS && ret != CMD_WARNING &&
1313 ret != CMD_ERR_NOTHING_TODO)
1314 error_ret = ret;
1315 }
1316
1317 if (error_ret) {
1318 return error_ret;
1319 }
1320
1321 return CMD_SUCCESS;
1322 }
1323
1324 /* Configuration from terminal */
1325 DEFUN (config_terminal,
1326 config_terminal_cmd,
1327 "configure terminal",
1328 "Configuration from vty interface\n"
1329 "Configuration terminal\n")
1330 {
1331 if (vty_config_lock (vty))
1332 vty->node = CONFIG_NODE;
1333 else
1334 {
1335 vty_out (vty, "VTY configuration is locked by other VTY%s", VTY_NEWLINE);
1336 return CMD_WARNING;
1337 }
1338 return CMD_SUCCESS;
1339 }
1340
1341 /* Enable command */
1342 DEFUN (enable,
1343 config_enable_cmd,
1344 "enable",
1345 "Turn on privileged mode command\n")
1346 {
1347 /* If enable password is NULL, change to ENABLE_NODE */
1348 if ((host.enable == NULL && host.enable_encrypt == NULL) ||
1349 vty->type == VTY_SHELL_SERV)
1350 vty->node = ENABLE_NODE;
1351 else
1352 vty->node = AUTH_ENABLE_NODE;
1353
1354 return CMD_SUCCESS;
1355 }
1356
1357 /* Disable command */
1358 DEFUN (disable,
1359 config_disable_cmd,
1360 "disable",
1361 "Turn off privileged mode command\n")
1362 {
1363 if (vty->node == ENABLE_NODE)
1364 vty->node = VIEW_NODE;
1365 return CMD_SUCCESS;
1366 }
1367
1368 /* Down vty node level. */
1369 DEFUN (config_exit,
1370 config_exit_cmd,
1371 "exit",
1372 "Exit current mode and down to previous mode\n")
1373 {
1374 cmd_exit (vty);
1375 return CMD_SUCCESS;
1376 }
1377
1378 void
1379 cmd_exit (struct vty *vty)
1380 {
1381 switch (vty->node)
1382 {
1383 case VIEW_NODE:
1384 case ENABLE_NODE:
1385 if (vty_shell (vty))
1386 exit (0);
1387 else
1388 vty->status = VTY_CLOSE;
1389 break;
1390 case CONFIG_NODE:
1391 vty->node = ENABLE_NODE;
1392 vty_config_unlock (vty);
1393 break;
1394 case INTERFACE_NODE:
1395 case NS_NODE:
1396 case VRF_NODE:
1397 case ZEBRA_NODE:
1398 case BGP_NODE:
1399 case RIP_NODE:
1400 case RIPNG_NODE:
1401 case OSPF_NODE:
1402 case OSPF6_NODE:
1403 case LDP_NODE:
1404 case LDP_L2VPN_NODE:
1405 case ISIS_NODE:
1406 case KEYCHAIN_NODE:
1407 case MASC_NODE:
1408 case RMAP_NODE:
1409 case PIM_NODE:
1410 case VTY_NODE:
1411 vty->node = CONFIG_NODE;
1412 break;
1413 case BGP_IPV4_NODE:
1414 case BGP_IPV4M_NODE:
1415 case BGP_VPNV4_NODE:
1416 case BGP_VPNV6_NODE:
1417 case BGP_ENCAP_NODE:
1418 case BGP_ENCAPV6_NODE:
1419 case BGP_VRF_POLICY_NODE:
1420 case BGP_VNC_DEFAULTS_NODE:
1421 case BGP_VNC_NVE_GROUP_NODE:
1422 case BGP_VNC_L2_GROUP_NODE:
1423 case BGP_IPV6_NODE:
1424 case BGP_IPV6M_NODE:
1425 case BGP_EVPN_NODE:
1426 vty->node = BGP_NODE;
1427 break;
1428 case LDP_IPV4_NODE:
1429 case LDP_IPV6_NODE:
1430 vty->node = LDP_NODE;
1431 break;
1432 case LDP_IPV4_IFACE_NODE:
1433 vty->node = LDP_IPV4_NODE;
1434 break;
1435 case LDP_IPV6_IFACE_NODE:
1436 vty->node = LDP_IPV6_NODE;
1437 break;
1438 case LDP_PSEUDOWIRE_NODE:
1439 vty->node = LDP_L2VPN_NODE;
1440 break;
1441 case KEYCHAIN_KEY_NODE:
1442 vty->node = KEYCHAIN_NODE;
1443 break;
1444 case LINK_PARAMS_NODE:
1445 vty->node = INTERFACE_NODE;
1446 break;
1447 default:
1448 break;
1449 }
1450 }
1451
1452 /* ALIAS_FIXME */
1453 DEFUN (config_quit,
1454 config_quit_cmd,
1455 "quit",
1456 "Exit current mode and down to previous mode\n")
1457 {
1458 return config_exit (self, vty, argc, argv);
1459 }
1460
1461
1462 /* End of configuration. */
1463 DEFUN (config_end,
1464 config_end_cmd,
1465 "end",
1466 "End current mode and change to enable mode.")
1467 {
1468 switch (vty->node)
1469 {
1470 case VIEW_NODE:
1471 case ENABLE_NODE:
1472 /* Nothing to do. */
1473 break;
1474 case CONFIG_NODE:
1475 case INTERFACE_NODE:
1476 case NS_NODE:
1477 case VRF_NODE:
1478 case ZEBRA_NODE:
1479 case RIP_NODE:
1480 case RIPNG_NODE:
1481 case BGP_NODE:
1482 case BGP_ENCAP_NODE:
1483 case BGP_ENCAPV6_NODE:
1484 case BGP_VRF_POLICY_NODE:
1485 case BGP_VNC_DEFAULTS_NODE:
1486 case BGP_VNC_NVE_GROUP_NODE:
1487 case BGP_VNC_L2_GROUP_NODE:
1488 case BGP_VPNV4_NODE:
1489 case BGP_VPNV6_NODE:
1490 case BGP_IPV4_NODE:
1491 case BGP_IPV4M_NODE:
1492 case BGP_IPV6_NODE:
1493 case BGP_IPV6M_NODE:
1494 case BGP_EVPN_NODE:
1495 case RMAP_NODE:
1496 case OSPF_NODE:
1497 case OSPF6_NODE:
1498 case LDP_NODE:
1499 case LDP_IPV4_NODE:
1500 case LDP_IPV6_NODE:
1501 case LDP_IPV4_IFACE_NODE:
1502 case LDP_IPV6_IFACE_NODE:
1503 case LDP_L2VPN_NODE:
1504 case LDP_PSEUDOWIRE_NODE:
1505 case ISIS_NODE:
1506 case KEYCHAIN_NODE:
1507 case KEYCHAIN_KEY_NODE:
1508 case MASC_NODE:
1509 case PIM_NODE:
1510 case VTY_NODE:
1511 case LINK_PARAMS_NODE:
1512 vty_config_unlock (vty);
1513 vty->node = ENABLE_NODE;
1514 break;
1515 default:
1516 break;
1517 }
1518 return CMD_SUCCESS;
1519 }
1520
1521 /* Show version. */
1522 DEFUN (show_version,
1523 show_version_cmd,
1524 "show version",
1525 SHOW_STR
1526 "Displays zebra version\n")
1527 {
1528 vty_out (vty, "%s %s (%s).%s", FRR_FULL_NAME, FRR_VERSION,
1529 host.name ? host.name : "",
1530 VTY_NEWLINE);
1531 vty_out (vty, "%s%s%s", FRR_COPYRIGHT, GIT_INFO, VTY_NEWLINE);
1532 vty_out (vty, "configured with:%s %s%s", VTY_NEWLINE,
1533 FRR_CONFIG_ARGS, VTY_NEWLINE);
1534
1535 return CMD_SUCCESS;
1536 }
1537
1538 /* Help display function for all node. */
1539 DEFUN (config_help,
1540 config_help_cmd,
1541 "help",
1542 "Description of the interactive help system\n")
1543 {
1544 vty_out (vty,
1545 "Quagga VTY provides advanced help feature. When you need help,%s\
1546 anytime at the command line please press '?'.%s\
1547 %s\
1548 If nothing matches, the help list will be empty and you must backup%s\
1549 until entering a '?' shows the available options.%s\
1550 Two styles of help are provided:%s\
1551 1. Full help is available when you are ready to enter a%s\
1552 command argument (e.g. 'show ?') and describes each possible%s\
1553 argument.%s\
1554 2. Partial help is provided when an abbreviated argument is entered%s\
1555 and you want to know what arguments match the input%s\
1556 (e.g. 'show me?'.)%s%s", VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
1557 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE,
1558 VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
1559 return CMD_SUCCESS;
1560 }
1561
1562 static void
1563 permute (struct graph_node *start, struct vty *vty)
1564 {
1565 static struct list *position = NULL;
1566 if (!position) position = list_new ();
1567
1568 // recursive dfs
1569 listnode_add (position, start);
1570 for (unsigned int i = 0; i < vector_active (start->to); i++)
1571 {
1572 struct graph_node *gn = vector_slot (start->to, i);
1573 struct cmd_token *tok = gn->data;
1574 if (tok->attr == CMD_ATTR_HIDDEN ||
1575 tok->attr == CMD_ATTR_DEPRECATED)
1576 continue;
1577 else if (tok->type == END_TKN || gn == start)
1578 {
1579 struct graph_node *gnn;
1580 struct listnode *ln;
1581 vty_out (vty, " ");
1582 for (ALL_LIST_ELEMENTS_RO (position,ln,gnn))
1583 {
1584 struct cmd_token *tt = gnn->data;
1585 if (tt->type < SPECIAL_TKN)
1586 vty_out (vty, " %s", tt->text);
1587 }
1588 if (gn == start)
1589 vty_out (vty, "...");
1590 vty_out (vty, VTY_NEWLINE);
1591 }
1592 else
1593 permute (gn, vty);
1594 }
1595 list_delete_node (position, listtail(position));
1596 }
1597
1598 int
1599 cmd_list_cmds (struct vty *vty, int do_permute)
1600 {
1601 struct cmd_node *node = vector_slot (cmdvec, vty->node);
1602
1603 if (do_permute)
1604 permute (vector_slot (node->cmdgraph->nodes, 0), vty);
1605 else
1606 {
1607 /* loop over all commands at this node */
1608 struct cmd_element *element = NULL;
1609 for (unsigned int i = 0; i < vector_active(node->cmd_vector); i++)
1610 if ((element = vector_slot (node->cmd_vector, i)) &&
1611 element->attr != CMD_ATTR_DEPRECATED &&
1612 element->attr != CMD_ATTR_HIDDEN)
1613 vty_out (vty, " %s%s", element->string, VTY_NEWLINE);
1614 }
1615 return CMD_SUCCESS;
1616 }
1617
1618 /* Help display function for all node. */
1619 DEFUN (config_list,
1620 config_list_cmd,
1621 "list [permutations]",
1622 "Print command list\n"
1623 "Print all possible command permutations\n")
1624 {
1625 return cmd_list_cmds (vty, argc == 2);
1626 }
1627
1628 DEFUN (show_commandtree,
1629 show_commandtree_cmd,
1630 "show commandtree [permutations]",
1631 SHOW_STR
1632 "Show command tree\n"
1633 "Permutations that we are interested in\n")
1634 {
1635 return cmd_list_cmds (vty, argc == 3);
1636 }
1637
1638 /* Write current configuration into file. */
1639
1640 DEFUN (config_write,
1641 config_write_cmd,
1642 "write [<file|memory|terminal>]",
1643 "Write running configuration to memory, network, or terminal\n"
1644 "Write to configuration file\n"
1645 "Write configuration currently in memory\n"
1646 "Write configuration to terminal\n")
1647 {
1648 int idx_type = 1;
1649 unsigned int i;
1650 int fd, dirfd;
1651 struct cmd_node *node;
1652 char *config_file, *slash;
1653 char *config_file_tmp = NULL;
1654 char *config_file_sav = NULL;
1655 int ret = CMD_WARNING;
1656 struct vty *file_vty;
1657 struct stat conf_stat;
1658
1659 // if command was 'write terminal' or 'show running-config'
1660 if (argc == 2 && (!strcmp(argv[idx_type]->text, "terminal") ||
1661 !strcmp(argv[0]->text, "show")))
1662 {
1663 if (vty->type == VTY_SHELL_SERV)
1664 {
1665 for (i = 0; i < vector_active (cmdvec); i++)
1666 if ((node = vector_slot (cmdvec, i)) && node->func && node->vtysh)
1667 {
1668 if ((*node->func) (vty))
1669 vty_out (vty, "!%s", VTY_NEWLINE);
1670 }
1671 }
1672 else
1673 {
1674 vty_out (vty, "%sCurrent configuration:%s", VTY_NEWLINE,
1675 VTY_NEWLINE);
1676 vty_out (vty, "!%s", VTY_NEWLINE);
1677
1678 for (i = 0; i < vector_active (cmdvec); i++)
1679 if ((node = vector_slot (cmdvec, i)) && node->func)
1680 {
1681 if ((*node->func) (vty))
1682 vty_out (vty, "!%s", VTY_NEWLINE);
1683 }
1684 vty_out (vty, "end%s",VTY_NEWLINE);
1685 }
1686 return CMD_SUCCESS;
1687 }
1688
1689 if (host.noconfig)
1690 return CMD_SUCCESS;
1691
1692 /* Check and see if we are operating under vtysh configuration */
1693 if (host.config == NULL)
1694 {
1695 vty_out (vty, "Can't save to configuration file, using vtysh.%s",
1696 VTY_NEWLINE);
1697 return CMD_WARNING;
1698 }
1699
1700 /* Get filename. */
1701 config_file = host.config;
1702
1703 #ifndef O_DIRECTORY
1704 #define O_DIRECTORY 0
1705 #endif
1706 slash = strrchr (config_file, '/');
1707 if (slash)
1708 {
1709 char *config_dir = XSTRDUP (MTYPE_TMP, config_file);
1710 config_dir[slash - config_file] = '\0';
1711 dirfd = open(config_dir, O_DIRECTORY | O_RDONLY);
1712 XFREE (MTYPE_TMP, config_dir);
1713 }
1714 else
1715 dirfd = open(".", O_DIRECTORY | O_RDONLY);
1716 /* if dirfd is invalid, directory sync fails, but we're still OK */
1717
1718 config_file_sav =
1719 XMALLOC (MTYPE_TMP, strlen (config_file) + strlen (CONF_BACKUP_EXT) + 1);
1720 strcpy (config_file_sav, config_file);
1721 strcat (config_file_sav, CONF_BACKUP_EXT);
1722
1723
1724 config_file_tmp = XMALLOC (MTYPE_TMP, strlen (config_file) + 8);
1725 sprintf (config_file_tmp, "%s.XXXXXX", config_file);
1726
1727 /* Open file to configuration write. */
1728 fd = mkstemp (config_file_tmp);
1729 if (fd < 0)
1730 {
1731 vty_out (vty, "Can't open configuration file %s.%s", config_file_tmp,
1732 VTY_NEWLINE);
1733 goto finished;
1734 }
1735 if (fchmod (fd, CONFIGFILE_MASK) != 0)
1736 {
1737 vty_out (vty, "Can't chmod configuration file %s: %s (%d).%s",
1738 config_file_tmp, safe_strerror(errno), errno, VTY_NEWLINE);
1739 goto finished;
1740 }
1741
1742 /* Make vty for configuration file. */
1743 file_vty = vty_new ();
1744 file_vty->wfd = fd;
1745 file_vty->type = VTY_FILE;
1746
1747 /* Config file header print. */
1748 vty_out (file_vty, "!\n! Zebra configuration saved from vty\n! ");
1749 vty_time_print (file_vty, 1);
1750 vty_out (file_vty, "!\n");
1751
1752 for (i = 0; i < vector_active (cmdvec); i++)
1753 if ((node = vector_slot (cmdvec, i)) && node->func)
1754 {
1755 if ((*node->func) (file_vty))
1756 vty_out (file_vty, "!\n");
1757 }
1758 vty_close (file_vty);
1759
1760 if (stat(config_file, &conf_stat) >= 0)
1761 {
1762 if (unlink (config_file_sav) != 0)
1763 if (errno != ENOENT)
1764 {
1765 vty_out (vty, "Can't unlink backup configuration file %s.%s", config_file_sav,
1766 VTY_NEWLINE);
1767 goto finished;
1768 }
1769 if (link (config_file, config_file_sav) != 0)
1770 {
1771 vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
1772 VTY_NEWLINE);
1773 goto finished;
1774 }
1775 if (dirfd >= 0)
1776 fsync (dirfd);
1777 }
1778 if (rename (config_file_tmp, config_file) != 0)
1779 {
1780 vty_out (vty, "Can't save configuration file %s.%s", config_file,
1781 VTY_NEWLINE);
1782 goto finished;
1783 }
1784 if (dirfd >= 0)
1785 fsync (dirfd);
1786
1787 vty_out (vty, "Configuration saved to %s%s", config_file,
1788 VTY_NEWLINE);
1789 ret = CMD_SUCCESS;
1790
1791 finished:
1792 if (ret != CMD_SUCCESS)
1793 unlink (config_file_tmp);
1794 if (dirfd >= 0)
1795 close (dirfd);
1796 XFREE (MTYPE_TMP, config_file_tmp);
1797 XFREE (MTYPE_TMP, config_file_sav);
1798 return ret;
1799 }
1800
1801 /* ALIAS_FIXME for 'write <terminal|memory>' */
1802 DEFUN (show_running_config,
1803 show_running_config_cmd,
1804 "show running-config",
1805 SHOW_STR
1806 "running configuration (same as write terminal/memory)\n")
1807 {
1808 return config_write (self, vty, argc, argv);
1809 }
1810
1811 /* ALIAS_FIXME for 'write file' */
1812 DEFUN (copy_runningconf_startupconf,
1813 copy_runningconf_startupconf_cmd,
1814 "copy running-config startup-config",
1815 "Copy configuration\n"
1816 "Copy running config to... \n"
1817 "Copy running config to startup config (same as write file)\n")
1818 {
1819 return config_write (self, vty, argc, argv);
1820 }
1821 /** -- **/
1822
1823 /* Write startup configuration into the terminal. */
1824 DEFUN (show_startup_config,
1825 show_startup_config_cmd,
1826 "show startup-config",
1827 SHOW_STR
1828 "Contents of startup configuration\n")
1829 {
1830 char buf[BUFSIZ];
1831 FILE *confp;
1832
1833 if (host.noconfig)
1834 return CMD_SUCCESS;
1835 if (host.config == NULL)
1836 return CMD_WARNING;
1837
1838 confp = fopen (host.config, "r");
1839 if (confp == NULL)
1840 {
1841 vty_out (vty, "Can't open configuration file [%s] due to '%s'%s",
1842 host.config, safe_strerror(errno), VTY_NEWLINE);
1843 return CMD_WARNING;
1844 }
1845
1846 while (fgets (buf, BUFSIZ, confp))
1847 {
1848 char *cp = buf;
1849
1850 while (*cp != '\r' && *cp != '\n' && *cp != '\0')
1851 cp++;
1852 *cp = '\0';
1853
1854 vty_out (vty, "%s%s", buf, VTY_NEWLINE);
1855 }
1856
1857 fclose (confp);
1858
1859 return CMD_SUCCESS;
1860 }
1861
1862 int
1863 cmd_hostname_set (const char *hostname)
1864 {
1865 XFREE (MTYPE_HOST, host.name);
1866 host.name = hostname ? XSTRDUP (MTYPE_HOST, hostname) : NULL;
1867 return CMD_SUCCESS;
1868 }
1869
1870 /* Hostname configuration */
1871 DEFUN (config_hostname,
1872 hostname_cmd,
1873 "hostname WORD",
1874 "Set system's network name\n"
1875 "This system's network name\n")
1876 {
1877 struct cmd_token *word = argv[1];
1878
1879 if (!isalpha((int) word->arg[0]))
1880 {
1881 vty_out (vty, "Please specify string starting with alphabet%s", VTY_NEWLINE);
1882 return CMD_WARNING;
1883 }
1884
1885 return cmd_hostname_set (word->arg);
1886 }
1887
1888 DEFUN (config_no_hostname,
1889 no_hostname_cmd,
1890 "no hostname [HOSTNAME]",
1891 NO_STR
1892 "Reset system's network name\n"
1893 "Host name of this router\n")
1894 {
1895 return cmd_hostname_set (NULL);
1896 }
1897
1898 /* VTY interface password set. */
1899 DEFUN (config_password,
1900 password_cmd,
1901 "password [(8-8)] WORD",
1902 "Assign the terminal connection password\n"
1903 "Specifies a HIDDEN password will follow\n"
1904 "The password string\n")
1905 {
1906 int idx_8 = 1;
1907 int idx_word = 2;
1908 if (argc == 3) // '8' was specified
1909 {
1910 if (host.password)
1911 XFREE (MTYPE_HOST, host.password);
1912 host.password = NULL;
1913 if (host.password_encrypt)
1914 XFREE (MTYPE_HOST, host.password_encrypt);
1915 host.password_encrypt = XSTRDUP (MTYPE_HOST, argv[idx_word]->arg);
1916 return CMD_SUCCESS;
1917 }
1918
1919 if (!isalnum (argv[idx_8]->arg[0]))
1920 {
1921 vty_out (vty,
1922 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
1923 return CMD_WARNING;
1924 }
1925
1926 if (host.password)
1927 XFREE (MTYPE_HOST, host.password);
1928 host.password = NULL;
1929
1930 if (host.encrypt)
1931 {
1932 if (host.password_encrypt)
1933 XFREE (MTYPE_HOST, host.password_encrypt);
1934 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[idx_8]->arg));
1935 }
1936 else
1937 host.password = XSTRDUP (MTYPE_HOST, argv[idx_8]->arg);
1938
1939 return CMD_SUCCESS;
1940 }
1941
1942 /* VTY enable password set. */
1943 DEFUN (config_enable_password,
1944 enable_password_cmd,
1945 "enable password [(8-8)] WORD",
1946 "Modify enable password parameters\n"
1947 "Assign the privileged level password\n"
1948 "Specifies a HIDDEN password will follow\n"
1949 "The HIDDEN 'enable' password string\n")
1950 {
1951 int idx_8 = 2;
1952 int idx_word = 3;
1953
1954 /* Crypt type is specified. */
1955 if (argc == 4)
1956 {
1957 if (argv[idx_8]->arg[0] == '8')
1958 {
1959 if (host.enable)
1960 XFREE (MTYPE_HOST, host.enable);
1961 host.enable = NULL;
1962
1963 if (host.enable_encrypt)
1964 XFREE (MTYPE_HOST, host.enable_encrypt);
1965 host.enable_encrypt = XSTRDUP (MTYPE_HOST, argv[idx_word]->arg);
1966
1967 return CMD_SUCCESS;
1968 }
1969 else
1970 {
1971 vty_out (vty, "Unknown encryption type.%s", VTY_NEWLINE);
1972 return CMD_WARNING;
1973 }
1974 }
1975
1976 if (!isalnum (argv[idx_8]->arg[0]))
1977 {
1978 vty_out (vty,
1979 "Please specify string starting with alphanumeric%s", VTY_NEWLINE);
1980 return CMD_WARNING;
1981 }
1982
1983 if (host.enable)
1984 XFREE (MTYPE_HOST, host.enable);
1985 host.enable = NULL;
1986
1987 /* Plain password input. */
1988 if (host.encrypt)
1989 {
1990 if (host.enable_encrypt)
1991 XFREE (MTYPE_HOST, host.enable_encrypt);
1992 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (argv[idx_8]->arg));
1993 }
1994 else
1995 host.enable = XSTRDUP (MTYPE_HOST, argv[idx_8]->arg);
1996
1997 return CMD_SUCCESS;
1998 }
1999
2000 /* VTY enable password delete. */
2001 DEFUN (no_config_enable_password,
2002 no_enable_password_cmd,
2003 "no enable password",
2004 NO_STR
2005 "Modify enable password parameters\n"
2006 "Assign the privileged level password\n")
2007 {
2008 if (host.enable)
2009 XFREE (MTYPE_HOST, host.enable);
2010 host.enable = NULL;
2011
2012 if (host.enable_encrypt)
2013 XFREE (MTYPE_HOST, host.enable_encrypt);
2014 host.enable_encrypt = NULL;
2015
2016 return CMD_SUCCESS;
2017 }
2018
2019 DEFUN (service_password_encrypt,
2020 service_password_encrypt_cmd,
2021 "service password-encryption",
2022 "Set up miscellaneous service\n"
2023 "Enable encrypted passwords\n")
2024 {
2025 if (host.encrypt)
2026 return CMD_SUCCESS;
2027
2028 host.encrypt = 1;
2029
2030 if (host.password)
2031 {
2032 if (host.password_encrypt)
2033 XFREE (MTYPE_HOST, host.password_encrypt);
2034 host.password_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.password));
2035 }
2036 if (host.enable)
2037 {
2038 if (host.enable_encrypt)
2039 XFREE (MTYPE_HOST, host.enable_encrypt);
2040 host.enable_encrypt = XSTRDUP (MTYPE_HOST, zencrypt (host.enable));
2041 }
2042
2043 return CMD_SUCCESS;
2044 }
2045
2046 DEFUN (no_service_password_encrypt,
2047 no_service_password_encrypt_cmd,
2048 "no service password-encryption",
2049 NO_STR
2050 "Set up miscellaneous service\n"
2051 "Enable encrypted passwords\n")
2052 {
2053 if (! host.encrypt)
2054 return CMD_SUCCESS;
2055
2056 host.encrypt = 0;
2057
2058 if (host.password_encrypt)
2059 XFREE (MTYPE_HOST, host.password_encrypt);
2060 host.password_encrypt = NULL;
2061
2062 if (host.enable_encrypt)
2063 XFREE (MTYPE_HOST, host.enable_encrypt);
2064 host.enable_encrypt = NULL;
2065
2066 return CMD_SUCCESS;
2067 }
2068
2069 DEFUN (config_terminal_length,
2070 config_terminal_length_cmd,
2071 "terminal length (0-512)",
2072 "Set terminal line parameters\n"
2073 "Set number of lines on a screen\n"
2074 "Number of lines on screen (0 for no pausing)\n")
2075 {
2076 int idx_number = 2;
2077 int lines;
2078 char *endptr = NULL;
2079
2080 lines = strtol (argv[idx_number]->arg, &endptr, 10);
2081 if (lines < 0 || lines > 512 || *endptr != '\0')
2082 {
2083 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
2084 return CMD_WARNING;
2085 }
2086 vty->lines = lines;
2087
2088 return CMD_SUCCESS;
2089 }
2090
2091 DEFUN (config_terminal_no_length,
2092 config_terminal_no_length_cmd,
2093 "terminal no length",
2094 "Set terminal line parameters\n"
2095 NO_STR
2096 "Set number of lines on a screen\n")
2097 {
2098 vty->lines = -1;
2099 return CMD_SUCCESS;
2100 }
2101
2102 DEFUN (service_terminal_length,
2103 service_terminal_length_cmd,
2104 "service terminal-length (0-512)",
2105 "Set up miscellaneous service\n"
2106 "System wide terminal length configuration\n"
2107 "Number of lines of VTY (0 means no line control)\n")
2108 {
2109 int idx_number = 2;
2110 int lines;
2111 char *endptr = NULL;
2112
2113 lines = strtol (argv[idx_number]->arg, &endptr, 10);
2114 if (lines < 0 || lines > 512 || *endptr != '\0')
2115 {
2116 vty_out (vty, "length is malformed%s", VTY_NEWLINE);
2117 return CMD_WARNING;
2118 }
2119 host.lines = lines;
2120
2121 return CMD_SUCCESS;
2122 }
2123
2124 DEFUN (no_service_terminal_length,
2125 no_service_terminal_length_cmd,
2126 "no service terminal-length [(0-512)]",
2127 NO_STR
2128 "Set up miscellaneous service\n"
2129 "System wide terminal length configuration\n"
2130 "Number of lines of VTY (0 means no line control)\n")
2131 {
2132 host.lines = -1;
2133 return CMD_SUCCESS;
2134 }
2135
2136 DEFUN_HIDDEN (do_echo,
2137 echo_cmd,
2138 "echo MESSAGE...",
2139 "Echo a message back to the vty\n"
2140 "The message to echo\n")
2141 {
2142 char *message;
2143
2144 vty_out (vty, "%s%s", ((message = argv_concat (argv, argc, 1)) ? message : ""),
2145 VTY_NEWLINE);
2146 if (message)
2147 XFREE(MTYPE_TMP, message);
2148 return CMD_SUCCESS;
2149 }
2150
2151 DEFUN (config_logmsg,
2152 config_logmsg_cmd,
2153 "logmsg <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging> MESSAGE...",
2154 "Send a message to enabled logging destinations\n"
2155 LOG_LEVEL_DESC
2156 "The message to send\n")
2157 {
2158 int idx_log_level = 1;
2159 int idx_message = 2;
2160 int level;
2161 char *message;
2162
2163 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2164 return CMD_ERR_NO_MATCH;
2165
2166 zlog(level, "%s", ((message = argv_concat(argv, argc, idx_message)) ? message : ""));
2167 if (message)
2168 XFREE(MTYPE_TMP, message);
2169
2170 return CMD_SUCCESS;
2171 }
2172
2173 DEFUN (show_logging,
2174 show_logging_cmd,
2175 "show logging",
2176 SHOW_STR
2177 "Show current logging configuration\n")
2178 {
2179 struct zlog *zl = zlog_default;
2180
2181 vty_out (vty, "Syslog logging: ");
2182 if (zl->maxlvl[ZLOG_DEST_SYSLOG] == ZLOG_DISABLED)
2183 vty_out (vty, "disabled");
2184 else
2185 vty_out (vty, "level %s, facility %s, ident %s",
2186 zlog_priority[zl->maxlvl[ZLOG_DEST_SYSLOG]],
2187 facility_name(zl->facility), zl->ident);
2188 vty_out (vty, "%s", VTY_NEWLINE);
2189
2190 vty_out (vty, "Stdout logging: ");
2191 if (zl->maxlvl[ZLOG_DEST_STDOUT] == ZLOG_DISABLED)
2192 vty_out (vty, "disabled");
2193 else
2194 vty_out (vty, "level %s",
2195 zlog_priority[zl->maxlvl[ZLOG_DEST_STDOUT]]);
2196 vty_out (vty, "%s", VTY_NEWLINE);
2197
2198 vty_out (vty, "Monitor logging: ");
2199 if (zl->maxlvl[ZLOG_DEST_MONITOR] == ZLOG_DISABLED)
2200 vty_out (vty, "disabled");
2201 else
2202 vty_out (vty, "level %s",
2203 zlog_priority[zl->maxlvl[ZLOG_DEST_MONITOR]]);
2204 vty_out (vty, "%s", VTY_NEWLINE);
2205
2206 vty_out (vty, "File logging: ");
2207 if ((zl->maxlvl[ZLOG_DEST_FILE] == ZLOG_DISABLED) ||
2208 !zl->fp)
2209 vty_out (vty, "disabled");
2210 else
2211 vty_out (vty, "level %s, filename %s",
2212 zlog_priority[zl->maxlvl[ZLOG_DEST_FILE]],
2213 zl->filename);
2214 vty_out (vty, "%s", VTY_NEWLINE);
2215
2216 vty_out (vty, "Protocol name: %s%s",
2217 zl->protoname, VTY_NEWLINE);
2218 vty_out (vty, "Record priority: %s%s",
2219 (zl->record_priority ? "enabled" : "disabled"), VTY_NEWLINE);
2220 vty_out (vty, "Timestamp precision: %d%s",
2221 zl->timestamp_precision, VTY_NEWLINE);
2222
2223 return CMD_SUCCESS;
2224 }
2225
2226 DEFUN (config_log_stdout,
2227 config_log_stdout_cmd,
2228 "log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2229 "Logging control\n"
2230 "Set stdout logging level\n"
2231 LOG_LEVEL_DESC)
2232 {
2233 int idx_log_level = 2;
2234
2235 if (argc == idx_log_level)
2236 {
2237 zlog_set_level (ZLOG_DEST_STDOUT, zlog_default->default_lvl);
2238 return CMD_SUCCESS;
2239 }
2240 int level;
2241
2242 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2243 return CMD_ERR_NO_MATCH;
2244 zlog_set_level (ZLOG_DEST_STDOUT, level);
2245 return CMD_SUCCESS;
2246 }
2247
2248 DEFUN (no_config_log_stdout,
2249 no_config_log_stdout_cmd,
2250 "no log stdout [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2251 NO_STR
2252 "Logging control\n"
2253 "Cancel logging to stdout\n"
2254 LOG_LEVEL_DESC)
2255 {
2256 zlog_set_level (ZLOG_DEST_STDOUT, ZLOG_DISABLED);
2257 return CMD_SUCCESS;
2258 }
2259
2260 DEFUN (config_log_monitor,
2261 config_log_monitor_cmd,
2262 "log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2263 "Logging control\n"
2264 "Set terminal line (monitor) logging level\n"
2265 LOG_LEVEL_DESC)
2266 {
2267 int idx_log_level = 2;
2268
2269 if (argc == idx_log_level)
2270 {
2271 zlog_set_level (ZLOG_DEST_MONITOR, zlog_default->default_lvl);
2272 return CMD_SUCCESS;
2273 }
2274 int level;
2275
2276 if ((level = level_match(argv[idx_log_level]->arg)) == ZLOG_DISABLED)
2277 return CMD_ERR_NO_MATCH;
2278 zlog_set_level (ZLOG_DEST_MONITOR, level);
2279 return CMD_SUCCESS;
2280 }
2281
2282 DEFUN (no_config_log_monitor,
2283 no_config_log_monitor_cmd,
2284 "no log monitor [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2285 NO_STR
2286 "Logging control\n"
2287 "Disable terminal line (monitor) logging\n"
2288 LOG_LEVEL_DESC)
2289 {
2290 zlog_set_level (ZLOG_DEST_MONITOR, ZLOG_DISABLED);
2291 return CMD_SUCCESS;
2292 }
2293
2294 static int
2295 set_log_file(struct vty *vty, const char *fname, int loglevel)
2296 {
2297 int ret;
2298 char *p = NULL;
2299 const char *fullpath;
2300
2301 /* Path detection. */
2302 if (! IS_DIRECTORY_SEP (*fname))
2303 {
2304 char cwd[MAXPATHLEN+1];
2305 cwd[MAXPATHLEN] = '\0';
2306
2307 if (getcwd (cwd, MAXPATHLEN) == NULL)
2308 {
2309 zlog_err ("config_log_file: Unable to alloc mem!");
2310 return CMD_WARNING;
2311 }
2312
2313 if ( (p = XMALLOC (MTYPE_TMP, strlen (cwd) + strlen (fname) + 2))
2314 == NULL)
2315 {
2316 zlog_err ("config_log_file: Unable to alloc mem!");
2317 return CMD_WARNING;
2318 }
2319 sprintf (p, "%s/%s", cwd, fname);
2320 fullpath = p;
2321 }
2322 else
2323 fullpath = fname;
2324
2325 ret = zlog_set_file (fullpath, loglevel);
2326
2327 if (p)
2328 XFREE (MTYPE_TMP, p);
2329
2330 if (!ret)
2331 {
2332 vty_out (vty, "can't open logfile %s\n", fname);
2333 return CMD_WARNING;
2334 }
2335
2336 if (host.logfile)
2337 XFREE (MTYPE_HOST, host.logfile);
2338
2339 host.logfile = XSTRDUP (MTYPE_HOST, fname);
2340
2341 #if defined(HAVE_CUMULUS)
2342 if (zlog_default->maxlvl[ZLOG_DEST_SYSLOG] != ZLOG_DISABLED)
2343 zlog_default->maxlvl[ZLOG_DEST_SYSLOG] = ZLOG_DISABLED;
2344 #endif
2345 return CMD_SUCCESS;
2346 }
2347
2348 DEFUN (config_log_file,
2349 config_log_file_cmd,
2350 "log file FILENAME [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2351 "Logging control\n"
2352 "Logging to file\n"
2353 "Logging filename\n"
2354 LOG_LEVEL_DESC)
2355 {
2356 int idx_filename = 2;
2357 int idx_log_levels = 3;
2358 if (argc == 4)
2359 {
2360 int level;
2361 if ((level = level_match(argv[idx_log_levels]->arg)) == ZLOG_DISABLED)
2362 return CMD_ERR_NO_MATCH;
2363 return set_log_file(vty, argv[idx_filename]->arg, level);
2364 }
2365 else
2366 return set_log_file(vty, argv[idx_filename]->arg, zlog_default->default_lvl);
2367 }
2368
2369 DEFUN (no_config_log_file,
2370 no_config_log_file_cmd,
2371 "no log file [FILENAME [LEVEL]]",
2372 NO_STR
2373 "Logging control\n"
2374 "Cancel logging to file\n"
2375 "Logging file name\n"
2376 "Logging file name\n"
2377 "Logging level\n")
2378 {
2379 zlog_reset_file ();
2380
2381 if (host.logfile)
2382 XFREE (MTYPE_HOST, host.logfile);
2383
2384 host.logfile = NULL;
2385
2386 return CMD_SUCCESS;
2387 }
2388
2389 DEFUN (config_log_syslog,
2390 config_log_syslog_cmd,
2391 "log syslog [<emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>]",
2392 "Logging control\n"
2393 "Set syslog logging level\n"
2394 LOG_LEVEL_DESC)
2395 {
2396 int idx_log_levels = 2;
2397 if (argc == 3)
2398 {
2399 int level;
2400 if ((level = level_match (argv[idx_log_levels]->arg)) == ZLOG_DISABLED)
2401 return CMD_ERR_NO_MATCH;
2402 zlog_set_level (ZLOG_DEST_SYSLOG, level);
2403 return CMD_SUCCESS;
2404 }
2405 else
2406 {
2407 zlog_set_level (ZLOG_DEST_SYSLOG, zlog_default->default_lvl);
2408 return CMD_SUCCESS;
2409 }
2410 }
2411
2412 DEFUN (no_config_log_syslog,
2413 no_config_log_syslog_cmd,
2414 "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>]",
2415 NO_STR
2416 "Logging control\n"
2417 "Cancel logging to syslog\n"
2418 LOG_FACILITY_DESC
2419 LOG_LEVEL_DESC)
2420 {
2421 zlog_set_level (ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
2422 return CMD_SUCCESS;
2423 }
2424
2425 DEFUN (config_log_facility,
2426 config_log_facility_cmd,
2427 "log facility <kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>",
2428 "Logging control\n"
2429 "Facility parameter for syslog messages\n"
2430 LOG_FACILITY_DESC)
2431 {
2432 int idx_target = 2;
2433 int facility = facility_match(argv[idx_target]->arg);
2434
2435 zlog_default->facility = facility;
2436 return CMD_SUCCESS;
2437 }
2438
2439 DEFUN (no_config_log_facility,
2440 no_config_log_facility_cmd,
2441 "no log facility [<kern|user|mail|daemon|auth|syslog|lpr|news|uucp|cron|local0|local1|local2|local3|local4|local5|local6|local7>]",
2442 NO_STR
2443 "Logging control\n"
2444 "Reset syslog facility to default (daemon)\n"
2445 LOG_FACILITY_DESC)
2446 {
2447 zlog_default->facility = LOG_DAEMON;
2448 return CMD_SUCCESS;
2449 }
2450
2451 DEFUN_DEPRECATED (config_log_trap,
2452 config_log_trap_cmd,
2453 "log trap <emergencies|alerts|critical|errors|warnings|notifications|informational|debugging>",
2454 "Logging control\n"
2455 "(Deprecated) Set logging level and default for all destinations\n"
2456 LOG_LEVEL_DESC)
2457 {
2458 int new_level ;
2459 int i;
2460
2461 if ((new_level = level_match(argv[2]->arg)) == ZLOG_DISABLED)
2462 return CMD_ERR_NO_MATCH;
2463
2464 zlog_default->default_lvl = new_level;
2465 for (i = 0; i < ZLOG_NUM_DESTS; i++)
2466 if (zlog_default->maxlvl[i] != ZLOG_DISABLED)
2467 zlog_default->maxlvl[i] = new_level;
2468 return CMD_SUCCESS;
2469 }
2470
2471 DEFUN_DEPRECATED (no_config_log_trap,
2472 no_config_log_trap_cmd,
2473 "no log trap [emergencies|alerts|critical|errors|warnings|notifications|informational|debugging]",
2474 NO_STR
2475 "Logging control\n"
2476 "Permit all logging information\n"
2477 LOG_LEVEL_DESC)
2478 {
2479 zlog_default->default_lvl = LOG_DEBUG;
2480 return CMD_SUCCESS;
2481 }
2482
2483 DEFUN (config_log_record_priority,
2484 config_log_record_priority_cmd,
2485 "log record-priority",
2486 "Logging control\n"
2487 "Log the priority of the message within the message\n")
2488 {
2489 zlog_default->record_priority = 1 ;
2490 return CMD_SUCCESS;
2491 }
2492
2493 DEFUN (no_config_log_record_priority,
2494 no_config_log_record_priority_cmd,
2495 "no log record-priority",
2496 NO_STR
2497 "Logging control\n"
2498 "Do not log the priority of the message within the message\n")
2499 {
2500 zlog_default->record_priority = 0 ;
2501 return CMD_SUCCESS;
2502 }
2503
2504 DEFUN (config_log_timestamp_precision,
2505 config_log_timestamp_precision_cmd,
2506 "log timestamp precision (0-6)",
2507 "Logging control\n"
2508 "Timestamp configuration\n"
2509 "Set the timestamp precision\n"
2510 "Number of subsecond digits\n")
2511 {
2512 int idx_number = 3;
2513 VTY_GET_INTEGER_RANGE("Timestamp Precision",
2514 zlog_default->timestamp_precision, argv[idx_number]->arg, 0, 6);
2515 return CMD_SUCCESS;
2516 }
2517
2518 DEFUN (no_config_log_timestamp_precision,
2519 no_config_log_timestamp_precision_cmd,
2520 "no log timestamp precision",
2521 NO_STR
2522 "Logging control\n"
2523 "Timestamp configuration\n"
2524 "Reset the timestamp precision to the default value of 0\n")
2525 {
2526 zlog_default->timestamp_precision = 0 ;
2527 return CMD_SUCCESS;
2528 }
2529
2530 int
2531 cmd_banner_motd_file (const char *file)
2532 {
2533 int success = CMD_SUCCESS;
2534 char p[PATH_MAX];
2535 char *rpath;
2536 char *in;
2537
2538 rpath = realpath (file, p);
2539 if (!rpath)
2540 return CMD_ERR_NO_FILE;
2541 in = strstr (rpath, SYSCONFDIR);
2542 if (in == rpath)
2543 {
2544 if (host.motdfile)
2545 XFREE (MTYPE_HOST, host.motdfile);
2546 host.motdfile = XSTRDUP (MTYPE_HOST, file);
2547 }
2548 else
2549 success = CMD_WARNING;
2550
2551 return success;
2552 }
2553
2554 DEFUN (banner_motd_file,
2555 banner_motd_file_cmd,
2556 "banner motd file FILE",
2557 "Set banner\n"
2558 "Banner for motd\n"
2559 "Banner from a file\n"
2560 "Filename\n")
2561 {
2562 int idx_file = 3;
2563 const char *filename = argv[idx_file]->arg;
2564 int cmd = cmd_banner_motd_file (filename);
2565
2566 if (cmd == CMD_ERR_NO_FILE)
2567 vty_out (vty, "%s does not exist", filename);
2568 else if (cmd == CMD_WARNING)
2569 vty_out (vty, "%s must be in %s", filename, SYSCONFDIR);
2570
2571 return cmd;
2572 }
2573
2574 DEFUN (banner_motd_default,
2575 banner_motd_default_cmd,
2576 "banner motd default",
2577 "Set banner string\n"
2578 "Strings for motd\n"
2579 "Default string\n")
2580 {
2581 host.motd = default_motd;
2582 return CMD_SUCCESS;
2583 }
2584
2585 DEFUN (no_banner_motd,
2586 no_banner_motd_cmd,
2587 "no banner motd",
2588 NO_STR
2589 "Set banner string\n"
2590 "Strings for motd\n")
2591 {
2592 host.motd = NULL;
2593 if (host.motdfile)
2594 XFREE (MTYPE_HOST, host.motdfile);
2595 host.motdfile = NULL;
2596 return CMD_SUCCESS;
2597 }
2598
2599 /* Set config filename. Called from vty.c */
2600 void
2601 host_config_set (const char *filename)
2602 {
2603 if (host.config)
2604 XFREE (MTYPE_HOST, host.config);
2605 host.config = XSTRDUP (MTYPE_HOST, filename);
2606 }
2607
2608 const char *
2609 host_config_get (void)
2610 {
2611 return host.config;
2612 }
2613
2614 void
2615 install_default (enum node_type node)
2616 {
2617 install_element (node, &config_exit_cmd);
2618 install_element (node, &config_quit_cmd);
2619 install_element (node, &config_end_cmd);
2620 install_element (node, &config_help_cmd);
2621 install_element (node, &config_list_cmd);
2622
2623 install_element (node, &config_write_cmd);
2624 install_element (node, &show_running_config_cmd);
2625 }
2626
2627 /* Initialize command interface. Install basic nodes and commands.
2628 *
2629 * terminal = 0 -- vtysh / no logging, no config control
2630 * terminal = 1 -- normal daemon
2631 * terminal = -1 -- watchfrr / no logging, but minimal config control */
2632 void
2633 cmd_init (int terminal)
2634 {
2635 qobj_init ();
2636
2637 /* Allocate initial top vector of commands. */
2638 cmdvec = vector_init (VECTOR_MIN_SIZE);
2639
2640 /* Default host value settings. */
2641 host.name = NULL;
2642 host.password = NULL;
2643 host.enable = NULL;
2644 host.logfile = NULL;
2645 host.config = NULL;
2646 host.noconfig = (terminal < 0);
2647 host.lines = -1;
2648 host.motd = default_motd;
2649 host.motdfile = NULL;
2650
2651 /* Install top nodes. */
2652 install_node (&view_node, NULL);
2653 install_node (&enable_node, NULL);
2654 install_node (&auth_node, NULL);
2655 install_node (&auth_enable_node, NULL);
2656 install_node (&config_node, config_write_host);
2657
2658 /* Each node's basic commands. */
2659 install_element (VIEW_NODE, &show_version_cmd);
2660 if (terminal)
2661 {
2662 install_element (VIEW_NODE, &config_list_cmd);
2663 install_element (VIEW_NODE, &config_exit_cmd);
2664 install_element (VIEW_NODE, &config_quit_cmd);
2665 install_element (VIEW_NODE, &config_help_cmd);
2666 install_element (VIEW_NODE, &config_enable_cmd);
2667 install_element (VIEW_NODE, &config_terminal_length_cmd);
2668 install_element (VIEW_NODE, &config_terminal_no_length_cmd);
2669 install_element (VIEW_NODE, &show_logging_cmd);
2670 install_element (VIEW_NODE, &show_commandtree_cmd);
2671 install_element (VIEW_NODE, &echo_cmd);
2672 }
2673
2674 if (terminal)
2675 {
2676 install_element (ENABLE_NODE, &config_end_cmd);
2677 install_element (ENABLE_NODE, &config_disable_cmd);
2678 install_element (ENABLE_NODE, &config_terminal_cmd);
2679 install_element (ENABLE_NODE, &copy_runningconf_startupconf_cmd);
2680 install_element (ENABLE_NODE, &config_write_cmd);
2681 install_element (ENABLE_NODE, &show_running_config_cmd);
2682 }
2683 install_element (ENABLE_NODE, &show_startup_config_cmd);
2684
2685 if (terminal)
2686 {
2687 install_element (ENABLE_NODE, &config_logmsg_cmd);
2688 install_default (CONFIG_NODE);
2689
2690 thread_cmd_init ();
2691 workqueue_cmd_init ();
2692 }
2693
2694 install_element (CONFIG_NODE, &hostname_cmd);
2695 install_element (CONFIG_NODE, &no_hostname_cmd);
2696
2697 if (terminal > 0)
2698 {
2699 install_element (CONFIG_NODE, &password_cmd);
2700 install_element (CONFIG_NODE, &enable_password_cmd);
2701 install_element (CONFIG_NODE, &no_enable_password_cmd);
2702
2703 install_element (CONFIG_NODE, &config_log_stdout_cmd);
2704 install_element (CONFIG_NODE, &no_config_log_stdout_cmd);
2705 install_element (CONFIG_NODE, &config_log_monitor_cmd);
2706 install_element (CONFIG_NODE, &no_config_log_monitor_cmd);
2707 install_element (CONFIG_NODE, &config_log_file_cmd);
2708 install_element (CONFIG_NODE, &no_config_log_file_cmd);
2709 install_element (CONFIG_NODE, &config_log_syslog_cmd);
2710 install_element (CONFIG_NODE, &no_config_log_syslog_cmd);
2711 install_element (CONFIG_NODE, &config_log_facility_cmd);
2712 install_element (CONFIG_NODE, &no_config_log_facility_cmd);
2713 install_element (CONFIG_NODE, &config_log_trap_cmd);
2714 install_element (CONFIG_NODE, &no_config_log_trap_cmd);
2715 install_element (CONFIG_NODE, &config_log_record_priority_cmd);
2716 install_element (CONFIG_NODE, &no_config_log_record_priority_cmd);
2717 install_element (CONFIG_NODE, &config_log_timestamp_precision_cmd);
2718 install_element (CONFIG_NODE, &no_config_log_timestamp_precision_cmd);
2719 install_element (CONFIG_NODE, &service_password_encrypt_cmd);
2720 install_element (CONFIG_NODE, &no_service_password_encrypt_cmd);
2721 install_element (CONFIG_NODE, &banner_motd_default_cmd);
2722 install_element (CONFIG_NODE, &banner_motd_file_cmd);
2723 install_element (CONFIG_NODE, &no_banner_motd_cmd);
2724 install_element (CONFIG_NODE, &service_terminal_length_cmd);
2725 install_element (CONFIG_NODE, &no_service_terminal_length_cmd);
2726
2727 vrf_install_commands ();
2728 }
2729
2730 #ifdef DEV_BUILD
2731 grammar_sandbox_init();
2732 #endif
2733 }
2734
2735 struct cmd_token *
2736 new_cmd_token (enum cmd_token_type type, u_char attr,
2737 const char *text, const char *desc)
2738 {
2739 struct cmd_token *token = XCALLOC (MTYPE_CMD_TOKENS, sizeof (struct cmd_token));
2740 token->type = type;
2741 token->attr = attr;
2742 token->text = text ? XSTRDUP (MTYPE_CMD_TEXT, text) : NULL;
2743 token->desc = desc ? XSTRDUP (MTYPE_CMD_DESC, desc) : NULL;
2744 token->refcnt = 1;
2745 token->arg = NULL;
2746 token->allowrepeat = false;
2747
2748 return token;
2749 }
2750
2751 void
2752 del_cmd_token (struct cmd_token *token)
2753 {
2754 if (!token) return;
2755
2756 if (token->text)
2757 XFREE (MTYPE_CMD_TEXT, token->text);
2758 if (token->desc)
2759 XFREE (MTYPE_CMD_DESC, token->desc);
2760 if (token->arg)
2761 XFREE (MTYPE_CMD_ARG, token->arg);
2762
2763 XFREE (MTYPE_CMD_TOKENS, token);
2764 }
2765
2766 struct cmd_token *
2767 copy_cmd_token (struct cmd_token *token)
2768 {
2769 struct cmd_token *copy = new_cmd_token (token->type, token->attr, NULL, NULL);
2770 copy->max = token->max;
2771 copy->min = token->min;
2772 copy->text = token->text ? XSTRDUP (MTYPE_CMD_TEXT, token->text) : NULL;
2773 copy->desc = token->desc ? XSTRDUP (MTYPE_CMD_DESC, token->desc) : NULL;
2774 copy->arg = token->arg ? XSTRDUP (MTYPE_CMD_ARG, token->arg) : NULL;
2775
2776 return copy;
2777 }
2778
2779 void
2780 cmd_terminate ()
2781 {
2782 struct cmd_node *cmd_node;
2783
2784 if (cmdvec)
2785 {
2786 for (unsigned int i = 0; i < vector_active (cmdvec); i++)
2787 if ((cmd_node = vector_slot (cmdvec, i)) != NULL)
2788 {
2789 // deleting the graph delets the cmd_element as well
2790 graph_delete_graph (cmd_node->cmdgraph);
2791 vector_free (cmd_node->cmd_vector);
2792 hash_clean (cmd_node->cmd_hash, NULL);
2793 hash_free (cmd_node->cmd_hash);
2794 cmd_node->cmd_hash = NULL;
2795 }
2796
2797 vector_free (cmdvec);
2798 cmdvec = NULL;
2799 }
2800
2801 if (host.name)
2802 XFREE (MTYPE_HOST, host.name);
2803 if (host.password)
2804 XFREE (MTYPE_HOST, host.password);
2805 if (host.password_encrypt)
2806 XFREE (MTYPE_HOST, host.password_encrypt);
2807 if (host.enable)
2808 XFREE (MTYPE_HOST, host.enable);
2809 if (host.enable_encrypt)
2810 XFREE (MTYPE_HOST, host.enable_encrypt);
2811 if (host.logfile)
2812 XFREE (MTYPE_HOST, host.logfile);
2813 if (host.motdfile)
2814 XFREE (MTYPE_HOST, host.motdfile);
2815 if (host.config)
2816 XFREE (MTYPE_HOST, host.config);
2817
2818 qobj_finish ();
2819 }