MEDIUM: config: properly propagate process binding between proxies

We now recursively propagate the bind-process values between frontends
and backends instead of doing it during name resolving. This ensures
that we're able to properly propagate all the bind-process directives
even across "listen" instances, which are not perfectly covered at the
moment, depending on the declaration order.
(cherry picked from commit 64ab6077b768ee02b04a36b30ee195639a2fabc1)
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 5288600..b9853ef 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -5932,6 +5932,64 @@
 	return err_code;
 }
 
+/* This function propagates processes from frontend <from> to backend <to> so
+ * that it is always guaranteed that a backend pointed to by a frontend is
+ * bound to all of its processes. After that, if the target is a "listen"
+ * instance, the function recursively descends the target's own targets along
+ * default_backend, use_backend rules, and reqsetbe rules. Since the bits are
+ * checked first to ensure that <to> is already bound to all processes of
+ * <from>, there is no risk of looping and we ensure to follow the shortest
+ * path to the destination.
+ *
+ * It is possible to set <to> to NULL for the first call so that the function
+ * takes care of visiting the initial frontend in <from>.
+ *
+ * It is important to note that the function relies on the fact that all names
+ * have already been resolved.
+ */
+void propagate_processes(struct proxy *from, struct proxy *to)
+{
+	struct switching_rule *rule;
+	struct hdr_exp *exp;
+
+	if (to) {
+		/* check whether we need to go down */
+		if (from->bind_proc &&
+		    (from->bind_proc & to->bind_proc) == from->bind_proc)
+			return;
+
+		if (!from->bind_proc && !to->bind_proc)
+			return;
+
+		to->bind_proc = from->bind_proc ?
+			(to->bind_proc | from->bind_proc) : 0;
+
+		/* now propagate down */
+		from = to;
+	}
+
+	if (!from->cap & PR_CAP_FE)
+		return;
+
+	/* default_backend */
+	if (from->defbe.be)
+		propagate_processes(from, from->defbe.be);
+
+	/* use_backend */
+	list_for_each_entry(rule, &from->switching_rules, list) {
+		to = rule->be.backend;
+		propagate_processes(from, to);
+	}
+
+	/* reqsetbe */
+	for (exp = from->req_exp; exp != NULL; exp = exp->next) {
+		if (exp->action != ACT_SETBE)
+			continue;
+		to = (struct proxy *)exp->replace;
+		propagate_processes(from, to);
+	}
+}
+
 /*
  * Returns the error code, 0 if OK, or any combination of :
  *  - ERR_ABORT: must abort ASAP
@@ -6162,11 +6220,6 @@
 			} else {
 				free(curproxy->defbe.name);
 				curproxy->defbe.be = target;
-				/* we force the backend to be present on at least all of
-				 * the frontend's processes.
-				 */
-				target->bind_proc = curproxy->bind_proc ?
-					(target->bind_proc | curproxy->bind_proc) : 0;
 
 				/* Emit a warning if this proxy also has some servers */
 				if (curproxy->srv) {
@@ -6199,11 +6252,6 @@
 				} else {
 					free((void *)exp->replace);
 					exp->replace = (const char *)target;
-					/* we force the backend to be present on at least all of
-					 * the frontend's processes.
-					 */
-					target->bind_proc = curproxy->bind_proc ?
-						(target->bind_proc | curproxy->bind_proc) : 0;
 				}
 			}
 		}
@@ -6252,15 +6300,10 @@
 			} else {
 				free((void *)rule->be.name);
 				rule->be.backend = target;
-				/* we force the backend to be present on at least all of
-				 * the frontend's processes.
-				 */
-				target->bind_proc = curproxy->bind_proc ?
-					(target->bind_proc | curproxy->bind_proc) : 0;
 			}
 		}
 
-		/* find the target proxy for 'use_backend' rules */
+		/* find the target server for 'use_server' rules */
 		list_for_each_entry(srule, &curproxy->server_rules, list) {
 			struct server *target = findserver(curproxy, srule->srv.name);
 
@@ -7131,6 +7174,12 @@
 		}
 	}
 
+	/* At this point, target names have already been resolved */
+	for (curproxy = proxy; curproxy; curproxy = curproxy->next) {
+		if (curproxy->cap & PR_CAP_FE)
+			propagate_processes(curproxy, NULL);
+	}
+
 	/* automatically compute fullconn if not set. We must not do it in the
 	 * loop above because cross-references are not yet fully resolved.
 	 */