MEDIUM: listener: parse the new "process" bind keyword

This sets the bind_proc entry in the bind_conf config block. For now it's
still unused, but the doc was updated.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e178678..75c17be 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1865,6 +1865,9 @@
   Please note that 'all' really means all processes regardless of the machine's
   word size, and is not limited to the first 32 or 64.
 
+  Each "bind" line may further be limited to a subset of the proxy's processes,
+  please consult the "process" bind keyword in section 5.1.
+
   If some backends are referenced by frontends bound to other processes, the
   backend automatically inherits the frontend's processes.
 
@@ -1885,7 +1888,7 @@
             bind 10.0.0.4:80
             bind-process 1-4
 
-  See also : "nbproc" in global section.
+  See also : "nbproc" in global section, and "process" in section 5.1.
 
 
 block { if | unless } <condition>
@@ -8273,6 +8276,17 @@
   enabled (check with haproxy -vv). Note that the NPN extension has been
   replaced with the ALPN extension (see the "alpn" keyword).
 
+process [ all | odd | even | <number 1-64>[-<number 1-64>] ]
+  This restricts the list of processes on which this listener is allowed to
+  run. It does not enforce any process but eliminates those which do not match.
+  If the frontend uses a "bind-process" setting, the intersection between the
+  two is applied. If in the end the listener is not allowed to run on any
+  remaining process, a warning is emitted, and the listener will either run on
+  the first process of the listener if a single process was specified, or on
+  all of its processes if multiple processes were specified. For the unlikely
+  case where several ranges are needed, this directive may be repeated. See
+  also "bind-process" and "nbproc".
+
 ssl
   This setting is only available when support for OpenSSL was built in. It
   enables SSL deciphering on connections instantiated from this listener. A
diff --git a/include/types/listener.h b/include/types/listener.h
index 47cb185..83b63af 100644
--- a/include/types/listener.h
+++ b/include/types/listener.h
@@ -134,6 +134,7 @@
 	struct eb_root sni_w_ctx;  /* sni_ctx tree of all known certs wildcards sorted by name */
 #endif
 	int is_ssl;                /* SSL is required for these listeners */
+	unsigned long bind_proc;   /* bitmask of processes allowed to use these listeners */
 	struct {                   /* UNIX socket permissions */
 		uid_t uid;         /* -1 to leave unchanged */
 		gid_t gid;         /* -1 to leave unchanged */
diff --git a/src/listener.c b/src/listener.c
index 4a55e5a..9032a87 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -11,6 +11,7 @@
  */
 
 #define _GNU_SOURCE
+#include <ctype.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -703,6 +704,49 @@
 	return 0;
 }
 
+/* parse the "process" bind keyword */
+static int bind_parse_process(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
+{
+	unsigned long set = 0;
+	unsigned int low, high;
+
+	if (strcmp(args[cur_arg + 1], "all") == 0) {
+		set = 0;
+	}
+	else if (strcmp(args[cur_arg + 1], "odd") == 0) {
+		set |= ~0UL/3UL; /* 0x555....555 */
+	}
+	else if (strcmp(args[cur_arg + 1], "even") == 0) {
+		set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
+	}
+	else if (isdigit((int)*args[cur_arg + 1])) {
+		char *dash = strchr(args[cur_arg + 1], '-');
+
+		low = high = str2uic(args[cur_arg + 1]);
+		if (dash)
+			high = str2uic(dash + 1);
+
+		if (high < low) {
+			unsigned int swap = low;
+			low = high;
+			high = swap;
+		}
+
+		if (low < 1 || high > LONGBITS) {
+			memprintf(err, "'%s' : invalid range %d-%d, allowed range is 1..%d", args[cur_arg], low, high, LONGBITS);
+			return ERR_ALERT | ERR_FATAL;
+		}
+		while (low <= high)
+			set |= 1UL << (low++ - 1);
+	}
+	else {
+		memprintf(err, "'%s' expects 'all', 'odd', 'even', or a process range with numbers from 1 to %d.", args[cur_arg], LONGBITS);
+		return ERR_ALERT | ERR_FATAL;
+	}
+
+	conf->bind_proc = set;
+	return 0;
+}
 
 /* Note: must not be declared <const> as its list will be overwritten.
  * Please take care of keeping this list alphabetically sorted.
@@ -734,6 +778,7 @@
 	{ "maxconn",      bind_parse_maxconn,      1 }, /* set maxconn of listening socket */
 	{ "name",         bind_parse_name,         1 }, /* set name of listening socket */
 	{ "nice",         bind_parse_nice,         1 }, /* set nice of listening socket */
+	{ "process",      bind_parse_process,      1 }, /* set list of allowed process for this socket */
 	{ /* END */ },
 }};