|
|
- From ff81ac47267c4e0227d1e3fbc5b1dedfd81a2a2f Mon Sep 17 00:00:00 2001
- From: Willy Tarreau <w@1wt.eu>
- Date: Tue, 25 Oct 2016 17:20:24 +0200
- Subject: [PATCH 16/26] BUG/MEDIUM: systemd: let the wrapper know that haproxy
- has completed or failed
-
- Pierre Cheynier found that there's a persistent issue with the systemd
- wrapper. Too fast reloads can lead to certain old processes not being
- signaled at all and continuing to run. The problem was tracked down as
- a race between the startup and the signal processing : nothing prevents
- the wrapper from starting new processes while others are still starting,
- and the resulting pid file will only contain the latest pids in this
- case. This can happen with large configs and/or when a lot of SSL
- certificates are involved.
-
- In order to solve this we want the wrapper to wait for the new processes
- to complete their startup. But we also want to ensure it doesn't wait for
- nothing in case of error.
-
- The solution found here is to create a pipe between the wrapper and the
- sub-processes. The wrapper waits on the pipe and the sub-processes are
- expected to close this pipe once they completed their startup. That way
- we don't queue up new processes until the previous ones have registered
- their pids to the pid file. And if anything goes wrong, the wrapper is
- immediately released. The only thing is that we need the sub-processes
- to know the pipe's file descriptor. We pass it in an environment variable
- called HAPROXY_WRAPPER_FD.
-
- It was confirmed both by Pierre and myself that this completely solves
- the "zombie" process issue so that only the new processes continue to
- listen on the sockets.
-
- It seems that in the future this stuff could be moved to the haproxy
- master process, also getting rid of an environment variable.
-
- This fix needs to be backported to 1.6 and 1.5.
- (cherry picked from commit b957109727f7fed556c049d40bacf45f0df211db)
- ---
- src/haproxy-systemd-wrapper.c | 36 ++++++++++++++++++++++++++++++++++++
- src/haproxy.c | 10 ++++++++++
- 2 files changed, 46 insertions(+)
-
- diff --git a/src/haproxy-systemd-wrapper.c b/src/haproxy-systemd-wrapper.c
- index f4fcab1..b426f96 100644
- --- a/src/haproxy-systemd-wrapper.c
- +++ b/src/haproxy-systemd-wrapper.c
- @@ -65,16 +65,30 @@ static void locate_haproxy(char *buffer, size_t buffer_size)
- return;
- }
-
- +/* Note: this function must not exit in case of error (except in the child), as
- + * it is only dedicated the starting a new haproxy process. By keeping the
- + * process alive it will ensure that future signal delivery may get rid of
- + * the issue. If the first startup fails, the wrapper will notice it and
- + * return an error thanks to wait() returning ECHILD.
- + */
- static void spawn_haproxy(char **pid_strv, int nb_pid)
- {
- char haproxy_bin[512];
- pid_t pid;
- int main_argc;
- char **main_argv;
- + int pipefd[2];
- + char fdstr[20];
- + int ret;
-
- main_argc = wrapper_argc - 1;
- main_argv = wrapper_argv + 1;
-
- + if (pipe(pipefd) != 0) {
- + fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to create a pipe, please try again later.\n");
- + return;
- + }
- +
- pid = fork();
- if (!pid) {
- char **argv;
- @@ -89,6 +103,15 @@ static void spawn_haproxy(char **pid_strv, int nb_pid)
- }
-
- reset_signal_handler();
- +
- + close(pipefd[0]); /* close the read side */
- +
- + snprintf(fdstr, sizeof(fdstr), "%d", pipefd[1]);
- + if (setenv("HAPROXY_WRAPPER_FD", fdstr, 1) != 0) {
- + fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to setenv(), please try again later.\n");
- + exit(1);
- + }
- +
- locate_haproxy(haproxy_bin, 512);
- argv[argno++] = haproxy_bin;
- for (i = 0; i < main_argc; ++i)
- @@ -113,6 +136,19 @@ static void spawn_haproxy(char **pid_strv, int nb_pid)
- else if (pid == -1) {
- fprintf(stderr, SD_NOTICE "haproxy-systemd-wrapper: failed to fork(), please try again later.\n");
- }
- +
- + /* The parent closes the write side and waits for the child to close it
- + * as well. Also deal the case where the fd would unexpectedly be 1 or 2
- + * by silently draining all data.
- + */
- + close(pipefd[1]);
- +
- + do {
- + char c;
- + ret = read(pipefd[0], &c, sizeof(c));
- + } while ((ret > 0) || (ret == -1 && errno == EINTR));
- + /* the child has finished starting up */
- + close(pipefd[0]);
- }
-
- static int read_pids(char ***pid_strv)
- diff --git a/src/haproxy.c b/src/haproxy.c
- index a657dc4..2d476f2 100644
- --- a/src/haproxy.c
- +++ b/src/haproxy.c
- @@ -1843,6 +1843,7 @@ int main(int argc, char **argv)
- int ret = 0;
- int *children = calloc(global.nbproc, sizeof(int));
- int proc;
- + char *wrapper_fd;
-
- /* the father launches the required number of processes */
- for (proc = 0; proc < global.nbproc; proc++) {
- @@ -1879,6 +1880,15 @@ int main(int argc, char **argv)
- close(pidfd);
- }
-
- + /* each child must notify the wrapper that it's ready by closing the requested fd */
- + wrapper_fd = getenv("HAPROXY_WRAPPER_FD");
- + if (wrapper_fd) {
- + int pipe_fd = atoi(wrapper_fd);
- +
- + if (pipe_fd >= 0)
- + close(pipe_fd);
- + }
- +
- /* We won't ever use this anymore */
- free(oldpids); oldpids = NULL;
- free(global.chroot); global.chroot = NULL;
- --
- 2.7.3
-
|