MINOR: mworker: support a configurable maximum number of reloads

This patch implements a new global parameter for the master-worker mode.
When setting the mworker-max-reloads value, a worker receive a SIGTERM
if its number of reloads is greater than this value.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index f2954a8..31aac44 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -571,6 +571,7 @@
    - log-tag
    - log-send-hostname
    - lua-load
+   - mworker-max-reloads
    - nbproc
    - nbthread
    - node
@@ -966,6 +967,13 @@
 
   See also "-W" in the management guide.
 
+mworker-max-reloads <number>
+  In master-worker mode, this option limits the number of time a worker can
+  survive to a reload. If the worker did not left after a reload, once its
+  number of reloads is greater than this number, the worker will receive a
+  SIGTERM. This option helps to keep under control the number of workers.
+  See also "show proc" in the Management Guide.
+
 nbproc <number>
   Creates <number> processes when going daemon. This requires the "daemon"
   mode. By default, only one process is created, which is the recommended mode
diff --git a/include/proto/mworker.h b/include/proto/mworker.h
index bf8317f..86f0904 100644
--- a/include/proto/mworker.h
+++ b/include/proto/mworker.h
@@ -35,5 +35,6 @@
 
 int mworker_ext_launch_all();
 
+void mworker_kill_max_reloads(int sig);
 
 #endif /* PROTO_MWORKER_H_ */
diff --git a/src/haproxy.c b/src/haproxy.c
index 8ab0c2e..4c37125 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2780,6 +2780,10 @@
 	if (nb_oldpids && !(global.mode & MODE_MWORKER_WAIT))
 		nb_oldpids = tell_old_pids(oldpids_sig);
 
+	/* send a SIGTERM to workers who have a too high reloads number  */
+	if ((global.mode & MODE_MWORKER) && !(global.mode & MODE_MWORKER_WAIT))
+		mworker_kill_max_reloads(SIGTERM);
+
 	if ((getenv("HAPROXY_MWORKER_REEXEC") == NULL)) {
 		nb_oldpids = 0;
 		free(oldpids);
diff --git a/src/mworker.c b/src/mworker.c
index abc67bd..8df748d 100644
--- a/src/mworker.c
+++ b/src/mworker.c
@@ -17,6 +17,7 @@
 #include <string.h>
 #include <sys/wait.h>
 
+#include <common/cfgparse.h>
 #include <common/initcall.h>
 #include <common/mini-clist.h>
 
@@ -41,6 +42,7 @@
 #endif
 
 static int exitcode = -1;
+static int max_reloads = -1; /* number max of reloads a worker can have until they are killed */
 
 /* ----- children processes handling ----- */
 
@@ -59,6 +61,16 @@
 	}
 }
 
+void mworker_kill_max_reloads(int sig)
+{
+	struct mworker_proc *child;
+
+	list_for_each_entry(child, &proc_list, list) {
+		if (max_reloads != -1 && (child->options & PROC_O_TYPE_WORKER) &&
+		    (child->pid > 0) && (child->reloads > max_reloads))
+			kill(child->pid, sig);
+	}
+}
 
 /* return 1 if a pid is a current child otherwise 0 */
 int mworker_current_child(int pid)
@@ -515,6 +527,41 @@
 }
 
 
+static int mworker_parse_global_max_reloads(char **args, int section_type, struct proxy *curpx,
+           struct proxy *defpx, const char *file, int linenum, char **err)
+{
+
+	int err_code = 0;
+
+	if (alertif_too_many_args(1, file, linenum, args, &err_code))
+		goto out;
+
+	if (*(args[1]) == 0) {
+		memprintf(err, "%sparsing [%s:%d] : '%s' expects an integer argument.\n", *err, file, linenum, args[0]);
+		err_code |= ERR_ALERT | ERR_FATAL;
+		goto out;
+	}
+
+	max_reloads = atol(args[1]);
+	if (max_reloads < 0) {
+		memprintf(err, "%sparsing [%s:%d] '%s' : invalid value %d, must be >= 0", *err, file, linenum, args[0], max_reloads);
+		err_code |= ERR_ALERT | ERR_FATAL;
+		goto out;
+	}
+
+out:
+	return err_code;
+}
+
+
+static struct cfg_kw_list mworker_kws = {{ }, {
+	{ CFG_GLOBAL, "mworker-max-reloads", mworker_parse_global_max_reloads },
+	{ 0, NULL, NULL },
+}};
+
+INITCALL1(STG_REGISTER, cfg_register_keywords, &mworker_kws);
+
+
 /* register cli keywords */
 static struct cli_kw_list cli_kws = {{ },{
 	{ { "@<relative pid>", NULL }, "@<relative pid> : send a command to the <relative pid> process", NULL, cli_io_handler_show_proc, NULL, NULL, ACCESS_MASTER_ONLY},