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 <qlyoung@nvidia.com>
This commit is contained in:
Quentin Young 2021-11-19 16:03:02 -05:00 committed by Donald Sharp
parent 449f66552f
commit ac76809027
4 changed files with 108 additions and 115 deletions

View File

@ -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");

View File

@ -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 *);

View File

@ -4,6 +4,7 @@
*/
#include <zebra.h>
#include <sys/wait.h>
#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

View File

@ -7,7 +7,6 @@
#include <sys/un.h>
#include <setjmp.h>
#include <sys/wait.h>
#include <pwd.h>
#include <sys/file.h>
#include <unistd.h>
@ -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);