BUG/MEDIUM: mworker: fix the reload with an -- option

When HAProxy is started with a '--' option, all following parameters are
considered configuration files. You can't add new options after a '--'.

The current reload system of the master-worker adds extra options at the
end of the arguments list. Which is a problem if HAProxy was started wih
'--'.

This patch fixes the issue by copying the new option at the beginning of
the arguments list instead of the end.

This patch must be backported as far as 1.8.

(cherry picked from commit 0041741ef7253fcad2fc98f0b2a9968fb4af3574)
[wla: context edit]
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
(cherry picked from commit 29e710624583d71e7eeae63a6ea7734006a1ea57)
Signed-off-by: William Lallemand <wlallemand@haproxy.org>
diff --git a/src/haproxy.c b/src/haproxy.c
index 54cbe0a..40758e5 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -226,7 +226,7 @@
  */
 int shut_your_big_mouth_gcc_int = 0;
 
-static char **next_argv = NULL;
+static char **old_argv = NULL; /* previous argv but cleaned up */
 
 struct list proc_list = LIST_HEAD_INIT(proc_list);
 
@@ -623,7 +623,10 @@
  */
 void mworker_reload()
 {
+	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 per_thread_deinit_fct *ptdf;
@@ -667,14 +670,19 @@
 	}
 
 	/* compute length  */
-	while (next_argv[next_argc])
-		next_argc++;
+	while (old_argv[old_argc])
+		old_argc++;
 
 	/* 1 for haproxy -sf, 2 for -x /socket */
-	next_argv = realloc(next_argv, (next_argc + 1 + 2 + mworker_child_nb() + nb_oldpids + 1) * sizeof(char *));
+	next_argv = calloc(old_argc + 1 + 2 + mworker_child_nb() + nb_oldpids + 1, sizeof(char *));
 	if (next_argv == NULL)
 		goto alloc_error;
 
+	/* copy the program name */
+	next_argv[next_argc++] = old_argv[0];
+
+	/* insert the new options just after argv[0] in case we have a -- */
+
 	/* add -sf <PID>*  to argv */
 	if (mworker_child_nb() > 0) {
 		struct mworker_proc *child;
@@ -684,24 +692,21 @@
 		list_for_each_entry(child, &proc_list, list) {
 			if (!(child->options & (PROC_O_TYPE_WORKER|PROC_O_TYPE_PROG)) || child->pid <= -1 )
 				continue;
-			next_argv[next_argc] = memprintf(&msg, "%d", child->pid);
-			if (next_argv[next_argc] == NULL)
+			if ((next_argv[next_argc++] = memprintf(&msg, "%d", child->pid)) == NULL)
 				goto alloc_error;
 			msg = NULL;
-			next_argc++;
 		}
 	}
-
-	next_argv[next_argc] = NULL;
-
 	/* add the -x option with the stat socket */
 	if (cur_unixsocket) {
-
 		next_argv[next_argc++] = "-x";
 		next_argv[next_argc++] = (char *)cur_unixsocket;
-		next_argv[next_argc++] = NULL;
 	}
 
+	/* copy the previous options */
+	for (i = 1; i < old_argc; i++)
+		next_argv[next_argc++] = old_argv[i];
+
 	ha_warning("Reexecuting Master process\n");
 	signal(SIGPROF, SIG_IGN);
 	execvp(next_argv[0], next_argv);
@@ -710,6 +715,8 @@
 	return;
 
 alloc_error:
+	free(next_argv);
+	next_argv = NULL;
 	ha_warning("Failed to reexecute the master process [%d]: Cannot allocate memory\n", pid);
 	return;
 }
@@ -1515,8 +1522,8 @@
 	int ideal_maxconn;
 
 	global.mode = MODE_STARTING;
-	next_argv = copy_argv(argc, argv);
-	if (!next_argv) {
+	old_argv = copy_argv(argc, argv);
+	if (!old_argv) {
 		ha_alert("failed to copy argv.\n");
 		exit(1);
 	}