This behavior allows to reload HAProxy using the master works mode.

It's inherited from the systemd wrapper, when the SIGUSR2 signal is
received, the master process will reexecute itself with the -sf flag
followed by the PIDs of the children.
---
 src/haproxy.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 130 insertions(+), 5 deletions(-)

diff --git a/src/haproxy.c b/src/haproxy.c
index 8c73613..d2b5d4c 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -193,6 +193,11 @@ char localpeer[MAX_HOSTNAME_LEN];
  */
 int shut_your_big_mouth_gcc_int = 0;
 
+int *children; /* store PIDs of children in master workers mode */
+
+static volatile sig_atomic_t caught_signal = 0;
+static char **next_argv = NULL;
+
 /* list of the temporarily limited listeners because of lack of resource */
 struct list global_listener_queue = LIST_HEAD_INIT(global_listener_queue);
 struct task *global_listener_queue_task;
@@ -387,6 +392,80 @@ static void usage(char *name)
 /*   more specific functions   ***************************************/
 /*********************************************************************/
 
+/* return 1 if a pid is a current child otherwise 0 */
+
+int current_child(int pid)
+{
+       int i;
+
+       for (i = 0; i < global.nbproc; i++) {
+               if (children[i] == pid)
+                       return 1;
+       }
+       return 0;
+}
+
+
+/*
+ * When called, this function add -sf followed by current children PIDs and
+ * possibily old children PIDs if they didn't leave yet.
+ */
+
+
+static void master_reload()
+{
+       int next_argc = 0;
+       int j;
+
+       signal(SIGUSR1, SIG_IGN);
+       signal(SIGUSR2, SIG_IGN);
+       signal(SIGHUP,  SIG_IGN);
+       signal(SIGINT,  SIG_DFL);
+       signal(SIGTERM, SIG_DFL);
+
+       /* compute length  */
+       while (next_argv[next_argc])
+               next_argc++;
+
+       /* 1 for haproxy -sf */
+       next_argv = realloc(next_argv, (next_argc + 1 + global.nbproc + 1) * 
sizeof(char *));
+       if (next_argv == NULL)
+               goto alloc_error;
+
+       if (children) {
+               char *msg = NULL;
+
+               /* add -sf <PID>*  to argv */
+               next_argv[next_argc++] = "-sf";
+               for (j = 0; j < global.nbproc; next_argc++,j++) {
+                       next_argv[next_argc] = memprintf(&msg, "%d", 
children[j]);
+                       if (next_argv[next_argc] == NULL)
+                               goto alloc_error;
+                       msg = NULL;
+               }
+               next_argv[next_argc] = NULL;
+       }
+       Warning("Reexecuting Master process [%d]\n", pid);
+       execv(next_argv[0], next_argv);
+
+alloc_error:
+       Warning("Cannot allocate memory\n");
+       Warning("Failed to reexecute the master processs [%d]\n", pid);
+       return;
+}
+
+/*
+ * In master-workers mode, SIGUSR2 on the master will reexec itself to ensure
+ * that the binary is up to date in memory, launch new processes and kill the
+ * old ones.
+ */
+
+static void sig_master_usr2(int signum)
+{
+       Warning("Received signal USR2, reloading...\n");
+       caught_signal = signum;
+}
+
 /*
  * upon SIGUSR1, let's have a soft stop. Note that soft_stop() broadcasts
  * a signal zero to all subscribers. This means that it's as easy as
@@ -776,6 +855,39 @@ out:
 }
 
 /*
+ * copy and cleanup the current argv
+ * Remove the -sf /-st parameters
+ * Return an allocated copy of argv
+ */
+
+static char **copy_argv(int argc, char **argv)
+{
+       char **newargv;
+       int i, j;
+
+       newargv = calloc(argc + 1, sizeof(char *));
+       if (newargv == NULL) {
+               Warning("Cannot allocate memory\n");
+               return NULL;
+       }
+
+       for (i = 0, j = 0; i < argc; i++, j++) {
+               char *flag = *(argv + i) + 1;
+
+               /* -sf or -st */
+               if (*flag == 's' && (flag[1] == 'f' || flag[1] == 't')) {
+                       /* list of pids to finish ('f') or terminate ('t') */
+                       i++;
+                       while (i < argc && argv[i][0] != '-') {
+                               i++;
+                       }
+               }
+               newargv[j] = argv[i];
+       }
+       return newargv;
+}
+
+/*
  * This function initializes all the necessary variables. It only returns
  * if everything is OK. If something fails, it exits.
  */
@@ -792,6 +904,8 @@ static void init(int argc, char **argv)
        struct proxy *px;
        struct post_check_fct *pcf;
 
+       next_argv = copy_argv(argc, argv);
+
        chunk_init(&trash, malloc(global.tune.bufsize), global.tune.bufsize);
        alloc_trash_buffers(global.tune.bufsize);
 
@@ -2099,10 +2213,10 @@ int main(int argc, char **argv)
                struct proxy *px;
                struct peers *curpeers;
                int ret = 0;
-               int *children = calloc(global.nbproc, sizeof(int));
                int proc;
                char *wrapper_fd;
 
+               children = calloc(global.nbproc, sizeof(int));
                /*
                 * if daemon + mworker: must fork here to let a master
                 * process live in background before forking children
@@ -2169,11 +2283,24 @@ int main(int argc, char **argv)
 
                if (proc == global.nbproc) {
                        if (global.mode & MODE_MWORKER) {
-                               int i;
+                               struct sigaction sa;
+
+                                /* Here we are not using the haproxy async way
+                                 for signals because it does not exists in
+                                 the master */
+                               memset(&sa, 0, sizeof(struct sigaction));
+                               sa.sa_handler = &sig_master_usr2;
+                               sigaction(SIGUSR2, &sa, NULL);
 
                                protocol_unbind_all();
                                for (proc = 0; proc < global.nbproc; proc++)
-                                       while (waitpid(-1, NULL, 0) == -1 && 
errno == EINTR);
+                                       while (waitpid(-1, NULL, 0) == -1 && 
errno == EINTR) {
+                                               int sig = caught_signal;
+                                               if (sig) {
+                                                       caught_signal = 0;
+                                                       master_reload();
+                                               }
+                                       }
                        }
                        exit(0); /* parent must leave */
                }
@@ -2245,8 +2372,6 @@ int main(int argc, char **argv)
                        curpeers->peers_fe = NULL;
                }
 
-               free(children);
-               children = NULL;
                /* if we're NOT in QUIET mode, we should now close the 3 first 
FDs to ensure
                 * that we can detach from the TTY. We MUST NOT do it in other 
cases since
                 * it would have already be done, and 0-2 would have been 
affected to listening
-- 
2.10.2


Reply via email to