MEDIUM: mworker: use the haproxy poll loop

In order to reorganize the code of the master worker, the mworker_wait()
function which was the main function was split. This function was
handling a wait() loop, but it does not need it anymore since the code
will use the poll loop of haproxy instead.

The function was split in several functions:

- mworker_catch_sigterm() which is a signal handler for SIGTERM ans
SIGUSR1 that sends the signals to the workers
- mworker_catch_sigchld() which is the code handling the leaving of a
child
- mworker_catch_sighup which basically call the mworker_restart()
function
- mworker_loop() which is the function calling the main poll loop in the
master
diff --git a/src/haproxy.c b/src/haproxy.c
index a0963a0..d40316a 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -213,6 +213,8 @@
 struct task *global_listener_queue_task;
 static struct task *manage_global_listener_queue(struct task *t, void *context, unsigned short state);
 
+static void *run_thread_poll_loop(void *data);
+
 /* bitfield of a few warnings to emit just once (WARN_*) */
 unsigned int warned = 0;
 
@@ -481,11 +483,6 @@
 	return 0;
 }
 
-static void mworker_signalhandler(int signum)
-{
-	caught_signal = signum;
-}
-
 static void mworker_block_signals()
 {
 	sigset_t set;
@@ -681,6 +678,8 @@
 		next_argv[next_argc++] = NULL;
 	}
 
+	deinit_pollers(); /* we don't want to leak the poller fd */
+
 	ha_warning("Reexecuting Master process\n");
 	execvp(next_argv[0], next_argv);
 
@@ -692,51 +691,41 @@
 	return;
 }
 
-
 /*
- * Wait for every children to exit
+ * When called, this function reexec haproxy with -sf followed by current
+ * children PIDs and possibily old children PIDs if they didn't leave yet.
  */
-
-static void mworker_wait()
+static void mworker_catch_sighup(struct sig_handler *sh)
 {
-	int exitpid = -1;
-	int status = 0;
+	mworker_reload();
+}
 
-restart_wait:
+static void mworker_catch_sigterm(struct sig_handler *sh)
+{
+	int sig = sh->arg;
 
 #if defined(USE_SYSTEMD)
-	if (global.tune.options & GTUNE_USE_SYSTEMD)
-		sd_notifyf(0, "READY=1\nMAINPID=%lu", (unsigned long)getpid());
+	if (global.tune.options & GTUNE_USE_SYSTEMD) {
+		sd_notify(0, "STOPPING=1");
+	}
 #endif
-
-	mworker_unblock_signals();
+	ha_warning("Exiting Master process...\n");
+	mworker_kill(sig);
+}
 
-	while (1) {
+/*
+ * Wait for every children to exit
+ */
 
-		while (((exitpid = wait(&status)) == -1) && errno == EINTR) {
-			int sig = caught_signal;
-			if (sig == SIGUSR2 || sig == SIGHUP) {
-				mworker_reload();
-				/* should reach there only if it fail */
-				goto restart_wait;
-			} else {
-#if defined(USE_SYSTEMD)
-				if ((global.tune.options & GTUNE_USE_SYSTEMD) && (sig == SIGUSR1 || sig == SIGTERM)) {
-					sd_notify(0, "STOPPING=1");
-				}
-#endif
-				ha_warning("Exiting Master process...\n");
-				mworker_kill(sig);
-			}
-			caught_signal = 0;
-		}
+static void mworker_catch_sigchld(struct sig_handler *sh)
+{
+	int exitpid = -1;
+	int status = 0;
 
-		if (exitpid == -1 && errno == ECHILD) {
-			ha_warning("All workers exited. Exiting... (%d)\n", status);
-			atexit_flag = 0;
-			exit(status); /* parent must leave using the latest status code known */
-		}
+restart_wait:
 
+	exitpid = waitpid(-1, &status, WNOHANG);
+	if (exitpid > 0) {
 		if (WIFEXITED(status))
 			status = WEXITSTATUS(status);
 		else if (WIFSIGNALED(status))
@@ -762,9 +751,47 @@
 				delete_oldpid(exitpid);
 			}
 		}
+		/* do it again to check if it was the last worker */
+		goto restart_wait;
 	}
+	/* Better rely on the system than on a list of process to check if it was the last one */
+	else if (exitpid == -1 && errno == ECHILD) {
+		ha_warning("All workers exited. Exiting... (%d)\n", status);
+		atexit_flag = 0;
+		exit(status); /* parent must leave using the latest status code known */
+	}
+
 }
 
+static void mworker_loop()
+{
+
+#if defined(USE_SYSTEMD)
+	if (global.tune.options & GTUNE_USE_SYSTEMD)
+		sd_notifyf(0, "READY=1\nMAINPID=%lu", (unsigned long)getpid());
+#endif
+
+	signal_register_fct(SIGTERM, mworker_catch_sigterm, SIGTERM);
+	signal_register_fct(SIGUSR1, mworker_catch_sigterm, SIGUSR1);
+	signal_register_fct(SIGINT, mworker_catch_sigterm, SIGINT);
+	signal_register_fct(SIGHUP, mworker_catch_sighup, SIGHUP);
+	signal_register_fct(SIGUSR2, mworker_catch_sighup, SIGUSR2);
+	signal_register_fct(SIGCHLD, mworker_catch_sigchld, SIGCHLD);
+
+	mworker_unblock_signals();
+	mworker_cleanlisteners();
+
+	global.nbthread = 1;
+	relative_pid = 1;
+	pid_bit = 1;
+
+	jobs++; /* this is the "master" job, we want to take care of the
+		signals even if there is no listener so the poll loop don't
+		leave */
+
+	fork_poller();
+	run_thread_poll_loop((int []){0});
+}
 
 /*
  * Reexec the process in failure mode, instead of exiting
@@ -1501,7 +1528,7 @@
 	if ((global.mode & MODE_MWORKER) && (getenv("HAPROXY_MWORKER_WAIT_ONLY") != NULL)) {
 
 		unsetenv("HAPROXY_MWORKER_WAIT_ONLY");
-		mworker_wait();
+		mworker_loop();
 	}
 
 	if ((global.mode & MODE_MWORKER) && (getenv("HAPROXY_MWORKER_REEXEC") != NULL)) {
@@ -2822,8 +2849,6 @@
 
 		if (proc == global.nbproc) {
 			if (global.mode & MODE_MWORKER) {
-				mworker_cleanlisteners();
-				deinit_pollers();
 
 				if ((!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE)) &&
 					(global.mode & MODE_DAEMON)) {
@@ -2835,7 +2860,7 @@
 					global.mode |= MODE_QUIET; /* ensure that we won't say anything from now */
 				}
 
-				mworker_wait();
+				mworker_loop();
 				/* should never get there */
 				exit(EXIT_FAILURE);
 			}