]> git.proxmox.com Git - mirror_frr.git/blob - vtysh/vtysh_config.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / vtysh / vtysh_config.c
1 /* Configuration generator.
2 * Copyright (C) 2000 Kunihiro Ishiguro
3 *
4 * This file is part of GNU Zebra.
5 *
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
9 * later version.
10 *
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include "command.h"
24 #include "linklist.h"
25 #include "memory.h"
26 #include "typesafe.h"
27
28 #include "vtysh/vtysh.h"
29 #include "vtysh/vtysh_user.h"
30
31 DEFINE_MGROUP(MVTYSH, "vtysh");
32 DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG, "Vtysh configuration");
33 DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG_LINE, "Vtysh configuration line");
34
35 vector configvec;
36
37 PREDECL_LIST(config_master);
38 PREDECL_HASH(config_master_hash);
39
40 struct config {
41 /* Configuration node name. */
42 char *name;
43
44 /* Configuration string line. */
45 struct list *line;
46
47 /* Configuration can be nested. */
48 struct config *parent;
49 vector nested;
50
51 /* Exit command. */
52 char *exit;
53
54 /* Index of this config. */
55 uint32_t index;
56
57 /* Node entry for the typed Red-black tree */
58 struct config_master_item rbt_item;
59 struct config_master_hash_item hash_item;
60 };
61
62 struct list *config_top;
63
64 static int line_cmp(char *c1, char *c2)
65 {
66 return strcmp(c1, c2);
67 }
68
69 static void line_del(char *line)
70 {
71 XFREE(MTYPE_VTYSH_CONFIG_LINE, line);
72 }
73
74 static struct config *config_new(void)
75 {
76 struct config *config;
77 config = XCALLOC(MTYPE_VTYSH_CONFIG, sizeof(struct config));
78 return config;
79 }
80
81 static void config_del(struct config *config)
82 {
83 vector_free(config->nested);
84 list_delete(&config->line);
85 if (config->exit)
86 XFREE(MTYPE_VTYSH_CONFIG_LINE, config->exit);
87 XFREE(MTYPE_VTYSH_CONFIG_LINE, config->name);
88 XFREE(MTYPE_VTYSH_CONFIG, config);
89 }
90
91 static int config_cmp(const struct config *c1, const struct config *c2)
92 {
93 return strcmp(c1->name, c2->name);
94 }
95
96 static uint32_t config_hash(const struct config *c)
97 {
98 return string_hash_make(c->name);
99 }
100
101 DECLARE_LIST(config_master, struct config, rbt_item);
102 DECLARE_HASH(config_master_hash, struct config, hash_item, config_cmp,
103 config_hash);
104
105 /*
106 * The config_master_head is a list for order of receipt
107 * The hash is for quick lookup under this NODE
108 */
109 struct configuration {
110 struct config_master_head master;
111 struct config_master_hash_head hash_master;
112 };
113
114 static struct config *config_get_vec(vector vec, int index, const char *line)
115 {
116 struct config *config, *config_loop;
117 struct configuration *configuration;
118 struct config lookup;
119
120 config = config_loop = NULL;
121
122 configuration = vector_lookup_ensure(vec, index);
123
124 if (!configuration) {
125 configuration = XMALLOC(MTYPE_VTYSH_CONFIG,
126 sizeof(struct configuration));
127 config_master_init(&configuration->master);
128 config_master_hash_init(&configuration->hash_master);
129 vector_set_index(vec, index, configuration);
130 }
131
132 lookup.name = (char *)line;
133 config = config_master_hash_find(&configuration->hash_master, &lookup);
134
135 if (!config) {
136 config = config_new();
137 config->line = list_new();
138 config->line->del = (void (*)(void *))line_del;
139 config->line->cmp = (int (*)(void *, void *))line_cmp;
140 config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
141 config->exit = NULL;
142 config->index = index;
143 config->nested = vector_init(1);
144 config_master_add_tail(&configuration->master, config);
145 config_master_hash_add(&configuration->hash_master, config);
146 }
147 return config;
148 }
149
150 static struct config *config_get(int index, const char *line)
151 {
152 return config_get_vec(configvec, index, line);
153 }
154
155 static struct config *config_get_nested(struct config *parent, int index,
156 const char *line)
157 {
158 struct config *config;
159
160 config = config_get_vec(parent->nested, index, line);
161 config->parent = parent;
162
163 return config;
164 }
165
166 void config_add_line(struct list *config, const char *line)
167 {
168 listnode_add(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
169 }
170
171 static void config_add_line_uniq(struct list *config, const char *line)
172 {
173 struct listnode *node, *nnode;
174 char *pnt;
175
176 for (ALL_LIST_ELEMENTS(config, node, nnode, pnt)) {
177 if (strcmp(pnt, line) == 0)
178 return;
179 }
180 listnode_add_sort(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
181 }
182
183 /*
184 * Add a line that should only be shown once, and always show at the end of the
185 * config block.
186 *
187 * If the line already exists, it will be moved to the end of the block. If it
188 * does not exist, it will be added at the end of the block.
189 *
190 * Note that this only makes sense when there is just one such line that should
191 * show up at the very end of a config block. Furthermore, if the same block
192 * can show up from multiple daemons, all of them must make sure to print the
193 * line at the end of their config, otherwise the line will show at the end of
194 * the config for the last daemon that printed it.
195 *
196 * Here is a motivating example with the 'exit-vrf' command. Suppose we receive
197 * a config from Zebra like so:
198 *
199 * vrf BLUE
200 * ip route A
201 * ip route B
202 * exit-vrf
203 *
204 * Then suppose we later receive this config from PIM:
205 *
206 * vrf BLUE
207 * ip msdp mesh-group MyGroup member 1.2.3.4
208 * exit-vrf
209 *
210 * Then we will combine them into one config block like so:
211 *
212 * vrf BLUE
213 * ip route A
214 * ip route B
215 * ip msdp mesh-group MyGroup member 1.2.3.4
216 * exit-vrf
217 *
218 * Because PIM also sent us an 'exit-vrf', we noticed that we already had one
219 * under the 'vrf BLUE' config block and so we moved it to the end of the
220 * config block again. If PIM had neglected to send us 'exit-vrf', the result
221 * would be this:
222 *
223 * vrf BLUE
224 * ip route A
225 * ip route B
226 * exit-vrf
227 * ip msdp mesh-group MyGroup member 1.2.3.4
228 *
229 * Therefore, daemons that share config blocks must take care to consistently
230 * print the same block terminators.
231 *
232 * Ideally this would be solved by adding a string to struct config that is
233 * always printed at the end when dumping a config. However, this would only
234 * work when the user is using integrated config. In the non-integrated config
235 * case, daemons are responsible for writing their own config files, and so the
236 * must be able to print these blocks correctly independently of vtysh, which
237 * means they are the ones that need to handle printing the block terminators
238 * and VTYSH needs to be smart enough to combine them properly.
239 *
240 * ---
241 *
242 * config
243 * The config to add the line to
244 *
245 * line
246 * The line to add to the end of the config
247 */
248 static void config_add_line_uniq_end(struct list *config, const char *line)
249 {
250 struct listnode *node;
251 char *pnt;
252
253 for (ALL_LIST_ELEMENTS_RO(config, node, pnt)) {
254 if (strcmp(pnt, line) == 0)
255 break;
256 }
257
258 if (!node)
259 config_add_line(config, line);
260 else
261 listnode_move_to_tail(config, node);
262 }
263
264 static void config_add_line_head(struct list *config, const char *line)
265 {
266 listnode_add_head(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
267 }
268
269 void vtysh_config_parse_line(void *arg, const char *line)
270 {
271 char c;
272 static struct config *config = NULL;
273
274 if (!line)
275 return;
276
277 c = line[0];
278
279 if (c == '\0')
280 return;
281
282 switch (c) {
283 /* Suppress exclamation points ! and commented lines. The !s are
284 * generated
285 * dynamically in vtysh_config_dump() */
286 case '!':
287 case '#':
288 break;
289 case ' ':
290 /* Store line to current configuration. */
291 if (config) {
292 if (config->index == KEYCHAIN_NODE
293 && strncmp(line, " key", strlen(" key")) == 0) {
294 config = config_get_nested(
295 config, KEYCHAIN_KEY_NODE, line);
296 } else if (config->index == KEYCHAIN_KEY_NODE) {
297 if (strncmp(line, " exit", strlen(" exit"))
298 == 0) {
299 config_add_line_uniq_end(config->line,
300 line);
301 config = config->parent;
302 } else {
303 config_add_line_uniq(config->line,
304 line);
305 }
306 } else if (strncmp(line, " link-params",
307 strlen(" link-params"))
308 == 0) {
309 config_add_line(config->line, line);
310 config->index = LINK_PARAMS_NODE;
311 } else if (strncmp(line, " ip multicast boundary",
312 strlen(" ip multicast boundary"))
313 == 0) {
314 config_add_line_uniq_end(config->line, line);
315 } else if (strncmp(line, " ip igmp query-interval",
316 strlen(" ip igmp query-interval"))
317 == 0) {
318 config_add_line_uniq_end(config->line, line);
319 } else if (config->index == LINK_PARAMS_NODE
320 && strncmp(line, " exit-link-params",
321 strlen(" exit"))
322 == 0) {
323 config_add_line(config->line, line);
324 config->index = INTERFACE_NODE;
325 } else if (!strncmp(line, " vrrp", strlen(" vrrp"))
326 || !strncmp(line, " no vrrp",
327 strlen(" no vrrp"))) {
328 config_add_line(config->line, line);
329 } else if (!strncmp(line, " ip mroute",
330 strlen(" ip mroute"))) {
331 config_add_line_uniq_end(config->line, line);
332 } else if (config->index == RMAP_NODE ||
333 config->index == INTERFACE_NODE ||
334 config->index == VTY_NODE)
335 config_add_line_uniq(config->line, line);
336 else if (config->index == NH_GROUP_NODE) {
337 if (strncmp(line, " resilient",
338 strlen(" resilient")) == 0)
339 config_add_line_head(config->line,
340 line);
341 else
342 config_add_line_uniq_end(config->line,
343 line);
344 } else
345 config_add_line(config->line, line);
346 } else
347 config_add_line(config_top, line);
348 break;
349 default:
350 if (strncmp(line, "exit", strlen("exit")) == 0) {
351 if (config) {
352 if (config->exit)
353 XFREE(MTYPE_VTYSH_CONFIG_LINE,
354 config->exit);
355 config->exit =
356 XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
357 }
358 } else if (strncmp(line, "interface", strlen("interface")) == 0)
359 config = config_get(INTERFACE_NODE, line);
360 else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
361 config = config_get(PW_NODE, line);
362 else if (strncmp(line, "vrf", strlen("vrf")) == 0)
363 config = config_get(VRF_NODE, line);
364 else if (strncmp(line, "nexthop-group", strlen("nexthop-group"))
365 == 0)
366 config = config_get(NH_GROUP_NODE, line);
367 else if (strncmp(line, "router-id", strlen("router-id")) == 0)
368 config = config_get(ZEBRA_NODE, line);
369 else if (strncmp(line, "router rip", strlen("router rip")) == 0)
370 config = config_get(RIP_NODE, line);
371 else if (strncmp(line, "router ripng", strlen("router ripng"))
372 == 0)
373 config = config_get(RIPNG_NODE, line);
374 else if (strncmp(line, "router eigrp", strlen("router eigrp"))
375 == 0)
376 config = config_get(EIGRP_NODE, line);
377 else if (strncmp(line, "router babel", strlen("router babel"))
378 == 0)
379 config = config_get(BABEL_NODE, line);
380 else if (strncmp(line, "router ospf", strlen("router ospf"))
381 == 0)
382 config = config_get(OSPF_NODE, line);
383 else if (strncmp(line, "router ospf6", strlen("router ospf6"))
384 == 0)
385 config = config_get(OSPF6_NODE, line);
386 else if (strncmp(line, "mpls ldp", strlen("mpls ldp")) == 0)
387 config = config_get(LDP_NODE, line);
388 else if (strncmp(line, "l2vpn", strlen("l2vpn")) == 0)
389 config = config_get(LDP_L2VPN_NODE, line);
390 else if (strncmp(line, "router bgp", strlen("router bgp")) == 0)
391 config = config_get(BGP_NODE, line);
392 else if (strncmp(line, "router isis", strlen("router isis"))
393 == 0)
394 config = config_get(ISIS_NODE, line);
395 else if (strncmp(line, "router openfabric", strlen("router openfabric"))
396 == 0)
397 config = config_get(OPENFABRIC_NODE, line);
398 else if (strncmp(line, "affinity-map",
399 strlen("affinity-map")) == 0)
400 config = config_get(AFFMAP_NODE, line);
401 else if (strncmp(line, "route-map", strlen("route-map")) == 0)
402 config = config_get(RMAP_NODE, line);
403 else if (strncmp(line, "no route-map", strlen("no route-map"))
404 == 0)
405 config = config_get(RMAP_NODE, line);
406 else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0)
407 config = config_get(PBRMAP_NODE, line);
408 else if (strncmp(line, "access-list", strlen("access-list"))
409 == 0)
410 config = config_get(ACCESS_NODE, line);
411 else if (strncmp(line, "ipv6 access-list",
412 strlen("ipv6 access-list"))
413 == 0)
414 config = config_get(ACCESS_IPV6_NODE, line);
415 else if (strncmp(line, "mac access-list",
416 strlen("mac access-list"))
417 == 0)
418 config = config_get(ACCESS_MAC_NODE, line);
419 else if (strncmp(line, "ip prefix-list",
420 strlen("ip prefix-list"))
421 == 0)
422 config = config_get(PREFIX_NODE, line);
423 else if (strncmp(line, "ipv6 prefix-list",
424 strlen("ipv6 prefix-list"))
425 == 0)
426 config = config_get(PREFIX_IPV6_NODE, line);
427 else if (strncmp(line, "bgp as-path access-list",
428 strlen("bgp as-path access-list"))
429 == 0)
430 config = config_get(AS_LIST_NODE, line);
431 else if (strncmp(line, "bgp community-list",
432 strlen("bgp community-list"))
433 == 0
434 || strncmp(line, "bgp extcommunity-list",
435 strlen("bgp extcommunity-list"))
436 == 0
437 || strncmp(line, "bgp large-community-list",
438 strlen("bgp large-community-list"))
439 == 0)
440 config = config_get(COMMUNITY_LIST_NODE, line);
441 else if (strncmp(line, "bgp community alias",
442 strlen("bgp community alias")) == 0)
443 config = config_get(COMMUNITY_ALIAS_NODE, line);
444 else if (strncmp(line, "ip route", strlen("ip route")) == 0)
445 config = config_get(IP_NODE, line);
446 else if (strncmp(line, "ipv6 route", strlen("ipv6 route")) == 0)
447 config = config_get(IP_NODE, line);
448 else if (strncmp(line, "key", strlen("key")) == 0)
449 config = config_get(KEYCHAIN_NODE, line);
450 else if (strncmp(line, "line", strlen("line")) == 0)
451 config = config_get(VTY_NODE, line);
452 else if ((strncmp(line, "ipv6 forwarding",
453 strlen("ipv6 forwarding"))
454 == 0)
455 || (strncmp(line, "ip forwarding",
456 strlen("ip forwarding"))
457 == 0))
458 config = config_get(FORWARDING_NODE, line);
459 else if (strncmp(line, "debug vrf", strlen("debug vrf")) == 0)
460 config = config_get(VRF_DEBUG_NODE, line);
461 else if (strncmp(line, "debug northbound",
462 strlen("debug northbound"))
463 == 0)
464 config = config_get(NORTHBOUND_DEBUG_NODE, line);
465 else if (strncmp(line, "debug route-map",
466 strlen("debug route-map"))
467 == 0)
468 config = config_get(RMAP_DEBUG_NODE, line);
469 else if (strncmp(line, "debug resolver",
470 strlen("debug resolver")) == 0)
471 config = config_get(RESOLVER_DEBUG_NODE, line);
472 else if (strncmp(line, "debug", strlen("debug")) == 0)
473 config = config_get(DEBUG_NODE, line);
474 else if (strncmp(line, "password", strlen("password")) == 0
475 || strncmp(line, "enable password",
476 strlen("enable password"))
477 == 0)
478 config = config_get(AAA_NODE, line);
479 else if (strncmp(line, "ip protocol", strlen("ip protocol"))
480 == 0)
481 config = config_get(PROTOCOL_NODE, line);
482 else if (strncmp(line, "ipv6 protocol", strlen("ipv6 protocol"))
483 == 0)
484 config = config_get(PROTOCOL_NODE, line);
485 else if (strncmp(line, "ip nht", strlen("ip nht")) == 0)
486 config = config_get(PROTOCOL_NODE, line);
487 else if (strncmp(line, "ipv6 nht", strlen("ipv6 nht")) == 0)
488 config = config_get(PROTOCOL_NODE, line);
489 else if (strncmp(line, "mpls", strlen("mpls")) == 0)
490 config = config_get(MPLS_NODE, line);
491 else if (strncmp(line, "segment-routing",
492 strlen("segment-routing"))
493 == 0)
494 config = config_get(SEGMENT_ROUTING_NODE, line);
495 else if (strncmp(line, "bfd", strlen("bfd")) == 0)
496 config = config_get(BFD_NODE, line);
497 else if (strncmp(line, "rpki", strlen("rpki")) == 0)
498 config = config_get(RPKI_NODE, line);
499 else {
500 if (strncmp(line, "log", strlen("log")) == 0 ||
501 strncmp(line, "hostname", strlen("hostname")) ==
502 0 ||
503 strncmp(line, "domainname", strlen("domainname")) ==
504 0 ||
505 strncmp(line, "allow-reserved-ranges",
506 strlen("allow-reserved-ranges")) == 0 ||
507 strncmp(line, "frr", strlen("frr")) == 0 ||
508 strncmp(line, "agentx", strlen("agentx")) == 0 ||
509 strncmp(line, "no log", strlen("no log")) == 0 ||
510 strncmp(line, "no ip prefix-list",
511 strlen("no ip prefix-list")) == 0 ||
512 strncmp(line, "no ipv6 prefix-list",
513 strlen("no ipv6 prefix-list")) == 0 ||
514 strncmp(line, "service ", strlen("service ")) ==
515 0 ||
516 strncmp(line, "no service cputime-stats",
517 strlen("no service cputime-stats")) == 0 ||
518 strncmp(line, "service cputime-warning",
519 strlen("service cputime-warning")) == 0)
520 config_add_line_uniq(config_top, line);
521 else
522 config_add_line(config_top, line);
523 config = NULL;
524 }
525 break;
526 }
527 }
528
529 /* Macro to check delimiter is needed between each configuration line
530 * or not. */
531 #define NO_DELIMITER(I) \
532 ((I) == AFFMAP_NODE || (I) == ACCESS_NODE || (I) == PREFIX_NODE || \
533 (I) == IP_NODE || (I) == AS_LIST_NODE || \
534 (I) == COMMUNITY_LIST_NODE || (I) == COMMUNITY_ALIAS_NODE || \
535 (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE || \
536 (I) == PREFIX_IPV6_NODE || (I) == FORWARDING_NODE || \
537 (I) == DEBUG_NODE || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE || \
538 (I) == NORTHBOUND_DEBUG_NODE || (I) == RMAP_DEBUG_NODE || \
539 (I) == RESOLVER_DEBUG_NODE || (I) == MPLS_NODE || \
540 (I) == KEYCHAIN_KEY_NODE)
541
542 static void configvec_dump(vector vec, bool nested)
543 {
544 struct listnode *mnode, *mnnode;
545 struct config *config;
546 struct configuration *configuration;
547 char *line;
548 unsigned int i;
549
550 for (i = 0; i < vector_active(vec); i++)
551 if ((configuration = vector_slot(vec, i)) != NULL) {
552 while ((config = config_master_pop(
553 &configuration->master))) {
554 config_master_hash_del(
555 &configuration->hash_master, config);
556 /* Don't print empty sections for interface.
557 * Route maps on the
558 * other hand could have a legitimate empty
559 * section at the end.
560 * VRF is handled in the backend, we could have
561 * "configured" VRFs with static routes which
562 * are not under the VRF node.
563 */
564 if (config->index == INTERFACE_NODE
565 && (listcount(config->line) == 1)
566 && (line = listnode_head(config->line))
567 && strmatch(line, "exit")) {
568 config_del(config);
569 continue;
570 }
571
572 vty_out(vty, "%s\n", config->name);
573
574 for (ALL_LIST_ELEMENTS(config->line, mnode,
575 mnnode, line))
576 vty_out(vty, "%s\n", line);
577
578 configvec_dump(config->nested, true);
579
580 if (config->exit)
581 vty_out(vty, "%s\n", config->exit);
582
583 if (!NO_DELIMITER(i))
584 vty_out(vty, "!\n");
585
586 config_del(config);
587 }
588 config_master_fini(&configuration->master);
589 config_master_hash_fini(&configuration->hash_master);
590 XFREE(MTYPE_VTYSH_CONFIG, configuration);
591 vector_slot(vec, i) = NULL;
592 if (!nested && NO_DELIMITER(i))
593 vty_out(vty, "!\n");
594 }
595 }
596
597 void vtysh_config_dump(void)
598 {
599 struct listnode *node, *nnode;
600 char *line;
601
602 for (ALL_LIST_ELEMENTS(config_top, node, nnode, line))
603 vty_out(vty, "%s\n", line);
604
605 list_delete_all_node(config_top);
606
607 vty_out(vty, "!\n");
608
609 configvec_dump(configvec, false);
610 }
611
612 /* Read up configuration file from file_name. */
613 static int vtysh_read_file(FILE *confp, bool dry_run)
614 {
615 struct vty *vty;
616 int ret;
617
618 vty = vty_new();
619 vty->wfd = STDERR_FILENO;
620 vty->type = VTY_TERM;
621 vty->node = CONFIG_NODE;
622
623 vtysh_execute_no_pager("enable");
624 vtysh_execute_no_pager("configure terminal");
625
626 if (!dry_run)
627 vtysh_execute_no_pager("XFRR_start_configuration");
628
629 /* Execute configuration file. */
630 ret = vtysh_config_from_file(vty, confp);
631
632 if (!dry_run)
633 vtysh_execute_no_pager("XFRR_end_configuration");
634
635 vtysh_execute_no_pager("end");
636 vtysh_execute_no_pager("disable");
637
638 vty_close(vty);
639
640 return (ret);
641 }
642
643 /* Read up configuration file from config_default_dir. */
644 int vtysh_read_config(const char *config_default_dir, bool dry_run)
645 {
646 FILE *confp = NULL;
647 bool save;
648 int ret;
649
650 confp = fopen(config_default_dir, "r");
651 if (confp == NULL) {
652 fprintf(stderr,
653 "%% Can't open configuration file %s due to '%s'.\n",
654 config_default_dir, safe_strerror(errno));
655 return CMD_ERR_NO_FILE;
656 }
657
658 save = vtysh_add_timestamp;
659 vtysh_add_timestamp = false;
660
661 ret = vtysh_read_file(confp, dry_run);
662 fclose(confp);
663
664 vtysh_add_timestamp = save;
665
666 return (ret);
667 }
668
669 /* We don't write vtysh specific into file from vtysh. vtysh.conf should
670 * be edited by hand. So, we handle only "write terminal" case here and
671 * integrate vtysh specific conf with conf from daemons.
672 */
673 void vtysh_config_write(void)
674 {
675 const char *name;
676 char line[512];
677
678 name = cmd_hostname_get();
679 if (name && name[0] != '\0') {
680 snprintf(line, sizeof(line), "hostname %s", name);
681 vtysh_config_parse_line(NULL, line);
682 }
683
684 name = cmd_domainname_get();
685 if (name && name[0] != '\0') {
686 snprintf(line, sizeof(line), "domainname %s", name);
687 vtysh_config_parse_line(NULL, line);
688 }
689
690 if (vtysh_write_integrated == WRITE_INTEGRATED_NO)
691 vtysh_config_parse_line(NULL,
692 "no service integrated-vtysh-config");
693 if (vtysh_write_integrated == WRITE_INTEGRATED_YES)
694 vtysh_config_parse_line(NULL,
695 "service integrated-vtysh-config");
696
697 user_config_write();
698 }
699
700 void vtysh_config_init(void)
701 {
702 config_top = list_new();
703 config_top->del = (void (*)(void *))line_del;
704 configvec = vector_init(1);
705 }