console-server: Enable multiple consoles

obmc-console can now support multiple consoles that can be configured.

The first console is the active console, if not configured otherwise.

This serves as preparation for uart mux support.

Change-Id: I6f350b8efe70c3b424bdadaa3fe1bbf89d310e5d
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/console-dbus.c b/console-dbus.c
index ba8356d..f6749e1 100644
--- a/console-dbus.c
+++ b/console-dbus.c
@@ -161,12 +161,50 @@
 	SD_BUS_VTABLE_END,
 };
 
+int dbus_server_init(struct console_server *server)
+{
+	int r;
+	int fd;
+	r = sd_bus_default(&server->bus);
+	if (r < 0) {
+		warnx("Failed to connect to bus: %s", strerror(-r));
+		return -1;
+	}
+
+	fd = sd_bus_get_fd(server->bus);
+	if (fd < 0) {
+		warnx("Couldn't get the bus file descriptor");
+		sd_bus_unref(server->bus);
+		return -1;
+	}
+
+	const ssize_t index = console_server_request_pollfd(server, fd, POLLIN);
+	if (index < 0) {
+		warnx("Error: failed to allocate pollfd");
+		sd_bus_unref(server->bus);
+		return -1;
+	}
+
+	server->dbus_pollfd_index = index;
+	return 0;
+}
+
+void dbus_server_fini(struct console_server *server)
+{
+	if (server->dbus_pollfd_index < server->capacity_pollfds) {
+		console_server_release_pollfd(server,
+					      server->dbus_pollfd_index);
+		server->dbus_pollfd_index = SIZE_MAX;
+	}
+
+	sd_bus_unref(server->bus);
+}
+
 int dbus_init(struct console *console,
 	      struct config *config __attribute__((unused)))
 {
 	char obj_name[dbus_obj_path_len];
 	char dbus_name[dbus_obj_path_len];
-	int fd;
 	int r;
 	size_t bytes;
 
@@ -175,12 +213,6 @@
 		return -1;
 	}
 
-	r = sd_bus_default(&console->bus);
-	if (r < 0) {
-		warnx("Failed to connect to bus: %s", strerror(-r));
-		return -1;
-	}
-
 	/* Register support console interface */
 	bytes = snprintf(obj_name, dbus_obj_path_len, OBJ_NAME,
 			 console->console_id);
@@ -192,9 +224,9 @@
 
 	if (console->server->tty.type == TTY_DEVICE_UART) {
 		/* Register UART interface */
-		r = sd_bus_add_object_vtable(console->bus, NULL, obj_name,
-					     UART_INTF, console_uart_vtable,
-					     console);
+		r = sd_bus_add_object_vtable(console->server->bus, NULL,
+					     obj_name, UART_INTF,
+					     console_uart_vtable, console);
 		if (r < 0) {
 			warnx("Failed to register UART interface: %s",
 			      strerror(-r));
@@ -203,8 +235,9 @@
 	}
 
 	/* Register access interface */
-	r = sd_bus_add_object_vtable(console->bus, NULL, obj_name, ACCESS_INTF,
-				     console_access_vtable, console);
+	r = sd_bus_add_object_vtable(console->server->bus, NULL, obj_name,
+				     ACCESS_INTF, console_access_vtable,
+				     console);
 	if (r < 0) {
 		warnx("Failed to issue method call: %s", strerror(-r));
 		return -1;
@@ -219,7 +252,7 @@
 	}
 
 	/* Finally register the bus name */
-	r = sd_bus_request_name(console->bus, dbus_name,
+	r = sd_bus_request_name(console->server->bus, dbus_name,
 				SD_BUS_NAME_ALLOW_REPLACEMENT |
 					SD_BUS_NAME_REPLACE_EXISTING);
 	if (r < 0) {
@@ -227,19 +260,5 @@
 		return -1;
 	}
 
-	fd = sd_bus_get_fd(console->bus);
-	if (fd < 0) {
-		warnx("Couldn't get the bus file descriptor");
-		return -1;
-	}
-
-	const ssize_t index =
-		console_server_request_pollfd(console->server, fd, POLLIN);
-	if (index < 0) {
-		fprintf(stderr, "Error: failed to allocate pollfd\n");
-		return -1;
-	}
-
-	console->dbus_pollfd_index = index;
 	return 0;
 }
diff --git a/console-server.c b/console-server.c
index b7a046b..870e5a7 100644
--- a/console-server.c
+++ b/console-server.c
@@ -923,34 +923,52 @@
 	}
 }
 
