MINOR: listeners: add a new stop_listener() function

This function will be used to definitely stop a listener (e.g. during a
soft_stop). This is actually tricky because it may be called for a proxy
or for a protocol, both of which require locks and already hold some. The
function takes booleans indicating which ones are already held, hoping
this will be enough. It's not well defined wether proto->disable() and
proto->rx_disable() are supposed to be called with any lock held, and
they are used from do_unbind_listener() with all these locks. Some back
annotations ought to be added on this point.

The proxy's listeners count is updated, and the proxy is marked as
disabled and woken up after the last one is gone. Note that a
listener in listen state is already not attached anymore since it
was disabled.
diff --git a/src/listener.c b/src/listener.c
index e44c82c..e8f2a95 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -309,6 +309,68 @@
 	HA_SPIN_UNLOCK(LISTENER_LOCK, &listener->lock);
 }
 
+/*
+ * This function completely stops a listener. It will need to operate under the
+ * proxy's lock, the protocol's lock, and the listener's lock. The caller is
+ * responsible for indicating in lpx, lpr, lli whether the respective locks are
+ * already held (non-zero) or not (zero) so that the function picks the missing
+ * ones, in this order. The proxy's listeners count is updated and the proxy is
+ * disabled and woken up after the last one is gone.
+ */
+void stop_listener(struct listener *l, int lpx, int lpr, int lli)
+{
+	struct proxy *px = l->bind_conf->frontend;
+	int must_close;
+
+	if (l->options & LI_O_NOSTOP) {
+		/* master-worker sockpairs are never closed but don't count as a
+		 * job.
+		 */
+		return;
+	}
+
+	/* There are several cases where we must not close an FD:
+	 *   - we're starting up and we have socket transfers enabled;
+	 *   - we're the master and this FD was inherited;
+	 */
+	if ((global.tune.options & GTUNE_SOCKET_TRANSFER && global.mode & MODE_STARTING) ||
+	    (master && (l->rx.flags & RX_F_INHERITED)))
+		must_close = 0;
+	else
+		must_close = 1;
+
+	if (!lpx)
+		HA_SPIN_LOCK(PROXY_LOCK, &px->lock);
+
+	if (!lpr)
+		HA_SPIN_LOCK(PROTO_LOCK, &proto_lock);
+
+	if (!lli)
+		HA_SPIN_LOCK(LISTENER_LOCK, &l->lock);
+
+	if (l->state > LI_INIT) {
+		do_unbind_listener(l, must_close);
+
+		if (l->state >= LI_ASSIGNED)
+			__delete_listener(l);
+
+		if (px->li_ready + px->li_paused == 0) {
+			px->disabled = 1;
+			if (px->task)
+				task_wakeup(px->task, TASK_WOKEN_MSG);
+		}
+	}
+
+	if (!lli)
+		HA_SPIN_UNLOCK(LISTENER_LOCK, &l->lock);
+
+	if (!lpr)
+		HA_SPIN_UNLOCK(PROTO_LOCK, &proto_lock);
+
+	if (!lpx)
+		HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
+}
+
 /* This function tries to temporarily disable a listener, depending on the OS
  * capabilities. Linux unbinds the listen socket after a SHUT_RD, and ignores
  * SHUT_WR. Solaris refuses either shutdown(). OpenBSD ignores SHUT_RD but