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