changeset 1162:8278d14e7aaa

irccd: add irc_bot_pollable_add, closes #2529
author David Demelier <markand@malikania.fr>
date Sat, 12 Feb 2022 19:55:57 +0100
parents b6a0d9515c82
children ad7673c59ec5
files CHANGES.md lib/irccd/irccd.c lib/irccd/irccd.h man/libirccd-irccd.3
diffstat 4 files changed, 142 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES.md	Mon Feb 07 08:42:25 2022 +0100
+++ b/CHANGES.md	Sat Feb 12 19:55:57 2022 +0100
@@ -1,6 +1,12 @@
 IRC Client Daemon CHANGES
 =========================
 
+irccd 4.1.0 ????-??-??
+======================
+
+- Add `irc_bot_pollable_add` to insert a custom interface into the main irccd
+  loop.
+
 irccd 4.0.1 2022-02-07
 ======================
 
--- a/lib/irccd/irccd.c	Mon Feb 07 08:42:25 2022 +0100
+++ b/lib/irccd/irccd.c	Sat Feb 12 19:55:57 2022 +0100
@@ -45,6 +45,11 @@
 	struct defer *next;
 };
 
+struct pollable {
+	struct irc_pollable *iface;
+	struct pollable *next;
+};
+
 struct irc irc = {0};
 
 static struct sigaction sa;
@@ -52,6 +57,8 @@
 static int pipes[2];
 static pthread_t self;
 static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
+static struct pollable *pollables;
+static size_t pollablesz;
 
 static int
 is_command(const struct irc_plugin *p, const struct irc_event *ev)
@@ -254,6 +261,18 @@
 		irc_util_die("read: %s\n", strerror(errno));
 }
 
+static inline void
+delpollable(struct pollable *pb)
+{
+	if (pb->iface->finish)
+		pb->iface->finish(pb->iface->data);
+
+	LL_DELETE(pollables, pb);
+	free(pb);
+
+	pollablesz--;
+}
+
 void
 irc_bot_init(void)
 {
@@ -582,7 +601,7 @@
 size_t
 irc_bot_poll_size(void)
 {
-	size_t i = 1;
+	size_t i = 1 + pollablesz;
 	struct irc_server *s;
 
 	LL_FOREACH(irc.servers, s)
@@ -597,6 +616,8 @@
 	assert(fds);
 
 	struct irc_server *s;
+	struct pollable *pb;
+	int frecv = 0, fsend = 0;
 
 	fds->fd = pipes[0];
 	fds->events = POLLIN;
@@ -604,6 +625,11 @@
 
 	LL_FOREACH(irc.servers, s)
 		irc_server_prepare(s, fds++);
+	LL_FOREACH(pollables, pb) {
+		pb->iface->want(&frecv, &fsend, pb->iface->data);
+		fds->fd = pb->iface->fd(pb->iface->data);
+		fds++->events |= (frecv ? POLLIN : 0) | (fsend ? POLLOUT : 0);
+	}
 }
 
 void
@@ -613,6 +639,9 @@
 
 	struct irc_server *s;
 	struct defer *d, *dtmp;
+	struct pollable *pb, *pbnext;
+	size_t pbsz = pollablesz;
+	int frecv, fsend;
 
 	pipe_flush(fds++);
 
@@ -625,6 +654,19 @@
 
 	LL_FOREACH(irc.servers, s)
 		irc_server_flush(s, fds++);
+
+	/*
+	 * We must iterate using the number of pollables because their sync
+	 * function may modify the list.
+	 */
+	for (pb = pollables; pbsz--; pb = pbnext) {
+		pbnext = pb->next;
+		frecv = fds->revents & POLLIN;
+		fsend = fds++->revents & POLLOUT;
+
+		if (pb->iface->sync(frecv, fsend, pb->iface->data) < 0)
+			delpollable(pb);
+	}
 }
 
 int
@@ -667,10 +709,26 @@
 }
 
 void