-static int run_console_iteration(struct console *console)
+static int run_console_per_console(struct console *console, size_t buf_size,
+				   struct timeval *tv)
 {
-	uint8_t buf[4096];
-	struct timeval tv;
-	long timeout;
-	ssize_t rc;
+	int rc;
 
-	if (console->rb->size < sizeof(buf)) {
+	if (console->rb->size < buf_size) {
 		fprintf(stderr, "Ringbuffer size should be greater than %zuB\n",
-			sizeof(buf));
+			buf_size);
 		return -1;
 	}
 
 	if (sigint) {
-		fprintf(stderr, "Received interrupt, exiting\n");
+		warnx("Received interrupt, exiting\n");
 		return -1;
 	}
 
+	/* ... and then the pollers */
+	rc = call_pollers(console, tv);
+	if (rc) {
+		return -1;
+	}
+
+	return 0;
+}
+
+static int run_console_iteration(struct console_server *server)
+{
+	struct timeval tv;
+	uint8_t buf[4096];
+	long timeout;
+	ssize_t rc;
+
 	rc = get_current_time(&tv);
 	if (rc) {
 		warn("Failed to read current time");
 		return -1;
 	}
 
-	timeout = get_poll_timeout(console, &tv);
+	timeout = get_poll_timeout(server->active, &tv);
 
-	rc = poll(console->server->pollfds, console->server->capacity_pollfds,
-		  (int)timeout);
+	rc = poll(server->pollfds, server->capacity_pollfds, (int)timeout);
+
+	if (sigint) {
+		warnx("Received interrupt, exiting\n");
+		return -1;
+	}
 
 	if (rc < 0) {
 		if (errno == EINTR) {
@@ -961,64 +979,279 @@
 	}
 
 	/* process internal fd first */
-	if (console->server->pollfds[console->server->tty_pollfd_index].revents) {
-		rc = read(console->server->tty.fd, buf, sizeof(buf));
+	if (server->pollfds[server->tty_pollfd_index].revents) {
+		rc = read(server->tty.fd, buf, sizeof(buf));
 		if (rc <= 0) {
 			warn("Error reading from tty device");
 			return -1;
 		}
-		rc = ringbuffer_queue(console->rb, buf, rc);
+
+		rc = ringbuffer_queue(server->active->rb, buf, rc);
 		if (rc) {
 			return -1;
 		}
 	}
 
-	if (console->server->pollfds[console->dbus_pollfd_index].revents) {
-		sd_bus_process(console->bus, NULL);
+	// process dbus
+	struct pollfd *dbus_pollfd =
+		&(server->pollfds[server->dbus_pollfd_index]);
+	if (dbus_pollfd->revents) {
+		sd_bus_process(server->bus, NULL);
 	}
 
-	/* ... and then the pollers */
-	rc = call_pollers(console, &tv);
-	if (rc) {
-		return -1;
+	for (size_t i = 0; i < server->n_consoles; i++) {
+		struct console *console = server->consoles[i];
+
+		rc = run_console_per_console(console, sizeof(buf), &tv);
+		if (rc != 0) {
+			return -1;
+		}
 	}
+
 	return 0;
 }
 
-int run_console(struct console *console)
+int run_server(struct console_server *server)
 {
-	sighandler_t sighandler_save = signal(SIGINT, sighandler);
+	sighandler_t sighandler_save;
 	ssize_t rc = 0;
 
+	if (server->n_consoles == 0) {
+		warnx("no console configured for this server");
+		return -1;
+	}
+
+	sighandler_save = signal(SIGINT, sighandler);
 	for (;;) {
-		rc = run_console_iteration(console);
+		rc = run_console_iteration(server);
 		if (rc) {
 			break;
 		}
 	}
-
 	signal(SIGINT, sighandler_save);
-	sd_bus_unref(console->bus);
 
 	return rc ? -1 : 0;
 }
+
 static const struct option options[] = {
 	{ "config", required_argument, 0, 'c' },
 	{ "console-id", required_argument, 0, 'i' },
 	{ 0, 0, 0, 0 },
 };
 
-int main(int argc, char **argv)
+static struct console *console_init(struct console_server *server,
+				    struct config *config,
+				    const char *console_id)
 {
 	size_t buffer_size = default_buffer_size;
+	const char *buffer_size_str = NULL;
+	int rc;
+
+	struct console *console = calloc(1, sizeof(struct console));
+	if (console == NULL) {
+		return NULL;
+	}
+
+	console->server = server;
+	console->console_id = console_id;
+
+	buffer_size_str =
+		config_get_section_value(config, console_id, "ringbuffer-size");
+
+	if (!buffer_size_str) {
+		buffer_size_str = config_get_value(config, "ringbuffer-size");
+	}
+
+	if (buffer_size_str) {
+		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
+		if (rc) {
+			warn("Invalid ringbuffer-size. Default to %zukB",
+			     buffer_size >> 10);
+		}
+	}
+
+	console->rb = ringbuffer_init(buffer_size);
+	if (!console->rb) {
+		goto cleanup_console;
+	}
+
+	if (set_socket_info(console, config, console_id)) {
+		warnx("set_socket_info failed");
+		goto cleanup_rb;
+	}
+
+	rc = dbus_init(console, config);
+	if (rc != 0) {
+		goto cleanup_rb;
+	}
+
+	handlers_init(console, config);
+
+	return console;
+
+cleanup_rb:
+	free(console->rb);
+cleanup_console:
+	free(console);
+
+	return NULL;
+}
+
+static void console_fini(struct console *console)
+{
+	handlers_fini(console);
+	ringbuffer_fini(console->rb);
+	free(console->pollers);
+	free(console);
+}
+
+// 'opt_console_id' may be NULL
+static int console_server_add_console(struct console_server *server,
+				      struct config *config,
+				      const char *opt_console_id)
+{
+	const char *console_id;
+	struct console *console;
+
+	console_id = config_resolve_console_id(config, opt_console_id);
+
+	struct console **tmp = reallocarray(server->consoles,
+					    server->n_consoles + 1,
+					    sizeof(struct console *));
+	if (tmp == NULL) {
+		warnx("could not realloc server->consoles");
+		return -1;
+	}
+	server->consoles = tmp;
+
+	console = console_init(server, config, console_id);
+	if (console == NULL) {
+		warnx("console_init failed");
+		return -1;
+	}
+
+	server->consoles[server->n_consoles++] = console;
+
+	return 0;
+}
+
+// returns NULL on error
+static struct console *
+console_server_add_consoles(struct console_server *server,
+			    const char *arg_console_id)
+{
+	int rc;
+
+	const int nsections = config_count_sections(server->config);
+	if (nsections < 0) {
+		return NULL;
+	}
+
+	if (nsections == 0) {
+		const char *console_id = arg_console_id;
+
+		rc = console_server_add_console(server, server->config,
+						console_id);
+		if (rc != 0) {
+			return NULL;
+		}
+	}
+
+	for (int i = 0; i < nsections; i++) {
+		const char *console_id =
+			config_get_section_name(server->config, i);
+
+		if (console_id == NULL) {
+			warnx("no console id provided\n");
+			return NULL;
+		}
+
+		rc = console_server_add_console(server, server->config,
+						console_id);
+		if (rc != 0) {
+			return NULL;
+		}
+	}
+
+	const char *initially_active =
+		config_get_value(server->config, "active-console");
+	if (!initially_active) {
+		return server->consoles[0];
+	}
+
+	printf("setting console-id '%s' as the initially active console\n",
+	       initially_active);
+
+	for (size_t i = 0; i < server->n_consoles; i++) {
+		struct console *console = server->consoles[i];
+
+		if (strcmp(console->console_id, initially_active) == 0) {
+			return console;
+		}
+	}
+
+	warnx("'active-console' '%s' not found among console ids\n",
+	      initially_active);
+
+	return NULL;
+}
+
+int console_server_init(struct console_server *server,
+			const char *config_filename,
+			const char *config_tty_kname, const char *console_id)
+{
+	int rc;
+	memset(server, 0, sizeof(struct console_server));
+
+	server->tty_pollfd_index = -1;
+
+	server->config = config_init(config_filename);
+	if (server->config == NULL) {
+		return -1;
+	}
+
+	uart_routing_init(server->config);
+
+	rc = tty_init(server, server->config, config_tty_kname);
+	if (rc != 0) {
+		warnx("error during tty_init, exiting.\n");
+		return -1;
+	}
+
+	rc = dbus_server_init(server);
+	if (rc != 0) {
+		warnx("error during dbus init for console server");
+		return -1;
+	}
+
+	server->active = console_server_add_consoles(server, console_id);
+	if (server->active == NULL) {
+		return -1;
+	}
+
+	return 0;
+}
+
+void console_server_fini(struct console_server *server)
+{
+	for (size_t i = 0; i < server->n_consoles; i++) {
+		console_fini(server->consoles[i]);
+	}
+
+	free(server->consoles);
+	dbus_server_fini(server);
+	tty_fini(server);
+	free(server->pollfds);
+	config_fini(server->config);
+}
+
+int main(int argc, char **argv)
+{
 	const char *config_filename = NULL;
 	const char *config_tty_kname = NULL;
-	const char *buffer_size_str = NULL;
 	const char *console_id = NULL;
 	struct console_server server = { 0 };
-	struct console *console;
-	struct config *config;
-	int rc;
+	int rc = 0;
 
 	for (;;) {
 		int c;
@@ -1045,77 +1278,18 @@
 
 	if (optind < argc) {
 		config_tty_kname = argv[optind];
+	} else {
+		errx(EXIT_FAILURE, "no tty device path has been provided\n");
 	}
 
-	config = config_init(config_filename);
-	if (!config) {
-		return EXIT_FAILURE;
+	rc = console_server_init(&server, config_filename, config_tty_kname,
+				 console_id);
+
+	if (rc == 0) {
+		rc = run_server(&server);
 	}
 
-	console = malloc(sizeof(struct console));
-	if (!console) {
-		rc = -1;
-		goto out_config_fini;
-	}
-
-	memset(console, 0, sizeof(*console));
-
-	server.active = console;
-	console->server = &server;
-
-	server.pollfds = NULL;
-	server.capacity_pollfds = 0;
-
-	buffer_size_str = config_get_value(config, "ringbuffer-size");
-	if (buffer_size_str) {
-		rc = config_parse_bytesize(buffer_size_str, &buffer_size);
-		if (rc) {
-			warn("Invalid ringbuffer-size. Default to %zukB",
-			     buffer_size >> 10);
-		}
-	}
-	console->rb = ringbuffer_init(buffer_size);
-	if (!console->rb) {
-		rc = -1;
-		goto out_console_fini;
-	}
-
-	if (set_socket_info(console, config, console_id)) {
-		rc = -1;
-		goto out_ringbuffer_fini;
-	}
-
-	uart_routing_init(config);
-
-	rc = tty_init(&server, config, config_tty_kname);
-	if (rc) {
-		goto out_ringbuffer_fini;
-	}
-
-	rc = dbus_init(console, config);
-	if (rc) {
-		goto out_tty_fini;
-	}
-
-	handlers_init(console, config);
-
-	rc = run_console(console);
-
-	handlers_fini(console);
-
-out_tty_fini:
-	tty_fini(&server);
-
-out_ringbuffer_fini:
-	ringbuffer_fini(console->rb);
-
-out_console_fini:
-	free(console->pollers);
-	free(server.pollfds);
-	free(console);
-
-out_config_fini:
-	config_fini(config);
+	console_server_fini(&server);
 
 	return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }
diff --git a/console-server.h b/console-server.h
index 5213dd0..7c2e711 100644
--- a/console-server.h
+++ b/console-server.h
@@ -124,7 +124,18 @@
 	// index into pollfds
 	size_t tty_pollfd_index;
 
+	struct config *config;
+
+	// the currently active console
 	struct console *active;
+
+	struct console **consoles;
+	size_t n_consoles;
+
+	// index into (struct console_server)->pollfds
+	size_t dbus_pollfd_index;
+
+	struct sd_bus *bus;
 };
 
 struct console {
@@ -145,11 +156,6 @@
 
 	struct poller **pollers;
 	long n_pollers;
-
-	// index into (struct console_server)->pollfds
-	size_t dbus_pollfd_index;
-
-	struct sd_bus *bus;
 };
 
 /* poller API */
@@ -240,6 +246,10 @@
 /* utils */
 int write_buf_to_fd(int fd, const uint8_t *buf, size_t len);
 
+/* console_server dbus */
+int dbus_server_init(struct console_server *server);
+void dbus_server_fini(struct console_server *server);
+
 /* console-dbus API */
 int dbus_init(struct console *console,
 	      struct config *config __attribute__((unused)));