Line data Source code
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 16 : DEFINE_MGROUP(MVTYSH, "vtysh");
32 16 : DEFINE_MTYPE_STATIC(MVTYSH, VTYSH_CONFIG, "Vtysh configuration");
33 16 : 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 0 : static int line_cmp(char *c1, char *c2)
65 : {
66 0 : return strcmp(c1, c2);
67 : }
68 :
69 0 : static void line_del(char *line)
70 : {
71 0 : XFREE(MTYPE_VTYSH_CONFIG_LINE, line);
72 0 : }
73 :
74 0 : static struct config *config_new(void)
75 : {
76 0 : struct config *config;
77 0 : config = XCALLOC(MTYPE_VTYSH_CONFIG, sizeof(struct config));
78 0 : return config;
79 : }
80 :
81 0 : static void config_del(struct config *config)
82 : {
83 0 : vector_free(config->nested);
84 0 : list_delete(&config->line);
85 0 : if (config->exit)
86 0 : XFREE(MTYPE_VTYSH_CONFIG_LINE, config->exit);
87 0 : XFREE(MTYPE_VTYSH_CONFIG_LINE, config->name);
88 0 : XFREE(MTYPE_VTYSH_CONFIG, config);
89 0 : }
90 :
91 0 : static int config_cmp(const struct config *c1, const struct config *c2)
92 : {
93 0 : return strcmp(c1->name, c2->name);
94 : }
95 :
96 0 : static uint32_t config_hash(const struct config *c)
97 : {
98 0 : return string_hash_make(c->name);
99 : }
100 :
101 0 : DECLARE_LIST(config_master, struct config, rbt_item);
102 0 : 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 0 : static struct config *config_get_vec(vector vec, int index, const char *line)
115 : {
116 0 : struct config *config, *config_loop;
117 0 : struct configuration *configuration;
118 0 : struct config lookup;
119 :
120 0 : config = config_loop = NULL;
121 :
122 0 : configuration = vector_lookup_ensure(vec, index);
123 :
124 0 : if (!configuration) {
125 0 : configuration = XMALLOC(MTYPE_VTYSH_CONFIG,
126 : sizeof(struct configuration));
127 0 : config_master_init(&configuration->master);
128 0 : config_master_hash_init(&configuration->hash_master);
129 0 : vector_set_index(vec, index, configuration);
130 : }
131 :
132 0 : lookup.name = (char *)line;
133 0 : config = config_master_hash_find(&configuration->hash_master, &lookup);
134 :
135 0 : if (!config) {
136 0 : config = config_new();
137 0 : config->line = list_new();
138 0 : config->line->del = (void (*)(void *))line_del;
139 0 : config->line->cmp = (int (*)(void *, void *))line_cmp;
140 0 : config->name = XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
141 0 : config->exit = NULL;
142 0 : config->index = index;
143 0 : config->nested = vector_init(1);
144 0 : config_master_add_tail(&configuration->master, config);
145 0 : config_master_hash_add(&configuration->hash_master, config);
146 : }
147 0 : return config;
148 : }
149 :
150 0 : static struct config *config_get(int index, const char *line)
151 : {
152 0 : return config_get_vec(configvec, index, line);
153 : }
154 :
155 0 : static struct config *config_get_nested(struct config *parent, int index,
156 : const char *line)
157 : {
158 0 : struct config *config;
159 :
160 0 : config = config_get_vec(parent->nested, index, line);
161 0 : config->parent = parent;
162 :
163 0 : return config;
164 : }
165 :
166 0 : void config_add_line(struct list *config, const char *line)
167 : {
168 0 : listnode_add(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
169 0 : }
170 :
171 0 : static void config_add_line_uniq(struct list *config, const char *line)
172 : {
173 0 : struct listnode *node, *nnode;
174 0 : char *pnt;
175 :
176 0 : for (ALL_LIST_ELEMENTS(config, node, nnode, pnt)) {
177 0 : if (strcmp(pnt, line) == 0)
178 : return;
179 : }
180 0 : 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 0 : static void config_add_line_uniq_end(struct list *config, const char *line)
249 : {
250 0 : struct listnode *node;
251 0 : char *pnt;
252 :
253 0 : for (ALL_LIST_ELEMENTS_RO(config, node, pnt)) {
254 0 : if (strcmp(pnt, line) == 0)
255 : break;
256 : }
257 :
258 0 : if (!node)
259 0 : config_add_line(config, line);
260 : else
261 0 : listnode_move_to_tail(config, node);
262 0 : }
263 :
264 0 : static void config_add_line_head(struct list *config, const char *line)
265 : {
266 0 : listnode_add_head(config, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line));
267 0 : }
268 :
269 0 : void vtysh_config_parse_line(void *arg, const char *line)
270 : {
271 0 : char c;
272 0 : static struct config *config = NULL;
273 :
274 0 : if (!line)
275 : return;
276 :
277 0 : c = line[0];
278 :
279 0 : if (c == '\0')
280 : return;
281 :
282 0 : 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 0 : case ' ':
290 : /* Store line to current configuration. */
291 0 : if (config) {
292 0 : if (config->index == KEYCHAIN_NODE
293 0 : && strncmp(line, " key", strlen(" key")) == 0) {
294 0 : config = config_get_nested(
295 : config, KEYCHAIN_KEY_NODE, line);
296 0 : } else if (config->index == KEYCHAIN_KEY_NODE) {
297 0 : if (strncmp(line, " exit", strlen(" exit"))
298 : == 0) {
299 0 : config_add_line_uniq_end(config->line,
300 : line);
301 0 : config = config->parent;
302 : } else {
303 0 : config_add_line_uniq(config->line,
304 : line);
305 : }
306 0 : } else if (strncmp(line, " link-params",
307 : strlen(" link-params"))
308 : == 0) {
309 0 : config_add_line(config->line, line);
310 0 : config->index = LINK_PARAMS_NODE;
311 0 : } else if (strncmp(line, " ip multicast boundary",
312 : strlen(" ip multicast boundary"))
313 : == 0) {
314 0 : config_add_line_uniq_end(config->line, line);
315 0 : } else if (strncmp(line, " ip igmp query-interval",
316 : strlen(" ip igmp query-interval"))
317 : == 0) {
318 0 : config_add_line_uniq_end(config->line, line);
319 0 : } else if (config->index == LINK_PARAMS_NODE
320 0 : && strncmp(line, " exit-link-params",
321 : strlen(" exit"))
322 : == 0) {
323 0 : config_add_line(config->line, line);
324 0 : config->index = INTERFACE_NODE;
325 0 : } else if (!strncmp(line, " vrrp", strlen(" vrrp"))
326 0 : || !strncmp(line, " no vrrp",
327 : strlen(" no vrrp"))) {
328 0 : config_add_line(config->line, line);
329 0 : } else if (!strncmp(line, " ip mroute",
330 : strlen(" ip mroute"))) {
331 0 : config_add_line_uniq_end(config->line, line);
332 0 : } else if (config->index == RMAP_NODE ||
333 0 : config->index == INTERFACE_NODE ||
334 : config->index == VTY_NODE)
335 0 : config_add_line_uniq(config->line, line);
336 0 : else if (config->index == NH_GROUP_NODE) {
337 0 : if (strncmp(line, " resilient",
338 : strlen(" resilient")) == 0)
339 0 : config_add_line_head(config->line,
340 : line);
341 : else
342 0 : config_add_line_uniq_end(config->line,
343 : line);
344 : } else
345 0 : config_add_line(config->line, line);
346 : } else
347 0 : config_add_line(config_top, line);
348 : break;
349 0 : default:
350 0 : if (strncmp(line, "exit", strlen("exit")) == 0) {
351 0 : if (config) {
352 0 : if (config->exit)
353 0 : XFREE(MTYPE_VTYSH_CONFIG_LINE,
354 : config->exit);
355 0 : config->exit =
356 0 : XSTRDUP(MTYPE_VTYSH_CONFIG_LINE, line);
357 : }
358 0 : } else if (strncmp(line, "interface", strlen("interface")) == 0)
359 0 : config = config_get(INTERFACE_NODE, line);
360 0 : else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
361 0 : config = config_get(PW_NODE, line);
362 0 : else if (strncmp(line, "vrf", strlen("vrf")) == 0)
363 0 : config = config_get(VRF_NODE, line);
364 0 : else if (strncmp(line, "nexthop-group", strlen("nexthop-group"))
365 : == 0)
366 0 : config = config_get(NH_GROUP_NODE, line);
367 0 : else if (strncmp(line, "router-id", strlen("router-id")) == 0)
368 0 : config = config_get(ZEBRA_NODE, line);
369 0 : else if (strncmp(line, "router rip", strlen("router rip")) == 0)
370 0 : config = config_get(RIP_NODE, line);
371 0 : else if (strncmp(line, "router ripng", strlen("router ripng"))
372 : == 0)
373 0 : config = config_get(RIPNG_NODE, line);
374 0 : else if (strncmp(line, "router eigrp", strlen("router eigrp"))
375 : == 0)
376 0 : config = config_get(EIGRP_NODE, line);
377 0 : else if (strncmp(line, "router babel", strlen("router babel"))
378 : == 0)
379 0 : config = config_get(BABEL_NODE, line);
380 0 : else if (strncmp(line, "router ospf", strlen("router ospf"))
381 : == 0)
382 0 : config = config_get(OSPF_NODE, line);
383 0 : else if (strncmp(line, "router ospf6", strlen("router ospf6"))
384 : == 0)
385 0 : config = config_get(OSPF6_NODE, line);
386 0 : else if (strncmp(line, "mpls ldp", strlen("mpls ldp")) == 0)
387 0 : config = config_get(LDP_NODE, line);
388 0 : else if (strncmp(line, "l2vpn", strlen("l2vpn")) == 0)
389 0 : config = config_get(LDP_L2VPN_NODE, line);
390 0 : else if (strncmp(line, "router bgp", strlen("router bgp")) == 0)
391 0 : config = config_get(BGP_NODE, line);
392 0 : else if (strncmp(line, "router isis", strlen("router isis"))
393 : == 0)
394 0 : config = config_get(ISIS_NODE, line);
395 0 : else if (strncmp(line, "router openfabric", strlen("router openfabric"))
396 : == 0)
397 0 : config = config_get(OPENFABRIC_NODE, line);
398 0 : else if (strncmp(line, "route-map", strlen("route-map")) == 0)
399 0 : config = config_get(RMAP_NODE, line);
400 0 : else if (strncmp(line, "no route-map", strlen("no route-map"))
401 : == 0)
402 0 : config = config_get(RMAP_NODE, line);
403 0 : else if (strncmp(line, "pbr-map", strlen("pbr-map")) == 0)
404 0 : config = config_get(PBRMAP_NODE, line);
405 0 : else if (strncmp(line, "access-list", strlen("access-list"))
406 : == 0)
407 0 : config = config_get(ACCESS_NODE, line);
408 0 : else if (strncmp(line, "ipv6 access-list",
409 : strlen("ipv6 access-list"))
410 : == 0)
411 0 : config = config_get(ACCESS_IPV6_NODE, line);
412 0 : else if (strncmp(line, "mac access-list",
413 : strlen("mac access-list"))
414 : == 0)
415 0 : config = config_get(ACCESS_MAC_NODE, line);
416 0 : else if (strncmp(line, "ip prefix-list",
417 : strlen("ip prefix-list"))
418 : == 0)
419 0 : config = config_get(PREFIX_NODE, line);
420 0 : else if (strncmp(line, "ipv6 prefix-list",
421 : strlen("ipv6 prefix-list"))
422 : == 0)
423 0 : config = config_get(PREFIX_IPV6_NODE, line);
424 0 : else if (strncmp(line, "bgp as-path access-list",
425 : strlen("bgp as-path access-list"))
426 : == 0)
427 0 : config = config_get(AS_LIST_NODE, line);
428 0 : else if (strncmp(line, "bgp community-list",
429 : strlen("bgp community-list"))
430 : == 0
431 0 : || strncmp(line, "bgp extcommunity-list",
432 : strlen("bgp extcommunity-list"))
433 : == 0
434 0 : || strncmp(line, "bgp large-community-list",
435 : strlen("bgp large-community-list"))
436 : == 0)
437 0 : config = config_get(COMMUNITY_LIST_NODE, line);
438 0 : else if (strncmp(line, "bgp community alias",
439 : strlen("bgp community alias")) == 0)
440 0 : config = config_get(COMMUNITY_ALIAS_NODE, line);
441 0 : else if (strncmp(line, "ip route", strlen("ip route")) == 0)
442 0 : config = config_get(IP_NODE, line);
443 0 : else if (strncmp(line, "ipv6 route", strlen("ipv6 route")) == 0)
444 0 : config = config_get(IP_NODE, line);
445 0 : else if (strncmp(line, "key", strlen("key")) == 0)
446 0 : config = config_get(KEYCHAIN_NODE, line);
447 0 : else if (strncmp(line, "line", strlen("line")) == 0)
448 0 : config = config_get(VTY_NODE, line);
449 0 : else if ((strncmp(line, "ipv6 forwarding",
450 : strlen("ipv6 forwarding"))
451 : == 0)
452 0 : || (strncmp(line, "ip forwarding",
453 : strlen("ip forwarding"))
454 : == 0))
455 0 : config = config_get(FORWARDING_NODE, line);
456 0 : else if (strncmp(line, "debug vrf", strlen("debug vrf")) == 0)
457 0 : config = config_get(VRF_DEBUG_NODE, line);
458 0 : else if (strncmp(line, "debug northbound",
459 : strlen("debug northbound"))
460 : == 0)
461 0 : config = config_get(NORTHBOUND_DEBUG_NODE, line);
462 0 : else if (strncmp(line, "debug route-map",
463 : strlen("debug route-map"))
464 : == 0)
465 0 : config = config_get(RMAP_DEBUG_NODE, line);
466 0 : else if (strncmp(line, "debug resolver",
467 : strlen("debug resolver")) == 0)
468 0 : config = config_get(RESOLVER_DEBUG_NODE, line);
469 0 : else if (strncmp(line, "debug", strlen("debug")) == 0)
470 0 : config = config_get(DEBUG_NODE, line);
471 0 : else if (strncmp(line, "password", strlen("password")) == 0
472 0 : || strncmp(line, "enable password",
473 : strlen("enable password"))
474 : == 0)
475 0 : config = config_get(AAA_NODE, line);
476 0 : else if (strncmp(line, "ip protocol", strlen("ip protocol"))
477 : == 0)
478 0 : config = config_get(PROTOCOL_NODE, line);
479 0 : else if (strncmp(line, "ipv6 protocol", strlen("ipv6 protocol"))
480 : == 0)
481 0 : config = config_get(PROTOCOL_NODE, line);
482 0 : else if (strncmp(line, "ip nht", strlen("ip nht")) == 0)
483 0 : config = config_get(PROTOCOL_NODE, line);
484 0 : else if (strncmp(line, "ipv6 nht", strlen("ipv6 nht")) == 0)
485 0 : config = config_get(PROTOCOL_NODE, line);
486 0 : else if (strncmp(line, "mpls", strlen("mpls")) == 0)
487 0 : config = config_get(MPLS_NODE, line);
488 0 : else if (strncmp(line, "segment-routing",
489 : strlen("segment-routing"))
490 : == 0)
491 0 : config = config_get(SEGMENT_ROUTING_NODE, line);
492 0 : else if (strncmp(line, "bfd", strlen("bfd")) == 0)
493 0 : config = config_get(BFD_NODE, line);
494 0 : else if (strncmp(line, "rpki", strlen("rpki")) == 0)
495 0 : config = config_get(RPKI_NODE, line);
496 : else {
497 0 : if (strncmp(line, "log", strlen("log")) == 0 ||
498 0 : strncmp(line, "hostname", strlen("hostname")) ==
499 0 : 0 ||
500 0 : strncmp(line, "domainname", strlen("domainname")) ==
501 0 : 0 ||
502 0 : strncmp(line, "allow-reserved-ranges",
503 0 : strlen("allow-reserved-ranges")) == 0 ||
504 0 : strncmp(line, "frr", strlen("frr")) == 0 ||
505 0 : strncmp(line, "agentx", strlen("agentx")) == 0 ||
506 0 : strncmp(line, "no log", strlen("no log")) == 0 ||
507 0 : strncmp(line, "no ip prefix-list",
508 0 : strlen("no ip prefix-list")) == 0 ||
509 0 : strncmp(line, "no ipv6 prefix-list",
510 0 : strlen("no ipv6 prefix-list")) == 0 ||
511 0 : strncmp(line, "service ", strlen("service ")) ==
512 0 : 0 ||
513 0 : strncmp(line, "no service cputime-stats",
514 0 : strlen("no service cputime-stats")) == 0 ||
515 0 : strncmp(line, "service cputime-warning",
516 : strlen("service cputime-warning")) == 0)
517 0 : config_add_line_uniq(config_top, line);
518 : else
519 0 : config_add_line(config_top, line);
520 0 : config = NULL;
521 : }
522 : break;
523 : }
524 : }
525 :
526 : /* Macro to check delimiter is needed between each configuration line
527 : * or not. */
528 : #define NO_DELIMITER(I) \
529 : ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
530 : || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \
531 : || (I) == COMMUNITY_ALIAS_NODE || (I) == ACCESS_IPV6_NODE \
532 : || (I) == ACCESS_MAC_NODE || (I) == PREFIX_IPV6_NODE \
533 : || (I) == FORWARDING_NODE || (I) == DEBUG_NODE || (I) == AAA_NODE \
534 : || (I) == VRF_DEBUG_NODE || (I) == NORTHBOUND_DEBUG_NODE \
535 : || (I) == RMAP_DEBUG_NODE || (I) == RESOLVER_DEBUG_NODE \
536 : || (I) == MPLS_NODE || (I) == KEYCHAIN_KEY_NODE)
537 :
538 0 : static void configvec_dump(vector vec, bool nested)
539 : {
540 0 : struct listnode *mnode, *mnnode;
541 0 : struct config *config;
542 0 : struct configuration *configuration;
543 0 : char *line;
544 0 : unsigned int i;
545 :
546 0 : for (i = 0; i < vector_active(vec); i++)
547 0 : if ((configuration = vector_slot(vec, i)) != NULL) {
548 0 : while ((config = config_master_pop(
549 : &configuration->master))) {
550 0 : config_master_hash_del(
551 : &configuration->hash_master, config);
552 : /* Don't print empty sections for interface.
553 : * Route maps on the
554 : * other hand could have a legitimate empty
555 : * section at the end.
556 : * VRF is handled in the backend, we could have
557 : * "configured" VRFs with static routes which
558 : * are not under the VRF node.
559 : */
560 0 : if (config->index == INTERFACE_NODE
561 0 : && (listcount(config->line) == 1)
562 0 : && (line = listnode_head(config->line))
563 0 : && strmatch(line, "exit")) {
564 0 : config_del(config);
565 0 : continue;
566 : }
567 :
568 0 : vty_out(vty, "%s\n", config->name);
569 :
570 0 : for (ALL_LIST_ELEMENTS(config->line, mnode,
571 : mnnode, line))
572 0 : vty_out(vty, "%s\n", line);
573 :
574 0 : configvec_dump(config->nested, true);
575 :
576 0 : if (config->exit)
577 0 : vty_out(vty, "%s\n", config->exit);
578 :
579 0 : if (!NO_DELIMITER(i))
580 0 : vty_out(vty, "!\n");
581 :
582 0 : config_del(config);
583 : }
584 0 : config_master_fini(&configuration->master);
585 0 : config_master_hash_fini(&configuration->hash_master);
586 0 : XFREE(MTYPE_VTYSH_CONFIG, configuration);
587 0 : vector_slot(vec, i) = NULL;
588 0 : if (!nested && NO_DELIMITER(i))
589 0 : vty_out(vty, "!\n");
590 : }
591 0 : }
592 :
593 0 : void vtysh_config_dump(void)
594 : {
595 0 : struct listnode *node, *nnode;
596 0 : char *line;
597 :
598 0 : for (ALL_LIST_ELEMENTS(config_top, node, nnode, line))
599 0 : vty_out(vty, "%s\n", line);
600 :
601 0 : list_delete_all_node(config_top);
602 :
603 0 : vty_out(vty, "!\n");
604 :
605 0 : configvec_dump(configvec, false);
606 0 : }
607 :
608 : /* Read up configuration file from file_name. */
609 16 : static int vtysh_read_file(FILE *confp, bool dry_run)
610 : {
611 16 : struct vty *vty;
612 16 : int ret;
613 :
614 16 : vty = vty_new();
615 16 : vty->wfd = STDERR_FILENO;
616 16 : vty->type = VTY_TERM;
617 16 : vty->node = CONFIG_NODE;
618 :
619 16 : vtysh_execute_no_pager("enable");
620 16 : vtysh_execute_no_pager("configure terminal");
621 :
622 16 : if (!dry_run)
623 16 : vtysh_execute_no_pager("XFRR_start_configuration");
624 :
625 : /* Execute configuration file. */
626 16 : ret = vtysh_config_from_file(vty, confp);
627 :
628 16 : if (!dry_run)
629 16 : vtysh_execute_no_pager("XFRR_end_configuration");
630 :
631 16 : vtysh_execute_no_pager("end");
632 16 : vtysh_execute_no_pager("disable");
633 :
634 16 : vty_close(vty);
635 :
636 16 : return (ret);
637 : }
638 :
639 : /* Read up configuration file from config_default_dir. */
640 16 : int vtysh_read_config(const char *config_default_dir, bool dry_run)
641 : {
642 16 : FILE *confp = NULL;
643 16 : bool save;
644 16 : int ret;
645 :
646 16 : confp = fopen(config_default_dir, "r");
647 16 : if (confp == NULL) {
648 0 : fprintf(stderr,
649 : "%% Can't open configuration file %s due to '%s'.\n",
650 0 : config_default_dir, safe_strerror(errno));
651 0 : return CMD_ERR_NO_FILE;
652 : }
653 :
654 16 : save = vtysh_add_timestamp;
655 16 : vtysh_add_timestamp = false;
656 :
657 16 : ret = vtysh_read_file(confp, dry_run);
658 16 : fclose(confp);
659 :
660 16 : vtysh_add_timestamp = save;
661 :
662 16 : return (ret);
663 : }
664 :
665 : /* We don't write vtysh specific into file from vtysh. vtysh.conf should
666 : * be edited by hand. So, we handle only "write terminal" case here and
667 : * integrate vtysh specific conf with conf from daemons.
668 : */
669 0 : void vtysh_config_write(void)
670 : {
671 0 : const char *name;
672 0 : char line[512];
673 :
674 0 : name = cmd_hostname_get();
675 0 : if (name && name[0] != '\0') {
676 0 : snprintf(line, sizeof(line), "hostname %s", name);
677 0 : vtysh_config_parse_line(NULL, line);
678 : }
679 :
680 0 : name = cmd_domainname_get();
681 0 : if (name && name[0] != '\0') {
682 0 : snprintf(line, sizeof(line), "domainname %s", name);
683 0 : vtysh_config_parse_line(NULL, line);
684 : }
685 :
686 0 : if (vtysh_write_integrated == WRITE_INTEGRATED_NO)
687 0 : vtysh_config_parse_line(NULL,
688 : "no service integrated-vtysh-config");
689 0 : if (vtysh_write_integrated == WRITE_INTEGRATED_YES)
690 0 : vtysh_config_parse_line(NULL,
691 : "service integrated-vtysh-config");
692 :
693 0 : user_config_write();
694 0 : }
695 :
696 8 : void vtysh_config_init(void)
697 : {
698 8 : config_top = list_new();
699 8 : config_top->del = (void (*)(void *))line_del;
700 8 : configvec = vector_init(1);
701 8 : }
|