+irc_bot_pollable_add(struct irc_pollable *iface)
+{
+	assert(iface);
+	assert(iface->fd && iface->want && iface->sync);
+
+	struct pollable *pb;
+
+	pb = irc_util_calloc(1, sizeof (*pb));
+	pb->iface = iface;
+
+	LL_APPEND(pollables, pb);
+	pollablesz++;
+}
+
+void
 irc_bot_finish(void)
 {
 	struct irc_plugin_loader *ld, *ldtmp;
 	struct defer *d, *dtmp;
+	struct pollable *pb, *pbtmp;
 
 	close(pipes[0]);
 	close(pipes[1]);
@@ -685,8 +743,17 @@
 	/* Remove all deferred calls. */
 	LL_FOREACH_SAFE(queue, d, dtmp)
 		free(d);
+	LL_FOREACH_SAFE(pollables, pb, pbtmp) {
+		if (pb->iface->finish)
+			pb->iface->finish(pb->iface->data);
+
+		free(pb);
+	}
 
 	queue = NULL;
+	pollables = NULL;
+	pollablesz = 0;
+
 	irc_bot_server_clear();
 	irc_bot_plugin_clear();
 	irc_bot_rule_clear();
--- a/lib/irccd/irccd.h	Mon Feb 07 08:42:25 2022 +0100
+++ b/lib/irccd/irccd.h	Sat Feb 12 19:55:57 2022 +0100
@@ -30,6 +30,14 @@
 extern "C" {
 #endif
 
+struct irc_pollable {
+	void *data;
+	int (*fd)(void *);
+	void (*want)(int *, int *, void *);
+	int (*sync)(int, int, void *);
+	void (*finish)(void *);
+};
+
 extern struct irc {
 	struct irc_server *servers;
 	struct irc_plugin *plugins;
@@ -117,6 +125,9 @@
 irc_bot_post(void (*)(void *), void *);
 
 void
+irc_bot_pollable_add(struct irc_pollable *);
+
+void
 irc_bot_finish(void);
 
 #if defined(__cplusplus)
--- a/man/libirccd-irccd.3	Mon Feb 07 08:42:25 2022 +0100
+++ b/man/libirccd-irccd.3	Sat Feb 12 19:55:57 2022 +0100
@@ -31,6 +31,14 @@
 	struct irc_rule *rules;
 	struct irc_hook *hooks;
 } irc;
+
+struct irc_pollable {
+	void  *data;
+	int  (*fd)(void *data);
+	void (*want)(int *frecv, int *fsend, void *data);
+	int  (*sync)(int frecv, int fsend, void *data);
+	void (*finish)(void *data);
+};
 .Ed
 .Pp
 .Ft void
@@ -86,6 +94,8 @@
 .Ft void
 .Fn irc_bot_post "void (*fn)(void *), void *data"
 .Ft void
+.Fn irc_bot_pollable_add "struct irc_pollable *pb"
+.Ft void
 .Fn irc_bot_finish "void"
 .\" DESCRIPTION
 .Sh DESCRIPTION
@@ -114,6 +124,44 @@
 .El
 .Pp
 The
+.Vt irc_pollable
+interface is an opaque structure that can be used to insert custom descriptors
+into the irccd main loop.
+.Pp
+All these functions takes as last argument the user data specified in the field
+.Va data .
+.Pp
+Available fields:
+.Bl -tag
+.It Va data
+Opaque user data, can be NULL.
+.It Va fd
+This function must return the user file descriptor to monitor.
+.It Va want
+This function must assign to the
+.Fa frecv
+and
+.Fa fsend
+arguments if the descriptor has to be selected for read or write condition
+respectively. They are set to 0 prior to the invocation.
+.It Va sync
+This function is called after polling for the file descriptors. The arguments
+.Fa frecv
+and
+.Fa fsend
+will be set to 1 if the condition were met similarly to the
+.Va want
+function.
+.Pp
+The function must return 0 on success and any other value on error.
+.It Va finish
+This optional function is called when the pollable is about to be finalized,
+this happens when the function
+.Va sync
+returned -1 or the bot is quitting.
+.El
+.Pp
+The
 .Fn irc_bot_init
 function initializes the irccd globals and some of its APIs. This function does
 not need to be called from plugins.
@@ -277,6 +325,15 @@
 as first argument.
 .Pp
 The
+.Fn irc_bot_pollable_add
+function inserts the new
+.Fa pb
+to the list of custom descriptor to monitor in the irccd main's loop. Ownership
+is kept to the user and the address of
+.Fa pb
+must remain valid until it is no longer necessary.
+.Pp
+The
 .Fn irc_bot_finish
 function cleanups any allocated resources.
 .Pp