MEDIUM: listeners: implement protocol level ->suspend/resume() calls

Now we have ->suspend() and ->resume() for listeners at the protocol
level. This means that it now becomes possible for a protocol to redefine
its own way to suspend and resume. The default functions are provided for
TCP, UDP and unix, and they are pass-through to the receiver equivalent
as it used to be till now. Nothing was defined for sockpair since it does
not need to suspend/resume during reloads, hence it will succeed.
diff --git a/src/listener.c b/src/listener.c
index 3e305c7..1acf85f 100644
--- a/src/listener.c
+++ b/src/listener.c
@@ -358,6 +358,65 @@
 		HA_SPIN_UNLOCK(PROXY_LOCK, &px->lock);
 }
 
+/* default function called to suspend a listener: it simply passes the call to
+ * the underlying receiver. This is find for most socket-based protocols. This
+ * must be called under the listener's lock. It will return non-zero on success,
+ * 0 on failure. If no receiver-level suspend is provided, the operation is
+ * assumed to succeed.
+ */
+int default_suspend_listener(struct listener *l)
+{
+	int ret = 1;
+
+	if (!l->rx.proto->rx_suspend)
+		return 1;
+
+	ret = l->rx.proto->rx_suspend(&l->rx);
+	return ret > 0 ? ret : 0;
+}
+
+
+/* Tries to resume a suspended listener, and returns non-zero on success or
+ * zero on failure. On certain errors, an alert or a warning might be displayed.
+ * It must be called with the listener's lock held. Depending on the listener's
+ * state and protocol, a listen() call might be used to resume operations, or a
+ * call to the receiver's resume() function might be used as well. This is
+ * suitable as a default function for TCP and UDP. This must be called with the
+ * listener's lock held.
+ */
+int default_resume_listener(struct listener *l)
+{
+	int ret = 1;
+
+	if (l->state == LI_ASSIGNED) {
+		char msg[100];
+		int err;
+
+		err = l->rx.proto->listen(l, msg, sizeof(msg));
+		if (err & ERR_ALERT)
+			ha_alert("Resuming listener: %s\n", msg);
+		else if (err & ERR_WARN)
+			ha_warning("Resuming listener: %s\n", msg);
+
+		if (err & (ERR_FATAL | ERR_ABORT)) {
+			ret = 0;
+			goto end;
+		}
+	}
+
+	if (l->state < LI_PAUSED) {
+		ret = 0;
+		goto end;
+	}
+
+	if (l->state == LI_PAUSED && l->rx.proto->rx_resume &&
+	    l->rx.proto->rx_resume(&l->rx) <= 0)
+		ret = 0;
+ end:
+	return ret;
+}
+
+
 /* 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
@@ -379,18 +438,8 @@
 	if (l->state <= LI_PAUSED)
 		goto end;
 
-	if (l->rx.proto->rx_suspend) {
-		/* Returns < 0 in case of failure, 0 if the listener
-		 * was totally stopped, or > 0 if correctly paused.
-		 */
-		ret = l->rx.proto->rx_suspend(&l->rx);
-
-		if (ret < 0) {
-			ret = 0;
-			goto end;
-		}
-		ret = 1;
-	}
+	if (l->rx.proto->suspend)
+		ret = l->rx.proto->suspend(l);
 
 	MT_LIST_DEL(&l->wait_queue);
 
@@ -435,33 +484,9 @@
 
 	if (l->state == LI_READY)
 		goto end;
-
-	if (l->state == LI_ASSIGNED) {
-		char msg[100];
-		int err;
-
-		err = l->rx.proto->listen(l, msg, sizeof(msg));
-		if (err & ERR_ALERT)
-			ha_alert("Resuming listener: %s\n", msg);
-		else if (err & ERR_WARN)
-			ha_warning("Resuming listener: %s\n", msg);
-
-		if (err & (ERR_FATAL | ERR_ABORT)) {
-			ret = 0;
-			goto end;
-		}
-	}
-
-	if (l->state < LI_PAUSED) {
-		ret = 0;
-		goto end;
-	}
 
-	if (l->state == LI_PAUSED && l->rx.proto->rx_resume &&
-	    l->rx.proto->rx_resume(&l->rx) <= 0) {
-		ret = 0;
-		goto end;
-	}
+	if (l->rx.proto->resume)
+		ret = l->rx.proto->resume(l);
 
 	if (l->maxconn && l->nbconn >= l->maxconn) {
 		l->rx.proto->disable(l);
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 71d13c8..9e6a3d7 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -65,6 +65,8 @@
 	.enable = tcp_enable_listener,
 	.disable = tcp_disable_listener,
 	.unbind = default_unbind_listener,
+	.suspend = default_suspend_listener,
+	.resume  = default_resume_listener,
 	.rx_enable = sock_enable,
 	.rx_disable = sock_disable,
 	.rx_unbind = sock_unbind,
@@ -91,6 +93,8 @@
 	.enable = tcp_enable_listener,
 	.disable = tcp_disable_listener,
 	.unbind = default_unbind_listener,
+	.suspend = default_suspend_listener,
+	.resume  = default_resume_listener,
 	.rx_enable = sock_enable,
 	.rx_disable = sock_disable,
 	.rx_unbind = sock_unbind,
diff --git a/src/proto_udp.c b/src/proto_udp.c
index 1c2477e..13545ad 100644
--- a/src/proto_udp.c
+++ b/src/proto_udp.c
@@ -61,6 +61,8 @@
 	.enable = udp_enable_listener,
 	.disable = udp_disable_listener,
 	.unbind = default_unbind_listener,
+	.suspend = default_suspend_listener,
+	.resume  = default_resume_listener,
 	.rx_enable = sock_enable,
 	.rx_disable = sock_disable,
 	.rx_unbind = sock_unbind,
@@ -85,6 +87,8 @@
 	.enable = udp_enable_listener,
 	.disable = udp_disable_listener,
 	.unbind = default_unbind_listener,
+	.suspend = default_suspend_listener,
+	.resume  = default_resume_listener,
 	.rx_enable = sock_enable,
 	.rx_disable = sock_disable,
 	.rx_unbind = sock_unbind,
diff --git a/src/proto_uxst.c b/src/proto_uxst.c
index 04f12fa..047b759 100644
--- a/src/proto_uxst.c
+++ b/src/proto_uxst.c
@@ -60,6 +60,7 @@
 	.enable = uxst_enable_listener,
 	.disable = uxst_disable_listener,
 	.unbind = default_unbind_listener,
+	.suspend = default_suspend_listener,
 	.rx_enable = sock_enable,
 	.rx_disable = sock_disable,
 	.rx_unbind = sock_unbind,