MINOR: ring: make the applet code not depend on the CLI

The ring to applet communication was only made to deal with CLI functions
but it's generic. Let's have generic appctx functions and have the CLI
rely on these instead. This patch introduces ring_attach_appctx() and
ring_detach_appctx().
diff --git a/include/proto/ring.h b/include/proto/ring.h
index a82f935..d927b38 100644
--- a/include/proto/ring.h
+++ b/include/proto/ring.h
@@ -30,6 +30,8 @@
 struct ring *ring_resize(struct ring *ring, size_t size);
 void ring_free(struct ring *ring);
 ssize_t ring_write(struct ring *ring, size_t maxlen, const struct ist pfx[], size_t npfx, const struct ist msg[], size_t nmsg);
+int ring_attach_appctx(struct ring *ring, struct appctx *appctx);
+void ring_detach_appctx(struct ring *ring, struct appctx *appctx, size_t ofs);
 int ring_attach_cli(struct ring *ring, struct appctx *appctx);
 int cli_io_handler_show_ring(struct appctx *appctx);
 void cli_io_release_show_ring(struct appctx *appctx);
diff --git a/src/ring.c b/src/ring.c
index 48003b3..b6fc4c1 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -199,6 +199,44 @@
 	return sent;
 }
 
+/* Tries to attach appctx <appctx> as a new reader on ring <ring>. This is
+ * meant to be used by low level appctx code such as CLI or ring forwarding.
+ * For higher level functions, please see the relevant parts in appctx or CLI.
+ * It returns non-zero on success or zero on failure if too many users are
+ * already attached. On success, the caller MUST call ring_detach_appctx()
+ * to detach itself, even if it was never woken up.
+ */
+int ring_attach_appctx(struct ring *ring, struct appctx *appctx)
+{
+	int users = ring->readers_count;
+
+	do {
+		if (users >= 255)
+			return 0;
+	} while (!_HA_ATOMIC_CAS(&ring->readers_count, &users, users + 1));
+	return 1;
+}
+
+/* detach an appctx from a ring. The appctx is expected to be waiting at
+ * offset <ofs>. Nothing is done if <ring> is NULL.
+ */
+void ring_detach_appctx(struct ring *ring, struct appctx *appctx, size_t ofs)
+{
+	if (!ring)
+		return;
+
+	HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &ring->lock);
+	if (ofs != ~0) {
+		/* reader was still attached */
+		ofs -= ring->ofs;
+		BUG_ON(ofs >= b_size(&ring->buf));
+		LIST_DEL_INIT(&appctx->wait_entry);
+		HA_ATOMIC_SUB(b_peek(&ring->buf, ofs), 1);
+	}
+	HA_ATOMIC_SUB(&ring->readers_count, 1);
+	HA_RWLOCK_WRUNLOCK(LOGSRV_LOCK, &ring->lock);
+}
+
 /* Tries to attach CLI handler <appctx> as a new reader on ring <ring>. This is
  * meant to be used when registering a CLI function to dump a buffer, so it
  * returns zero on success, or non-zero on failure with a message in the appctx
@@ -207,15 +245,10 @@
  */
 int ring_attach_cli(struct ring *ring, struct appctx *appctx)
 {
-	int users = ring->readers_count;
-
-	do {
-		if (users >= 255)
-			return cli_err(appctx,
-				       "Sorry, too many watchers (255) on this ring buffer. "
-				       "What could it have so interesting to attract so many watchers ?");
-
-	} while (!_HA_ATOMIC_CAS(&ring->readers_count, &users, users + 1));
+	if (!ring_attach_appctx(ring, appctx))
+		return cli_err(appctx,
+		               "Sorry, too many watchers (255) on this ring buffer. "
+		               "What could it have so interesting to attract so many watchers ?");
 
 	if (!appctx->io_handler)
 		appctx->io_handler = cli_io_handler_show_ring;
@@ -341,19 +374,7 @@
 	struct ring *ring = appctx->ctx.cli.p0;
 	size_t ofs = appctx->ctx.cli.o0;
 
-	if (!ring)
-		return;
-
-	HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &ring->lock);
-	if (ofs != ~0) {
-		/* reader was still attached */
-		ofs -= ring->ofs;
-		BUG_ON(ofs >= b_size(&ring->buf));
-		LIST_DEL_INIT(&appctx->wait_entry);
-		HA_ATOMIC_SUB(b_peek(&ring->buf, ofs), 1);
-	}
-	HA_ATOMIC_SUB(&ring->readers_count, 1);
-	HA_RWLOCK_WRUNLOCK(LOGSRV_LOCK, &ring->lock);
+	ring_detach_appctx(ring, appctx, ofs);
 }