MINOR: config: Add auto-increment feature for cpu-map

The prefix "auto:" can be added before the process set to let HAProxy
automatically bind a process to a CPU by incrementing process and CPU sets. To
be valid, both sets must have the same size. No matter the declaration order of
the CPU sets, it will be bound from the lower to the higher bound.

  Examples:
      # all these lines bind the process 1 to the cpu 0, the process 2 to cpu 1
      #  and so on.
      cpu-map auto:1-4   0-3
      cpu-map auto:1-4   0-1 2-3
      cpu-map auto:1-4   3 2 1 0

      # bind each process to exaclty one CPU using all/odd/even keyword
      cpu-map auto:all   0-63
      cpu-map auto:even  0-31
      cpu-map auto:odd   32-63

      # invalid cpu-map because process and CPU sets have different sizes.
      cpu-map auto:1-4   0    # invalid
      cpu-map auto:1     0-3  # invalid
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 8305515..664c05d 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -657,7 +657,7 @@
   with superuser privileges. It is important to ensure that <jail_dir> is both
   empty and unwritable to anyone.
 
-cpu-map <"all"|"odd"|"even"|process_num[-process_num]> <cpu-set>...
+cpu-map [auto:]<"all"|"odd"|"even"|process_num[-process_num]> <cpu-set>...
   On Linux 2.6 and above, it is possible to bind a process to a specific CPU
   set. This means that the process will never run on other CPUs. The "cpu-map"
   directive specifies CPU sets for process sets. The first argument is the
@@ -673,6 +673,28 @@
   of them.  Obviously, multiple "cpu-map" directives may be specified. Each
   "cpu-map" directive will replace the previous ones when they overlap.
 
+  The prefix "auto:" can be added before the process set to let HAProxy
+  automatically bind a process to a CPU by incrementing process and CPU
+  sets. To be valid, both sets must have the same size. No matter the
+  declaration order of the CPU sets, it will be bound from the lower to the
+  higher bound.
+
+  Examples:
+      # all these lines bind the process 1 to the cpu 0, the process 2 to cpu 1
+      #  and so on.
+      cpu-map auto:1-4   0-3
+      cpu-map auto:1-4   0-1 2-3
+      cpu-map auto:1-4   3 2 1 0
+
+      # bind each process to exaclty one CPU using all/odd/even keyword
+      cpu-map auto:all   0-63
+      cpu-map auto:even  0-31
+      cpu-map auto:odd   32-63
+
+      # invalid cpu-map because process and CPU sets have different sizes.
+      cpu-map auto:1-4   0    # invalid
+      cpu-map auto:1     0-3  # invalid
+
 crt-base <dir>
   Assigns a default directory to fetch SSL certificates from when a relative
   path is used with "crtfile" directives. Absolute locations specified after
diff --git a/include/common/cfgparse.h b/include/common/cfgparse.h
index 7332bd5..1ce7251 100644
--- a/include/common/cfgparse.h
+++ b/include/common/cfgparse.h
@@ -86,7 +86,7 @@
 int too_many_args(int maxarg, char **args, char **msg, int *err_code);
 int alertif_too_many_args_idx(int maxarg, int index, const char *file, int linenum, char **args, int *err_code);
 int alertif_too_many_args(int maxarg, const char *file, int linenum, char **args, int *err_code);
-int parse_process_number(const char *arg, unsigned long *proc, char **err);
+int parse_process_number(const char *arg, unsigned long *proc, int *autoinc, char **err);
 
 /*
  * Sends a warning if proxy <proxy> does not have at least one of the
diff --git a/src/cfgparse.c b/src/cfgparse.c
index d4cfa86..a7d5753 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -596,8 +596,16 @@
  * Note: this function can also be used to parse a thread number or a set of
  * threads.
  */
-int parse_process_number(const char *arg, unsigned long *proc, char **err)
+int parse_process_number(const char *arg, unsigned long *proc, int *autoinc, char **err)
 {
+	if (autoinc) {
+		*autoinc = 0;
+		if (strncmp(arg, "auto:", 5) == 0) {
+			arg += 5;
+			*autoinc = 1;
+		}
+	}
+
 	if (strcmp(arg, "all") == 0)
 		*proc |= ~0UL;
 	else if (strcmp(arg, "odd") == 0)
@@ -1698,7 +1706,7 @@
 		/* map a process list to a CPU set */
 #ifdef USE_CPU_AFFINITY
 		unsigned long proc = 0, cpus;
-		int i;
+		int i, n, autoinc;
 
 		if (!*args[1] || !*args[2]) {
 			Alert("parsing [%s:%d] : %s expects a process number "
@@ -1709,7 +1717,7 @@
 			goto out;
 		}
 
-		if (parse_process_number(args[1], &proc, &errmsg)) {
+		if (parse_process_number(args[1], &proc, &autoinc, &errmsg)) {
 			Alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
@@ -1720,9 +1728,23 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		for (i = 0; i < LONGBITS; i++)
-			if (proc & (1UL << i))
-				global.cpu_map[i] = cpus;
+
+		if (autoinc && my_popcountl(proc) != my_popcountl(cpus)) {
+			Alert("parsing [%s:%d] : %s : PROC range and CPU sets must have the same size to be auto-assigned\n",
+			      file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		for (i = n = 0; i < LONGBITS; i++) {
+			if (proc & (1UL << i)) {
+				if (autoinc) {
+					n += my_ffsl(cpus >> n);
+					global.cpu_map[i] = (1UL << (n-1));
+				}
+				else
+					global.cpu_map[i] = cpus;
+			}
+		}
 #else
 		Alert("parsing [%s:%d] : '%s' is not enabled, please check build options for USE_CPU_AFFINITY.\n",
 		      file, linenum, args[0]);
@@ -1747,12 +1769,12 @@
 			goto out;
 		}
 
-		if (parse_process_number(args[1], &proc, &errmsg)) {
+		if (parse_process_number(args[1], &proc, NULL, &errmsg)) {
 			Alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
-		if (parse_process_number(args[2], &thread, &errmsg)) {
+		if (parse_process_number(args[2], &thread, NULL, &errmsg)) {
 			Alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
@@ -3287,7 +3309,7 @@
 				set = 0;
 				break;
 			}
-			if (parse_process_number(args[cur_arg], &set, &errmsg)) {
+			if (parse_process_number(args[cur_arg], &set, NULL, &errmsg)) {
 				Alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
 				err_code |= ERR_ALERT | ERR_FATAL;
 				goto out;
diff --git a/src/cli.c b/src/cli.c
index 05cc15e..038bdd3 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -327,7 +327,7 @@
 				set = 0;
 				break;
 			}
-			if (parse_process_number(args[cur_arg], &set, err)) {
+			if (parse_process_number(args[cur_arg], &set, NULL, err)) {
 				memprintf(err, "'%s %s' : %s", args[0], args[1], *err);
 				return -1;
 			}
diff --git a/src/listener.c b/src/listener.c
index a06a464..c82ba36 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -943,7 +943,7 @@
 {
 	unsigned long set = 0;
 
-	if (parse_process_number(args[cur_arg + 1], &set, err)) {
+	if (parse_process_number(args[cur_arg + 1], &set, NULL, err)) {
 		memprintf(err, "'%s' : %s", args[cur_arg], *err);
 		return ERR_ALERT | ERR_FATAL;
 	}