MEDIUM: deinit: close all receivers/listeners before scanning proxies

Because of the zombie state, proxies have a skewed vision of the state
of listeners, which explains why there are hacks switching the state
from ZOMBIE to INIT in the proxy cleaning loop. This is particularly
complicated and not needed, as all the information is now available
in the protocol list and the fdtab.

What we do here instead is to first close all active listeners or
receivers by protocol and clean their protocol parts. Then we scan the
fdtab to get rid of remaining ones that were necessarily in INIT state
after a previous invocation of delete_listener(). From this point, we
know the listeners are cleaned, the can safely be freed by scanning the
proxies.
diff --git a/src/haproxy.c b/src/haproxy.c
index 335ccb7..69f4f20 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2422,6 +2422,37 @@
 	struct post_server_check_fct *pscf, *pscfb;
 	struct post_check_fct *pcf, *pcfb;
 	struct post_proxy_check_fct *ppcf, *ppcfb;
+	int cur_fd;
+
+	/* At this point the listeners state is weird:
+	 *  - most listeners are still bound and referenced in their protocol
+	 *  - some might be zombies that are not in their proto anymore, but
+	 *    still appear in their proxy's listeners with a valid FD.
+	 *  - some might be stopped and still appear in their proxy as FD #-1
+	 *  - among all of them, some might be inherited hence shared and we're
+	 *    not allowed to pause them or whatever, we must just close them.
+	 *  - finally some are not listeners (pipes, logs, stdout, etc) and
+	 *    must be left intact.
+	 *
+	 * The safe way to proceed is to unbind (and close) whatever is not yet
+	 * unbound so that no more receiver/listener remains alive. Then close
+	 * remaining listener FDs, which correspond to zombie listeners (those
+	 * belonging to disabled proxies that were in another process).
+	 * objt_listener() would be cleaner here but not converted yet.
+	 */
+	protocol_unbind_all();
+
+	for (cur_fd = 0; cur_fd < global.maxsock; cur_fd++) {
+		if (!fdtab[cur_fd].owner)
+			continue;
+
+		if (fdtab[cur_fd].iocb == listener_accept) {
+			struct listener *l = fdtab[cur_fd].owner;
+
+			BUG_ON(l->state != LI_INIT);
+			unbind_listener(l);
+		}
+	}
 
 	deinit_signals();
 	while (p) {
@@ -2609,18 +2640,6 @@
 		}/* end while(s) */
 
 		list_for_each_entry_safe(l, l_next, &p->conf.listeners, by_fe) {
-			/*
-			 * Zombie proxy, the listener just pretend to be up
-			 * because they still hold an opened fd.
-			 * Close it and give the listener its real state.
-			 */
-			if (p->state == PR_STSTOPPED && l->state >= LI_ZOMBIE) {
-				fd_delete(l->rx.fd);
-				l->rx.fd = -1;
-				l->state = LI_INIT;
-			}
-			unbind_listener(l);
-			delete_listener(l);
 			LIST_DEL(&l->by_fe);
 			LIST_DEL(&l->by_bind);
 			free(l->name);
@@ -2694,8 +2713,6 @@
 
 	deinit_log_buffers();
 
-	protocol_unbind_all();
-
 	list_for_each_entry(pdf, &post_deinit_list, list)
 		pdf->fct();