main: Extract a 'source' abstraction

The D-Bus set of sink actions will require polling the D-Bus fd and
processing any inbound messages. To do this, the polling must happen
where we're currently reading from the source file descriptor. In the
same manner as the sink abstraction extract a source abstraction to hide
the details. process() is then implemented in terms of the abstract
source interface.

Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Change-Id: I629e0d71f30592fa30affac1a920104e8086d8b0
diff --git a/main.c b/main.c
index 6a81933..c064035 100644
--- a/main.c
+++ b/main.c
@@ -29,6 +29,7 @@
  */
 
 #include <err.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
 #include <libgen.h>
@@ -41,6 +42,19 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+struct debug_source_ops {
+	int (*poll)(void *ctx, char *op);
+};
+
+struct debug_source {
+	const struct debug_source_ops *ops;
+	void *ctx;
+};
+
+struct debug_source_basic {
+	int source;
+};
+
 struct debug_sink_ops {
 	void (*debug)(void *ctx);
 	void (*reboot)(void *ctx);
@@ -98,12 +112,34 @@
 	.reboot = sysrq_sink_reboot,
 };
 
-static int process(int source, struct debug_sink *sink)
+static int basic_source_poll(void *ctx, char *op)
 {
+	struct debug_source_basic *basic = ctx;
 	ssize_t ingress;
-	char command;
 
-	while ((ingress = read(source, &command, sizeof(command))) == sizeof(command)) {
+	if ((ingress = read(basic->source, op, 1)) != 1) {
+		if (ingress < 0) {
+			warn("Failed to read from basic source");
+			return -errno;
+		}
+
+		/* Unreachable */
+		errx(EXIT_FAILURE, "Bad read, requested 1 got %zd", ingress);
+	}
+
+	return 0;
+}
+
+const struct debug_source_ops basic_source_ops = {
+	.poll = basic_source_poll,
+};
+
+static int process(struct debug_source *source, struct debug_sink *sink)
+{
+	char command;
+	int rc;
+
+	while (!(rc = source->ops->poll(source->ctx, &command))) {
 		switch (command) {
 		case 'D':
 			sink->ops->debug(sink->ctx);
@@ -116,16 +152,18 @@
 		}
 	}
 
-	if (ingress == -1)
-		warn("Failed to read from source");
+	if (rc < 0)
+		warnx("Failed to poll source: %s", strerror(-rc));
 
-	return ingress;
+	return rc;
 }
 
 int main(int argc, char * const argv[])
 {
 	const char *sink_actions = NULL;
+	struct debug_source_basic basic;
 	struct debug_sink_sysrq sysrq;
+	struct debug_source source;
 	struct debug_sink sink;
 	char devnode[PATH_MAX];
 	char *devid;
@@ -210,8 +248,12 @@
 	if (optind < argc)
 		err(EXIT_FAILURE, "Found %d unexpected arguments", argc - optind);
 
+	basic.source = sourcefd;
+	source.ops = &basic_source_ops;
+	source.ctx = &basic;
+
 	/* Trigger the actions on the sink when we receive an event from the source */
-	if (process(sourcefd, &sink) < 0)
+	if (process(&source, &sink) < 0)
 		errx(EXIT_FAILURE, "Failure while processing command stream");
 
 	return 0;