Add socket handler & console client
This change adds a socket handler to the console-server, exposing a unix
domain socket for the UART data. We use this to replace the existing
stdio handler.
We also add a client for this socket, allowing multiple processes to
read/write data from & to the server.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
diff --git a/socket-handler.c b/socket-handler.c
new file mode 100644
index 0000000..8e0cebc
--- /dev/null
+++ b/socket-handler.c
@@ -0,0 +1,177 @@
+
+#include <err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <endian.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "console-server.h"
+
+struct client {
+ struct poller *poller;
+ int fd;
+};
+
+struct socket_handler {
+ struct handler handler;
+ struct console *console;
+ int sd;
+
+ struct client *clients;
+ int n_clients;
+};
+
+static struct socket_handler *to_socket_handler(struct handler *handler)
+{
+ return container_of(handler, struct socket_handler, handler);
+}
+
+static void client_close(struct socket_handler *sh, struct client *client)
+{
+ int idx;
+
+ close(client->fd);
+ if (client->poller)
+ console_unregister_poller(sh->console, client->poller);
+
+ idx = client - sh->clients;
+
+ sh->n_clients--;
+ memmove(&sh->clients[idx], &sh->clients[idx+1],
+ sizeof(*sh->clients) * (sh->n_clients - idx));
+ sh->clients = realloc(sh->clients, sizeof(sh->clients) * sh->n_clients);
+}
+
+static enum poller_ret client_poll(struct handler *handler,
+ int events, void *data)
+{
+ struct socket_handler *sh = to_socket_handler(handler);
+ struct client *client = data;
+ uint8_t buf[4096];
+ int rc;
+
+ if (!(events & POLLIN))
+ return POLLER_OK;
+
+ rc = read(client->fd, buf, sizeof(buf));
+ if (rc <= 0) {
+ client->poller = NULL;
+ client_close(sh, client);
+ return POLLER_REMOVE;
+ }
+
+ console_data_out(sh->console, buf, rc);
+
+ return POLLER_OK;
+}
+
+static void client_send_data(struct socket_handler *sh,
+ struct client *client, uint8_t *buf, size_t len)
+{
+ int rc;
+
+ rc = write_buf_to_fd(client->fd, buf, len);
+ if (rc)
+ client_close(sh, client);
+}
+
+static enum poller_ret socket_poll(struct handler *handler,
+ int events, void __attribute__((unused)) *data)
+{
+ struct socket_handler *sh = to_socket_handler(handler);
+ struct client *client;
+ int fd, n;
+
+ if (!(events & POLLIN))
+ return POLLER_OK;
+
+ fd = accept(sh->sd, NULL, NULL);
+ if (fd < 0)
+ return POLLER_OK;
+
+ n = sh->n_clients++;
+ sh->clients = realloc(sh->clients, sizeof(*client) * sh->n_clients);
+ client = &sh->clients[n];
+
+ client->fd = fd;
+ client->poller = console_register_poller(sh->console, handler,
+ client_poll, client->fd, POLLIN, client);
+
+ return POLLER_OK;
+
+}
+
+static int socket_init(struct handler *handler, struct console *console)
+{
+ struct socket_handler *sh = to_socket_handler(handler);
+ struct sockaddr_un addr;
+ int rc;
+
+ sh->console = console;
+ sh->clients = NULL;
+ sh->n_clients = 0;
+
+ sh->sd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if(sh->sd < 0) {
+ warn("Can't create socket");
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, console_socket_path, console_socket_path_len);
+
+ rc = bind(sh->sd, (struct sockaddr *)&addr, sizeof(addr));
+ if (rc) {
+ warn("Can't bind to socket path %s",
+ console_socket_path_readable);
+ return -1;
+ }
+
+ rc = listen(sh->sd, 1);
+ if (rc) {
+ warn("Can't listen for incoming connections");
+ return -1;
+ }
+
+ console_register_poller(console, handler, socket_poll, sh->sd, POLLIN,
+ NULL);
+
+ return 0;
+}
+
+static int socket_data(struct handler *handler, uint8_t *buf, size_t len)
+{
+ struct socket_handler *sh = to_socket_handler(handler);
+ int i;
+
+ for (i = 0; i < sh->n_clients; i++) {
+ struct client *client = &sh->clients[i];
+ client_send_data(sh, client, buf, len);
+ }
+ return 0;
+}
+
+static void socket_fini(struct handler *handler)
+{
+ struct socket_handler *sh = to_socket_handler(handler);
+ close(sh->sd);
+}
+
+static struct socket_handler socket_handler = {
+ .handler = {
+ .name = "socket",
+ .init = socket_init,
+ .data_in = socket_data,
+ .fini = socket_fini,
+ },
+};
+
+console_register_handler(&socket_handler.handler);
+