Introduce pollers

Handlers may way to poll on multiple file descriptors. This change
introduces pollers, which are registered with:
console_register_poller(), which provides a function to call when a
specified fd has pollable events.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/console-server.c b/console-server.c
index e68eab1..1ddb36c 100644
--- a/console-server.c
+++ b/console-server.c
@@ -6,6 +6,7 @@
 
 #define _GNU_SOURCE
 
+#include <assert.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdlib.h>
@@ -29,10 +30,27 @@
 	int		tty_sirq;
 	int		tty_lpc_addr;
 	int		tty_fd;
+
 	struct handler	**handlers;
 	int		n_handlers;
+
+	struct poller	**pollers;
+	int		n_pollers;
+
+	struct pollfd	*pollfds;
 };
 
+struct poller {
+	struct handler	*handler;
+	void		*data;
+	poller_fn_t	fn;
+	bool		remove;
+};
+
+/* we have one extra entry in the pollfds array for the VUART tty */
+static const int n_internal_pollfds = 1;
+
+
 static void usage(const char *progname)
 {
 	fprintf(stderr,
@@ -148,6 +166,9 @@
 	 */
 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
 
+	console->pollfds[console->n_pollers].fd = console->tty_fd;
+	console->pollfds[console->n_pollers].events = POLLIN;
+
 	return 0;
 }
 
@@ -210,25 +231,125 @@
 	}
 
 	return rc;
+
 }
 
-static int handlers_poll_event(struct console *console,
-		struct pollfd *pollfds)
+struct poller *console_register_poller(struct console *console,
+		struct handler *handler, poller_fn_t poller_fn,
+		int fd, int events, void *data)
 {
-	struct handler *handler;
-	int i, rc, tmp;
+	struct poller *poller;
+	int n;
+
+	poller = malloc(sizeof(*poller));
+	poller->remove = false;
+	poller->handler = handler;
+	poller->fn = poller_fn;
+	poller->data = data;
+
+	/* add one to our pollers array */
+	n = console->n_pollers++;
+	console->pollers = realloc(console->pollers,
+			sizeof(*console->pollers) * console->n_pollers);
+
+	console->pollers[n] = poller;
+
+	/* increase pollfds array too  */
+	console->pollfds = realloc(console->pollfds,
+			sizeof(*console->pollfds) *
+				(n_internal_pollfds + console->n_pollers));
+
+	/* shift the end pollfds up by one */
+	memcpy(&console->pollfds[n+n_internal_pollfds],
+			&console->pollfds[n],
+			sizeof(*console->pollfds) * n_internal_pollfds);
+
+	console->pollfds[n].fd = fd;
+	console->pollfds[n].events = events;
+
+	return poller;
+}
+
+void console_unregister_poller(struct console *console,
+		struct poller *poller)
+{
+	int i;
+
+	/* find the entry in our pollers array */
+	for (i = 0; i < console->n_pollers; i++)
+		if (console->pollers[i] == poller)
+			break;
+
+	assert(i < console->n_pollers);
+
+	console->n_pollers--;
+
+	/* remove the item from the pollers array... */
+	memmove(&console->pollers[i], &console->pollers[i+1],
+			sizeof(*console->pollers)
+				* (console->n_pollers - i));
+
+	console->pollers = realloc(console->pollers,
+			sizeof(*console->pollers) * console->n_pollers);
+
+	/* ... and the pollfds array */
+	memmove(&console->pollfds[i], &console->pollfds[i+1],
+			sizeof(*console->pollfds) *
+				(n_internal_pollfds + console->n_pollers - i));
+
+	console->pollfds = realloc(console->pollfds,
+			sizeof(*console->pollfds) *
+				(n_internal_pollfds + console->n_pollers));
+
+
+	free(poller);
+}
+
+static int call_pollers(struct console *console)
+{
+	struct poller *poller;
+	struct pollfd *pollfd;
+	enum poller_ret prc;
+	int i, rc;
 
 	rc = 0;
 
-	for (i = 0; i < console->n_handlers; i++) {
-		handler = console->handlers[i];
+	/*
+	 * Process poll events by iterating through the pollers and pollfds
+	 * in-step, calling any pollers that we've found revents for.
+	 */
+	for (i = 0; i < console->n_pollers; i++) {
+		poller = console->pollers[i];
+		pollfd = &console->pollfds[i];
 
-		if (!handler->poll_event)
+		if (!pollfd->revents)
 			continue;
 
-		tmp = handler->poll_event(handler, pollfds[i].revents);
-		if (tmp == HANDLER_EXIT)
-			rc = 1;
+		prc = poller->fn(poller->handler, pollfd->revents,
+				poller->data);
+		if (prc == POLLER_EXIT)
+			rc = -1;
+		else if (prc == POLLER_REMOVE)
+			poller->remove = true;
+	}
+
+	/**
+	 * Process deferred removals; restarting each time we unregister, as
+	 * the array will have changed
+	 */
+	for (;;) {
+		bool removed = false;
+
+		for (i = 0; i < console->n_pollers; i++) {
+			poller = console->pollers[i];
+			if (poller->remove) {
+				console_unregister_poller(console, poller);
+				removed = true;
+				break;
+			}
+		}
+		if (!removed)
+			break;
 	}
 
 	return rc;
@@ -236,31 +357,22 @@
 
 int run_console(struct console *console)
 {
-	struct handler *handler;
-	struct pollfd *pollfds;
-	int i, rc;
-
-	pollfds = calloc(console->n_handlers + 1, sizeof(*pollfds));
-
-	pollfds[0].fd = console->tty_fd;
-	pollfds[0].events = POLLIN;
+	int rc;
 
 	for (;;) {
 		uint8_t buf[4096];
 
-		/* init pollers */
-		for (i = 0; i < console->n_handlers; i++) {
-			handler = console->handlers[i];
-			handler->init_poll(handler, &pollfds[i+1]);
-		}
-
-		rc = poll(pollfds, console->n_handlers + 1, -1);
+		rc = poll(console->pollfds,
+				console->n_pollers + n_internal_pollfds, -1);
 		if (rc < 0) {
 			warn("poll error");
 			return -1;
 		}
 
-		if (pollfds[0].revents) {
+		/* process internal fd first */
+		BUILD_ASSERT(n_internal_pollfds == 1);
+
+		if (console->pollfds[console->n_pollers].revents) {
 			rc = read(console->tty_fd, buf, sizeof(buf));
 			if (rc <= 0) {
 				warn("Error reading from tty device");
@@ -271,7 +383,8 @@
 				return 0;
 		}
 
-		rc = handlers_poll_event(console, pollfds + 1);
+		/* ... and then the pollers */
+		rc = call_pollers(console);
 		if (rc)
 			return 0;
 	}
@@ -328,6 +441,9 @@
 		}
 	}
 
+	console->pollfds = calloc(n_internal_pollfds,
+			sizeof(*console->pollfds));
+
 	if (!console->tty_kname) {
 		fprintf(stderr,
 			"Error: No TTY device specified (use --device)\n");