diff --git a/include/haproxy/global.h b/include/haproxy/global.h index dcd724a30..72370c62d 100644 --- a/include/haproxy/global.h +++ b/include/haproxy/global.h @@ -51,6 +51,8 @@ extern unsigned char boot_seed[20]; // per-boot random seed (160 bits initially extern THREAD_LOCAL struct buffer trash; extern char **init_env; extern char *progname; +extern char **old_argv; +extern const char *old_unixsocket; struct proxy; struct server; @@ -68,7 +70,6 @@ int compare_current_version(const char *version); void display_version(); void mworker_accept_wrapper(int fd); -void mworker_reload(int hardreload); /* to be used with warned and WARN_* */ static inline int already_warned(unsigned int warning) diff --git a/include/haproxy/mworker.h b/include/haproxy/mworker.h index 29c05d6d0..fbfcdd524 100644 --- a/include/haproxy/mworker.h +++ b/include/haproxy/mworker.h @@ -14,6 +14,7 @@ #ifndef _HAPROXY_MWORKER_H_ #define _HAPROXY_MWORKER_H_ +#include #include #include diff --git a/src/haproxy.c b/src/haproxy.c index 37917c087..ca31b8958 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -240,7 +240,7 @@ static int *oldpids = NULL; int oldpids_sig; /* use USR1 or TERM */ /* Path to the unix socket we use to retrieve listener sockets from the old process */ -static const char *old_unixsocket; +const char *old_unixsocket; int atexit_flag = 0; @@ -253,7 +253,7 @@ char hostname[MAX_HOSTNAME_LEN]; char *localpeer = NULL; static char *kwd_dump = NULL; // list of keyword dumps to produce -static char **old_argv = NULL; /* previous argv but cleaned up */ +char **old_argv = NULL; /* previous argv but cleaned up */ struct list proc_list = LIST_HEAD_INIT(proc_list); @@ -703,178 +703,6 @@ int delete_oldpid(int pid) return 0; } - -/* - * When called, this function reexec haproxy with -sf followed by current - * children PIDs and possibly old children PIDs if they didn't leave yet. - */ -static void mworker_reexec(int hardreload) -{ - char **next_argv = NULL; - int old_argc = 0; /* previous number of argument */ - int next_argc = 0; - int i = 0; - char *msg = NULL; - struct rlimit limit; - struct mworker_proc *current_child = NULL; - int x_off = 0; /* disable -x by putting -x /dev/null */ - - mworker_block_signals(); - - /* restore initial environment (before parsing the config) and do re-exec. - * The initial process environment should be restored here, preceded by - * clean_env(), which do the same job as clearenv(). - * Otherwise, after the re-exec we will start the new worker in the - * environment modified by '*env' keywords from the previous configuration, - * i.e. existed before the reload. - */ - if (clean_env() != 0) { - ha_alert("Master encountered a non-recoverable error, exiting.\n"); - exit(EXIT_FAILURE); - } - - if (restore_env() != 0) { - ha_alert("Master encountered a non-recoverable error, exiting.\n"); - exit(EXIT_FAILURE); - } - - setenv("HAPROXY_MWORKER_REEXEC", "1", 1); - - mworker_proc_list_to_env(); /* put the children description in the env */ - - /* during the reload we must ensure that every FDs that can't be - * reuse (ie those that are not referenced in the proc_list) - * are closed or they will leak. */ - - /* close the listeners FD */ - mworker_cli_proxy_stop(); - - if (fdtab) - deinit_pollers(); - -#ifdef HAVE_SSL_RAND_KEEP_RANDOM_DEVICES_OPEN - /* close random device FDs */ - RAND_keep_random_devices_open(0); -#endif - - /* restore the initial FD limits */ - limit.rlim_cur = rlim_fd_cur_at_boot; - limit.rlim_max = rlim_fd_max_at_boot; - if (raise_rlim_nofile(&limit, &limit) != 0) { - ha_warning("Failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n", - rlim_fd_cur_at_boot, rlim_fd_max_at_boot, - (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max); - } - - /* compute length */ - while (old_argv[old_argc]) - old_argc++; - - /* 1 for haproxy -sf, 2 for -x /socket */ - next_argv = calloc(old_argc + 1 + 2 + mworker_child_nb() + 1, - sizeof(*next_argv)); - if (next_argv == NULL) - goto alloc_error; - - /* copy the program name */ - next_argv[next_argc++] = old_argv[0]; - - /* we need to reintroduce /dev/null every time */ - if (old_unixsocket && strcmp(old_unixsocket, "/dev/null") == 0) - x_off = 1; - - /* insert the new options just after argv[0] in case we have a -- */ - - /* add -sf * to argv */ - if (mworker_child_nb() > 0) { - struct mworker_proc *child; - - if (hardreload) - next_argv[next_argc++] = "-st"; - else - next_argv[next_argc++] = "-sf"; - - list_for_each_entry(child, &proc_list, list) { - if (!(child->options & PROC_O_LEAVING) && (child->options & PROC_O_TYPE_WORKER)) - current_child = child; - - if (!(child->options & (PROC_O_TYPE_WORKER|PROC_O_TYPE_PROG)) || child->pid <= -1) - continue; - if ((next_argv[next_argc++] = memprintf(&msg, "%d", child->pid)) == NULL) - goto alloc_error; - msg = NULL; - } - } - if (!x_off && current_child) { - /* add the -x option with the socketpair of the current worker */ - next_argv[next_argc++] = "-x"; - if ((next_argv[next_argc++] = memprintf(&msg, "sockpair@%d", current_child->ipc_fd[0])) == NULL) - goto alloc_error; - msg = NULL; - } - - if (x_off) { - /* if the cmdline contained a -x /dev/null, continue to use it */ - next_argv[next_argc++] = "-x"; - next_argv[next_argc++] = "/dev/null"; - } - - /* copy the previous options */ - for (i = 1; i < old_argc; i++) - next_argv[next_argc++] = old_argv[i]; - - /* need to withdraw MODE_STARTING from master, because we have to free - * the startup logs ring here, see more details in print_message() - */ - global.mode &= ~MODE_STARTING; - startup_logs_free(startup_logs); - - signal(SIGPROF, SIG_IGN); - execvp(next_argv[0], next_argv); - ha_warning("Failed to reexecute the master process [%d]: %s\n", pid, strerror(errno)); - ha_free(&next_argv); - return; - -alloc_error: - ha_free(&next_argv); - ha_warning("Failed to reexecute the master process [%d]: Cannot allocate memory\n", pid); - return; -} - -/* reload haproxy and emit a warning */ -void mworker_reload(int hardreload) -{ - struct mworker_proc *child; - struct per_thread_deinit_fct *ptdf; - - ha_notice("Reloading HAProxy%s\n", hardreload?" (hard-reload)":""); - - /* close the poller FD and the thread waker pipe FD */ - list_for_each_entry(ptdf, &per_thread_deinit_list, list) - ptdf->fct(); - - /* increment the number of reloads, child->reloads is checked in - * mworker_env_to_proc_list() (after reload) in order to set - * PROC_O_LEAVING flag for the process - */ - list_for_each_entry(child, &proc_list, list) { - child->reloads++; - } - - if (global.tune.options & GTUNE_USE_SYSTEMD) { - struct timespec ts; - - (void)clock_gettime(CLOCK_MONOTONIC, &ts); - - sd_notifyf(0, - "RELOADING=1\n" - "STATUS=Reloading Configuration.\n" - "MONOTONIC_USEC=%" PRIu64 "\n", - (ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL)); - } - mworker_reexec(hardreload); -} - /* * Exit with an error message upon a master recovery mode failure. */ diff --git a/src/mworker.c b/src/mworker.c index 8d7d84b93..0d974468a 100644 --- a/src/mworker.c +++ b/src/mworker.c @@ -294,6 +294,177 @@ void mworker_broadcast_signal(struct sig_handler *sh) mworker_kill(sh->arg); } +/* + * When called, this function reexec haproxy with -sf followed by current + * children PIDs and possibly old children PIDs if they didn't leave yet. + */ +static void mworker_reexec(int hardreload) +{ + char **next_argv = NULL; + int old_argc = 0; /* previous number of argument */ + int next_argc = 0; + int i = 0; + char *msg = NULL; + struct rlimit limit; + struct mworker_proc *current_child = NULL; + int x_off = 0; /* disable -x by putting -x /dev/null */ + + mworker_block_signals(); + + /* restore initial environment (before parsing the config) and do re-exec. + * The initial process environment should be restored here, preceded by + * clean_env(), which do the same job as clearenv(). + * Otherwise, after the re-exec we will start the new worker in the + * environment modified by '*env' keywords from the previous configuration, + * i.e. existed before the reload. + */ + if (clean_env() != 0) { + ha_alert("Master encountered a non-recoverable error, exiting.\n"); + exit(EXIT_FAILURE); + } + + if (restore_env() != 0) { + ha_alert("Master encountered a non-recoverable error, exiting.\n"); + exit(EXIT_FAILURE); + } + + setenv("HAPROXY_MWORKER_REEXEC", "1", 1); + + mworker_proc_list_to_env(); /* put the children description in the env */ + + /* during the reload we must ensure that every FDs that can't be + * reuse (ie those that are not referenced in the proc_list) + * are closed or they will leak. */ + + /* close the listeners FD */ + mworker_cli_proxy_stop(); + + if (fdtab) + deinit_pollers(); + +#ifdef HAVE_SSL_RAND_KEEP_RANDOM_DEVICES_OPEN + /* close random device FDs */ + RAND_keep_random_devices_open(0); +#endif + + /* restore the initial FD limits */ + limit.rlim_cur = rlim_fd_cur_at_boot; + limit.rlim_max = rlim_fd_max_at_boot; + if (raise_rlim_nofile(&limit, &limit) != 0) { + ha_warning("Failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n", + rlim_fd_cur_at_boot, rlim_fd_max_at_boot, + (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max); + } + + /* compute length */ + while (old_argv[old_argc]) + old_argc++; + + /* 1 for haproxy -sf, 2 for -x /socket */ + next_argv = calloc(old_argc + 1 + 2 + mworker_child_nb() + 1, + sizeof(*next_argv)); + if (next_argv == NULL) + goto alloc_error; + + /* copy the program name */ + next_argv[next_argc++] = old_argv[0]; + + /* we need to reintroduce /dev/null every time */ + if (old_unixsocket && strcmp(old_unixsocket, "/dev/null") == 0) + x_off = 1; + + /* insert the new options just after argv[0] in case we have a -- */ + + /* add -sf * to argv */ + if (mworker_child_nb() > 0) { + struct mworker_proc *child; + + if (hardreload) + next_argv[next_argc++] = "-st"; + else + next_argv[next_argc++] = "-sf"; + + list_for_each_entry(child, &proc_list, list) { + if (!(child->options & PROC_O_LEAVING) && (child->options & PROC_O_TYPE_WORKER)) + current_child = child; + + if (!(child->options & (PROC_O_TYPE_WORKER|PROC_O_TYPE_PROG)) || child->pid <= -1) + continue; + if ((next_argv[next_argc++] = memprintf(&msg, "%d", child->pid)) == NULL) + goto alloc_error; + msg = NULL; + } + } + if (!x_off && current_child) { + /* add the -x option with the socketpair of the current worker */ + next_argv[next_argc++] = "-x"; + if ((next_argv[next_argc++] = memprintf(&msg, "sockpair@%d", current_child->ipc_fd[0])) == NULL) + goto alloc_error; + msg = NULL; + } + + if (x_off) { + /* if the cmdline contained a -x /dev/null, continue to use it */ + next_argv[next_argc++] = "-x"; + next_argv[next_argc++] = "/dev/null"; + } + + /* copy the previous options */ + for (i = 1; i < old_argc; i++) + next_argv[next_argc++] = old_argv[i]; + + /* need to withdraw MODE_STARTING from master, because we have to free + * the startup logs ring here, see more details in print_message() + */ + global.mode &= ~MODE_STARTING; + startup_logs_free(startup_logs); + + signal(SIGPROF, SIG_IGN); + execvp(next_argv[0], next_argv); + ha_warning("Failed to reexecute the master process [%d]: %s\n", pid, strerror(errno)); + ha_free(&next_argv); + return; + +alloc_error: + ha_free(&next_argv); + ha_warning("Failed to reexecute the master process [%d]: Cannot allocate memory\n", pid); + return; +} + +/* reload haproxy and emit a warning */ +static void mworker_reload(int hardreload) +{ + struct mworker_proc *child; + struct per_thread_deinit_fct *ptdf; + + ha_notice("Reloading HAProxy%s\n", hardreload?" (hard-reload)":""); + + /* close the poller FD and the thread waker pipe FD */ + list_for_each_entry(ptdf, &per_thread_deinit_list, list) + ptdf->fct(); + + /* increment the number of reloads, child->reloads is checked in + * mworker_env_to_proc_list() (after reload) in order to set + * PROC_O_LEAVING flag for the process + */ + list_for_each_entry(child, &proc_list, list) { + child->reloads++; + } + + if (global.tune.options & GTUNE_USE_SYSTEMD) { + struct timespec ts; + + (void)clock_gettime(CLOCK_MONOTONIC, &ts); + + sd_notifyf(0, + "RELOADING=1\n" + "STATUS=Reloading Configuration.\n" + "MONOTONIC_USEC=%" PRIu64 "\n", + (ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL)); + } + mworker_reexec(hardreload); +} + /* * When called, this function reexec haproxy with -sf followed by current * children PIDs and possibly old children PIDs if they didn't leave yet.