From ac768090270459cd032f5f325a8dffa27819c541 Mon Sep 17 00:00:00 2001 From: Quentin Young Date: Fri, 19 Nov 2021 16:03:02 -0500 Subject: [PATCH] vtysh: use fork() workflow for -f too It was being used for -b only; we should be able to use it for -f as well. This also merges the codepaths for -b and -f since they have no real functional difference. Signed-off-by: Quentin Young --- vtysh/vtysh.c | 2 +- vtysh/vtysh.h | 2 +- vtysh/vtysh_config.c | 99 +++++++++++++++++++++++++++++++++-- vtysh/vtysh_main.c | 120 +++++-------------------------------------- 4 files changed, 108 insertions(+), 115 deletions(-) diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 7c9c46202e..4d52bd036d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -3536,7 +3536,7 @@ DEFUN (vtysh_copy_to_running, int ret; const char *fname = argv[1]->arg; - ret = vtysh_read_config(fname, true); + ret = vtysh_apply_config(fname, true, false); /* Return to enable mode - the 'read_config' api leaves us up a level */ vtysh_execute_no_pager("enable"); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 640922eed9..1c2cca9d90 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -98,7 +98,7 @@ void config_add_line(struct list *, const char *); int vtysh_mark_file(const char *filename); -int vtysh_read_config(const char *filename, bool dry_run); +int vtysh_apply_config(const char *config_file_path, bool dry_run, bool fork); int vtysh_write_config_integrated(void); void vtysh_config_parse_line(void *, const char *); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 905761a011..0a0c745fda 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -4,6 +4,7 @@ */ #include +#include #include "command.h" #include "linklist.h" @@ -625,18 +626,20 @@ static int vtysh_read_file(FILE *confp, bool dry_run) return (ret); } -/* Read up configuration file from config_default_dir. */ -int vtysh_read_config(const char *config_default_dir, bool dry_run) +/* + * Read configuration file and send it to all connected daemons + */ +static int vtysh_read_config(const char *config_file_path, bool dry_run) { FILE *confp = NULL; bool save; int ret; - confp = fopen(config_default_dir, "r"); + confp = fopen(config_file_path, "r"); if (confp == NULL) { fprintf(stderr, "%% Can't open configuration file %s due to '%s'.\n", - config_default_dir, safe_strerror(errno)); + config_file_path, safe_strerror(errno)); return CMD_ERR_NO_FILE; } @@ -648,7 +651,93 @@ int vtysh_read_config(const char *config_default_dir, bool dry_run) vtysh_add_timestamp = save; - return (ret); + return ret; +} + +int vtysh_apply_config(const char *config_file_path, bool dry_run, bool do_fork) +{ + /* + * We need to apply the whole config file to all daemons. Instead of + * having one client talk to N daemons, we fork N times and let each + * child handle one daemon. + */ + pid_t fork_pid = getpid(); + int status = 0; + int ret; + int my_client_type; + char my_client[64]; + + if (do_fork) { + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + /* Store name of client this fork will handle */ + strlcpy(my_client, vtysh_client[i].name, + sizeof(my_client)); + my_client_type = vtysh_client[i].flag; + fork_pid = fork(); + + /* If child, break */ + if (fork_pid == 0) + break; + } + + /* parent, wait for children */ + if (fork_pid != 0) { + fprintf(stdout, + "Waiting for children to finish applying config...\n"); + while (wait(&status) > 0) + ; + return 0; + } + + /* + * children, grow up to be cowboys + */ + for (unsigned int i = 0; i < array_size(vtysh_client); i++) { + if (my_client_type != vtysh_client[i].flag) { + struct vtysh_client *cl; + + /* + * If this is a client we aren't responsible + * for, disconnect + */ + for (cl = &vtysh_client[i]; cl; cl = cl->next) { + if (cl->fd >= 0) + close(cl->fd); + cl->fd = -1; + } + } else if (vtysh_client[i].fd == -1 && + vtysh_client[i].next == NULL) { + /* + * If this is the client we are responsible + * for, but we aren't already connected to that + * client, that means the client isn't up in + * the first place and we can exit early + */ + exit(0); + } + } + + fprintf(stdout, "[%d|%s] sending configuration\n", getpid(), + my_client); + } + + ret = vtysh_read_config(config_file_path, dry_run); + + if (ret) { + if (do_fork) + fprintf(stderr, + "[%d|%s] Configuration file[%s] processing failure: %d\n", + getpid(), my_client, frr_config, ret); + else + fprintf(stderr, + "Configuration file[%s] processing failure: %d\n", + frr_config, ret); + } else if (do_fork) { + fprintf(stderr, "[%d|%s] done\n", getpid(), my_client); + exit(0); + } + + return ret; } /* We don't write vtysh specific into file from vtysh. vtysh.conf should diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 053d663245..860d79d5f9 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -345,8 +344,6 @@ int main(int argc, char **argv, char **env) char pathspace[MAXPATHLEN] = ""; const char *histfile = NULL; const char *histfile_env = getenv("VTYSH_HISTFILE"); - char my_client[64]; - int my_client_type; /* SUID: drop down to calling user & go back up when needed */ elevuid = geteuid(); @@ -497,7 +494,7 @@ int main(int argc, char **argv, char **env) /* Read vtysh configuration file before connecting to daemons. * (file may not be readable to calling user in SUID mode) */ suid_on(); - vtysh_read_config(vtysh_config, dryrun); + vtysh_apply_config(vtysh_config, dryrun, false); suid_off(); } /* Error code library system */ @@ -516,9 +513,9 @@ int main(int argc, char **argv, char **env) /* Start execution only if not in dry-run mode */ if (dryrun && !cmd) { if (inputfile) { - ret = vtysh_read_config(inputfile, dryrun); + ret = vtysh_apply_config(inputfile, dryrun, false); } else { - ret = vtysh_read_config(frr_config, dryrun); + ret = vtysh_apply_config(frr_config, dryrun, false); } exit(ret); @@ -597,10 +594,17 @@ int main(int argc, char **argv, char **env) return vtysh_write_config_integrated(); } - if (inputfile) { + if (boot_flag) + inputfile = frr_config; + + if (inputfile || boot_flag) { vtysh_flock_config(inputfile); - ret = vtysh_read_config(inputfile, dryrun); + ret = vtysh_apply_config(inputfile, dryrun, !no_fork); vtysh_unflock_config(); + + if (no_error) + ret = 0; + exit(ret); } @@ -717,106 +721,6 @@ int main(int argc, char **argv, char **env) exit(0); } - /* Boot startup configuration file. */ - if (boot_flag) { - /* - * flock config file before fork. After fork, each child will - * hold the same lock. The lock can be released by any one of - * them but they will exit without releasing the lock - the - * parent (us) will release it when they are done - */ - vtysh_flock_config(frr_config); - - /* - * In boot mode, we need to apply the whole config file to all - * daemons. Instead of having one client talk to N daemons, we - * fork N times and let each child handle one daemon. - */ - pid_t fork_pid = getpid(); - int status = 0; - - if (!no_fork) { - for (unsigned int i = 0; i < array_size(vtysh_client); - i++) { - /* Store name of client this fork will handle */ - strlcpy(my_client, vtysh_client[i].name, - sizeof(my_client)); - my_client_type = vtysh_client[i].flag; - fork_pid = fork(); - - /* If child, break */ - if (fork_pid == 0) - break; - } - - /* parent, wait for children */ - if (fork_pid != 0) { - fprintf(stdout, - "Waiting for children to finish applying config...\n"); - while (wait(&status) > 0) - ; - ret = 0; - goto boot_done; - } - - /* - * children, grow up to be cowboys - */ - for (unsigned int i = 0; i < array_size(vtysh_client); - i++) { - if (my_client_type != vtysh_client[i].flag) { - struct vtysh_client *cl; - - /* - * If this is a client we aren't - * responsible for, disconnect - */ - for (cl = &vtysh_client[i]; cl; - cl = cl->next) { - if (cl->fd >= 0) - close(cl->fd); - cl->fd = -1; - } - } else if (vtysh_client[i].fd == -1 && - vtysh_client[i].next == NULL) { - /* - * If this is the client we are - * responsible for, but we aren't - * already connected to that client, - * that means the client isn't up in the - * first place and we can exit early - */ - ret = 0; - goto boot_done; - } - } - - fprintf(stdout, "[%d|%s] sending configuration\n", - getpid(), my_client); - } - - ret = vtysh_read_config(frr_config, dryrun); - if (ret) { - if (!no_fork) - fprintf(stderr, - "[%d|%s] Configuration file[%s] processing failure: %d\n", - getpid(), my_client, frr_config, ret); - else - fprintf(stderr, - "Configuration file[%s] processing failure: %d\n", - frr_config, ret); - if (no_error) - ret = 0; - } else if (!no_fork) { - fprintf(stderr, "[%d|%s] done\n", getpid(), my_client); - } - - boot_done: - if (fork_pid != 0) - vtysh_unflock_config(); - exit(ret); - } - vtysh_readline_init(); vty_hello(vty);