]>
git.proxmox.com Git - mirror_frr.git/blob - vtysh/vtysh_config.c
1 /* Configuration generator.
2 * Copyright (C) 2000 Kunihiro Ishiguro
4 * This file is part of GNU Zebra.
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
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.
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
27 #include "vtysh/vtysh.h"
28 #include "vtysh/vtysh_user.h"
30 DEFINE_MGROUP(MVTYSH
, "vtysh")
31 DEFINE_MTYPE_STATIC(MVTYSH
, VTYSH_CONFIG
, "Vtysh configuration")
32 DEFINE_MTYPE_STATIC(MVTYSH
, VTYSH_CONFIG_LINE
, "Vtysh configuration line")
37 /* Configuration node name. */
40 /* Configuration string line. */
43 /* Configuration can be nest. */
44 struct config
*config
;
46 /* Index of this config. */
50 struct list
*config_top
;
52 static int line_cmp(char *c1
, char *c2
)
54 return strcmp(c1
, c2
);
57 static void line_del(char *line
)
59 XFREE(MTYPE_VTYSH_CONFIG_LINE
, line
);
62 static struct config
*config_new(void)
64 struct config
*config
;
65 config
= XCALLOC(MTYPE_VTYSH_CONFIG
, sizeof(struct config
));
69 static int config_cmp(struct config
*c1
, struct config
*c2
)
71 return strcmp(c1
->name
, c2
->name
);
74 static void config_del(struct config
*config
)
76 list_delete(&config
->line
);
77 XFREE(MTYPE_VTYSH_CONFIG_LINE
, config
->name
);
78 XFREE(MTYPE_VTYSH_CONFIG
, config
);
81 static struct config
*config_get(int index
, const char *line
)
83 struct config
*config
;
84 struct config
*config_loop
;
86 struct listnode
*node
, *nnode
;
88 config
= config_loop
= NULL
;
90 master
= vector_lookup_ensure(configvec
, index
);
94 master
->del
= (void (*)(void *))config_del
;
95 master
->cmp
= (int (*)(void *, void *))config_cmp
;
96 vector_set_index(configvec
, index
, master
);
99 for (ALL_LIST_ELEMENTS(master
, node
, nnode
, config_loop
)) {
100 if (strcmp(config_loop
->name
, line
) == 0)
101 config
= config_loop
;
105 config
= config_new();
106 config
->line
= list_new();
107 config
->line
->del
= (void (*)(void *))line_del
;
108 config
->line
->cmp
= (int (*)(void *, void *))line_cmp
;
109 config
->name
= XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
);
110 config
->index
= index
;
111 listnode_add(master
, config
);
116 void config_add_line(struct list
*config
, const char *line
)
118 listnode_add(config
, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
));
121 static void config_add_line_uniq(struct list
*config
, const char *line
)
123 struct listnode
*node
, *nnode
;
126 for (ALL_LIST_ELEMENTS(config
, node
, nnode
, pnt
)) {
127 if (strcmp(pnt
, line
) == 0)
130 listnode_add_sort(config
, XSTRDUP(MTYPE_VTYSH_CONFIG_LINE
, line
));
134 * Add a line that should only be shown once, and always show at the end of the
137 * If the line already exists, it will be moved to the end of the block. If it
138 * does not exist, it will be added at the end of the block.
140 * Note that this only makes sense when there is just one such line that should
141 * show up at the very end of a config block. Furthermore, if the same block
142 * can show up from multiple daemons, all of them must make sure to print the
143 * line at the end of their config, otherwise the line will show at the end of
144 * the config for the last daemon that printed it.
146 * Here is a motivating example with the 'exit-vrf' command. Suppose we receive
147 * a config from Zebra like so:
154 * Then suppose we later receive this config from PIM:
157 * ip msdp mesh-group MyGroup member 1.2.3.4
160 * Then we will combine them into one config block like so:
165 * ip msdp mesh-group MyGroup member 1.2.3.4
168 * Because PIM also sent us an 'exit-vrf', we noticed that we already had one
169 * under the 'vrf BLUE' config block and so we moved it to the end of the
170 * config block again. If PIM had neglected to send us 'exit-vrf', the result
177 * ip msdp mesh-group MyGroup member 1.2.3.4
179 * Therefore, daemons that share config blocks must take care to consistently
180 * print the same block terminators.
182 * Ideally this would be solved by adding a string to struct config that is
183 * always printed at the end when dumping a config. However, this would only
184 * work when the user is using integrated config. In the non-integrated config
185 * case, daemons are responsible for writing their own config files, and so the
186 * must be able to print these blocks correctly independently of vtysh, which
187 * means they are the ones that need to handle printing the block terminators
188 * and VTYSH needs to be smart enough to combine them properly.
193 * The config to add the line to
196 * The line to add to the end of the config
198 static void config_add_line_uniq_end(struct list
*config
, const char *line
)
200 struct listnode
*node
;
203 for (ALL_LIST_ELEMENTS_RO(config
, node
, pnt
)) {
204 if (strcmp(pnt
, line
) == 0)
209 config_add_line(config
, line
);
211 listnode_move_to_tail(config
, node
);
214 void vtysh_config_parse_line(void *arg
, const char *line
)
217 static struct config
*config
= NULL
;
228 /* Suppress exclamation points ! and commented lines. The !s are
230 * dynamically in vtysh_config_dump() */
235 /* Store line to current configuration. */
237 if (strncmp(line
, " link-params",
238 strlen(" link-params"))
240 config_add_line(config
->line
, line
);
241 config
->index
= LINK_PARAMS_NODE
;
242 } else if (strncmp(line
, " ip multicast boundary",
243 strlen(" ip multicast boundary"))
245 config_add_line_uniq_end(config
->line
, line
);
246 } else if (strncmp(line
, " ip igmp query-interval",
247 strlen(" ip igmp query-interval")) == 0) {
248 config_add_line_uniq_end(config
->line
, line
);
249 } else if (config
->index
== LINK_PARAMS_NODE
250 && strncmp(line
, " exit-link-params",
253 config_add_line(config
->line
, line
);
254 config
->index
= INTERFACE_NODE
;
255 } else if (config
->index
== VRF_NODE
256 && strncmp(line
, " exit-vrf",
259 config_add_line_uniq_end(config
->line
, line
);
260 } else if (config
->index
== RMAP_NODE
261 || config
->index
== INTERFACE_NODE
262 || config
->index
== LOGICALROUTER_NODE
263 || config
->index
== VTY_NODE
264 || config
->index
== VRF_NODE
)
265 config_add_line_uniq(config
->line
, line
);
267 config_add_line(config
->line
, line
);
269 config_add_line(config_top
, line
);
272 if (strncmp(line
, "interface", strlen("interface")) == 0)
273 config
= config_get(INTERFACE_NODE
, line
);
274 else if (strncmp(line
, "pseudowire", strlen("pseudowire")) == 0)
275 config
= config_get(PW_NODE
, line
);
276 else if (strncmp(line
, "logical-router", strlen("logical-router")) == 0)
277 config
= config_get(LOGICALROUTER_NODE
, line
);
278 else if (strncmp(line
, "vrf", strlen("vrf")) == 0)
279 config
= config_get(VRF_NODE
, line
);
280 else if (strncmp(line
, "nexthop-group", strlen("nexthop-group"))
282 config
= config_get(NH_GROUP_NODE
, line
);
283 else if (strncmp(line
, "router-id", strlen("router-id")) == 0)
284 config
= config_get(ZEBRA_NODE
, line
);
285 else if (strncmp(line
, "router rip", strlen("router rip")) == 0)
286 config
= config_get(RIP_NODE
, line
);
287 else if (strncmp(line
, "router ripng", strlen("router ripng"))
289 config
= config_get(RIPNG_NODE
, line
);
290 else if (strncmp(line
, "router eigrp", strlen("router eigrp"))
292 config
= config_get(EIGRP_NODE
, line
);
293 else if (strncmp(line
, "router babel", strlen("router babel"))
295 config
= config_get(BABEL_NODE
, line
);
296 else if (strncmp(line
, "router ospf", strlen("router ospf"))
298 config
= config_get(OSPF_NODE
, line
);
299 else if (strncmp(line
, "router ospf6", strlen("router ospf6"))
301 config
= config_get(OSPF6_NODE
, line
);
302 else if (strncmp(line
, "mpls ldp", strlen("mpls ldp")) == 0)
303 config
= config_get(LDP_NODE
, line
);
304 else if (strncmp(line
, "l2vpn", strlen("l2vpn")) == 0)
305 config
= config_get(LDP_L2VPN_NODE
, line
);
306 else if (strncmp(line
, "router bgp", strlen("router bgp")) == 0)
307 config
= config_get(BGP_NODE
, line
);
308 else if (strncmp(line
, "router isis", strlen("router isis"))
310 config
= config_get(ISIS_NODE
, line
);
311 else if (strncmp(line
, "router openfabric", strlen("router openfabric"))
313 config
= config_get(OPENFABRIC_NODE
, line
);
314 else if (strncmp(line
, "route-map", strlen("route-map")) == 0)
315 config
= config_get(RMAP_NODE
, line
);
316 else if (strncmp(line
, "pbr-map", strlen("pbr-map")) == 0)
317 config
= config_get(PBRMAP_NODE
, line
);
318 else if (strncmp(line
, "access-list", strlen("access-list"))
320 config
= config_get(ACCESS_NODE
, line
);
321 else if (strncmp(line
, "ipv6 access-list",
322 strlen("ipv6 access-list"))
324 config
= config_get(ACCESS_IPV6_NODE
, line
);
325 else if (strncmp(line
, "mac access-list",
326 strlen("mac access-list"))
328 config
= config_get(ACCESS_MAC_NODE
, line
);
329 else if (strncmp(line
, "ip prefix-list",
330 strlen("ip prefix-list"))
332 config
= config_get(PREFIX_NODE
, line
);
333 else if (strncmp(line
, "ipv6 prefix-list",
334 strlen("ipv6 prefix-list"))
336 config
= config_get(PREFIX_IPV6_NODE
, line
);
337 else if (strncmp(line
, "bgp as-path access-list",
338 strlen("bgp as-path access-list"))
340 config
= config_get(AS_LIST_NODE
, line
);
341 else if (strncmp(line
, "bgp community-list",
342 strlen("bgp community-list"))
344 || strncmp(line
, "bgp extcommunity-list",
345 strlen("bgp extcommunity-list"))
347 || strncmp(line
, "bgp large-community-list",
348 strlen("bgp large-community-list"))
350 config
= config_get(COMMUNITY_LIST_NODE
, line
);
351 else if (strncmp(line
, "ip route", strlen("ip route")) == 0)
352 config
= config_get(IP_NODE
, line
);
353 else if (strncmp(line
, "ipv6 route", strlen("ipv6 route")) == 0)
354 config
= config_get(IP_NODE
, line
);
355 else if (strncmp(line
, "key", strlen("key")) == 0)
356 config
= config_get(KEYCHAIN_NODE
, line
);
357 else if (strncmp(line
, "line", strlen("line")) == 0)
358 config
= config_get(VTY_NODE
, line
);
359 else if ((strncmp(line
, "ipv6 forwarding",
360 strlen("ipv6 forwarding"))
362 || (strncmp(line
, "ip forwarding",
363 strlen("ip forwarding"))
365 config
= config_get(FORWARDING_NODE
, line
);
366 else if (strncmp(line
, "debug vrf", strlen("debug vrf")) == 0)
367 config
= config_get(VRF_DEBUG_NODE
, line
);
368 else if (strncmp(line
, "debug northbound",
369 strlen("debug northbound"))
371 config
= config_get(NORTHBOUND_DEBUG_NODE
, line
);
372 else if (strncmp(line
, "debug", strlen("debug")) == 0)
373 config
= config_get(DEBUG_NODE
, line
);
374 else if (strncmp(line
, "password", strlen("password")) == 0
375 || strncmp(line
, "enable password",
376 strlen("enable password"))
378 config
= config_get(AAA_NODE
, line
);
379 else if (strncmp(line
, "ip protocol", strlen("ip protocol"))
381 config
= config_get(PROTOCOL_NODE
, line
);
382 else if (strncmp(line
, "ipv6 protocol", strlen("ipv6 protocol"))
384 config
= config_get(PROTOCOL_NODE
, line
);
385 else if (strncmp(line
, "ip nht", strlen("ip nht")) == 0)
386 config
= config_get(PROTOCOL_NODE
, line
);
387 else if (strncmp(line
, "ipv6 nht", strlen("ipv6 nht")) == 0)
388 config
= config_get(PROTOCOL_NODE
, line
);
389 else if (strncmp(line
, "mpls", strlen("mpls")) == 0)
390 config
= config_get(MPLS_NODE
, line
);
391 else if (strncmp(line
, "bfd", strlen("bfd")) == 0)
392 config
= config_get(BFD_NODE
, line
);
394 if (strncmp(line
, "log", strlen("log")) == 0
395 || strncmp(line
, "hostname", strlen("hostname"))
397 || strncmp(line
, "frr", strlen("frr")) == 0
398 || strncmp(line
, "agentx", strlen("agentx")) == 0
399 || strncmp(line
, "no log", strlen("no log")) == 0)
400 config_add_line_uniq(config_top
, line
);
402 config_add_line(config_top
, line
);
409 /* Macro to check delimiter is needed between each configuration line
411 #define NO_DELIMITER(I) \
412 ((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
413 || (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \
414 || (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE \
415 || (I) == PREFIX_IPV6_NODE || (I) == FORWARDING_NODE \
416 || (I) == DEBUG_NODE || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE \
417 || (I) == NORTHBOUND_DEBUG_NODE || (I) == MPLS_NODE)
419 /* Display configuration to file pointer. */
420 void vtysh_config_dump(void)
422 struct listnode
*node
, *nnode
;
423 struct listnode
*mnode
, *mnnode
;
424 struct config
*config
;
429 for (ALL_LIST_ELEMENTS(config_top
, node
, nnode
, line
))
430 vty_out(vty
, "%s\n", line
);
434 for (i
= 0; i
< vector_active(configvec
); i
++)
435 if ((master
= vector_slot(configvec
, i
)) != NULL
) {
436 for (ALL_LIST_ELEMENTS(master
, node
, nnode
, config
)) {
437 /* Don't print empty sections for interface.
439 * other hand could have a legitimate empty
440 * section at the end.
441 * VRF is handled in the backend, we could have
442 * "configured" VRFs with static routes which
443 * are not under the VRF node.
445 if (config
->index
== INTERFACE_NODE
446 && list_isempty(config
->line
))
449 vty_out(vty
, "%s\n", config
->name
);
451 for (ALL_LIST_ELEMENTS(config
->line
, mnode
,
453 vty_out(vty
, "%s\n", line
);
454 if (!NO_DELIMITER(i
))
461 for (i
= 0; i
< vector_active(configvec
); i
++)
462 if ((master
= vector_slot(configvec
, i
)) != NULL
) {
463 list_delete(&master
);
464 vector_slot(configvec
, i
) = NULL
;
466 list_delete_all_node(config_top
);
469 /* Read up configuration file from file_name. */
470 static int vtysh_read_file(FILE *confp
)
476 vty
->wfd
= STDERR_FILENO
;
477 vty
->type
= VTY_TERM
;
478 vty
->node
= CONFIG_NODE
;
480 vtysh_execute_no_pager("enable");
481 vtysh_execute_no_pager("configure terminal");
483 /* Execute configuration file. */
484 ret
= vtysh_config_from_file(vty
, confp
);
486 vtysh_execute_no_pager("end");
487 vtysh_execute_no_pager("disable");
494 /* Read up configuration file from config_default_dir. */
495 int vtysh_read_config(const char *config_default_dir
)
500 confp
= fopen(config_default_dir
, "r");
503 "%% Can't open configuration file %s due to '%s'.\n",
504 config_default_dir
, safe_strerror(errno
));
505 return (CMD_ERR_NO_FILE
);
508 ret
= vtysh_read_file(confp
);
514 /* We don't write vtysh specific into file from vtysh. vtysh.conf should
515 * be edited by hand. So, we handle only "write terminal" case here and
516 * integrate vtysh specific conf with conf from daemons.
518 void vtysh_config_write(void)
522 if (cmd_hostname_get()) {
523 sprintf(line
, "hostname %s", cmd_hostname_get());
524 vtysh_config_parse_line(NULL
, line
);
527 if (cmd_domainname_get()) {
528 sprintf(line
, "domainname %s", cmd_domainname_get());
529 vtysh_config_parse_line(NULL
, line
);
531 if (vtysh_write_integrated
== WRITE_INTEGRATED_NO
)
532 vtysh_config_parse_line(NULL
,
533 "no service integrated-vtysh-config");
534 if (vtysh_write_integrated
== WRITE_INTEGRATED_YES
)
535 vtysh_config_parse_line(NULL
,
536 "service integrated-vtysh-config");
541 void vtysh_config_init(void)
543 config_top
= list_new();
544 config_top
->del
= (void (*)(void *))line_del
;
545 configvec
= vector_init(1);