Merge branch 'travis' of https://github.com/shenki/obmc-console
diff --git a/console-server.c b/console-server.c
index ddff1af..f8feb52 100644
--- a/console-server.c
+++ b/console-server.c
@@ -31,6 +31,7 @@
 #include <string.h>
 #include <getopt.h>
 #include <limits.h>
+#include <termios.h>
 
 #include <sys/types.h>
 #include <sys/poll.h>
@@ -159,6 +160,27 @@
 }
 
 /**
+ * Set console to raw mode: we don't want any processing to occur on
+ * the underlying terminal input/output.
+ */
+static void tty_init_termios(struct console *console)
+{
+	struct termios termios;
+	int rc;
+
+	rc = tcgetattr(console->tty_fd, &termios);
+	if (rc) {
+		warn("Can't read tty termios");
+		return;
+	}
+
+	cfmakeraw(&termios);
+	rc = tcsetattr(console->tty_fd, TCSANOW, &termios);
+	if (rc)
+		warn("Can't set terminal raw mode for tty");
+}
+
+/**
  * Open and initialise the serial device
  */
 static int tty_init_io(struct console *console)
@@ -168,7 +190,6 @@
 	if (console->tty_lpc_addr)
 		tty_set_sysfs_attr(console, "lpc_address",
 				console->tty_lpc_addr);
-	tty_set_sysfs_attr(console, "enabled", 1);
 
 	console->tty_fd = open(console->tty_dev, O_RDWR);
 	if (console->tty_fd <= 0) {
@@ -181,6 +202,8 @@
 	 */
 	fcntl(console->tty_fd, F_SETFL, FNDELAY);
 
+	tty_init_termios(console);
+
 	console->pollfds[console->n_pollers].fd = console->tty_fd;
 	console->pollfds[console->n_pollers].events = POLLIN;
 
diff --git a/socket-handler.c b/socket-handler.c
index 7be9c71..7307ffe 100644
--- a/socket-handler.c
+++ b/socket-handler.c
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+#define _GNU_SOURCE
+
+#include <assert.h>
 #include <err.h>
+#include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -28,9 +32,14 @@
 
 #include "console-server.h"
 
+const size_t buffer_size_max = 100 * 1024;
+
 struct client {
 	struct poller	*poller;
 	int		fd;
+	uint8_t		*buf;
+	size_t		buf_alloc;
+	size_t		buf_len;
 };
 
 struct socket_handler {
@@ -39,7 +48,7 @@
 	struct poller	*poller;
 	int		sd;
 
-	struct client	*clients;
+	struct client	**clients;
 	int		n_clients;
 };
 
@@ -56,7 +65,14 @@
 	if (client->poller)
 		console_unregister_poller(sh->console, client->poller);
 
-	idx = client - sh->clients;
+	for (idx = 0; idx < sh->n_clients; idx++)
+		if (sh->clients[idx] == client)
+			break;
+
+	assert(idx < sh->n_clients);
+
+	free(client);
+	client = NULL;
 
 	sh->n_clients--;
 	memmove(&sh->clients[idx], &sh->clients[idx+1],
@@ -65,37 +81,105 @@
 			sizeof(*sh->clients) * sh->n_clients);
 }
 
+/* Write data to the client, until error or block.
+ *
+ * Returns -1 on hard failure, otherwise number of bytes written. A zero
+ * return indicates that no bytes were written due to potential block,
+ * but isn't a failure
+ */
+static ssize_t client_write_data(struct client *client, uint8_t *buf,
+		size_t len)
+{
+	size_t pos;
+	ssize_t rc;
+
+	for (pos = 0; pos < len; pos += rc) {
+		rc = write(client->fd, buf + pos, len - pos);
+		if (rc < 0) {
+			if (errno == EAGAIN || errno == EWOULDBLOCK)
+				break;
+
+			if (errno == EINTR)
+				continue;
+
+			return -1;
+		}
+		if (rc == 0)
+			return -1;
+	}
+
+	return pos;
+}
+
 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];
+	ssize_t len;
 	int rc;
 
-	if (!(events & POLLIN))
-		return POLLER_OK;
+	if (events & POLLIN) {
+		rc = read(client->fd, buf, sizeof(buf));
+		if (rc <= 0)
+			goto err_close;
 
-	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);
 	}
 
-	console_data_out(sh->console, buf, rc);
+	if (events & POLLOUT) {
+		len = client_write_data(client, client->buf, client->buf_len);
+		if (len < 0)
+			goto err_close;
+
+		/* consume from the queue */
+		client->buf_len -= len;
+		memmove(client->buf, client->buf + len,
+				client->buf_len);
+	}
 
 	return POLLER_OK;
+
+err_close:
+	client->poller = NULL;
+	client_close(sh, client);
+	return POLLER_REMOVE;
 }
 
-static void client_send_data(struct socket_handler *sh,
-		struct client *client, uint8_t *buf, size_t len)
+static int client_queue_data(struct client *client, uint8_t *buf, size_t len)
 {
-	int rc;
+	if (client->buf_len + len > client->buf_alloc) {
+		if (!client->buf_alloc)
+			client->buf_alloc = 2048;
+		client->buf_alloc *= 2;
 
-	rc = write_buf_to_fd(client->fd, buf, len);
-	if (rc)
-		client_close(sh, client);
+		if (client->buf_alloc > buffer_size_max)
+			return -1;
+
+		client->buf = realloc(client->buf, client->buf_alloc);
+	}
+
+	memcpy(client->buf + client->buf_len, buf, len);
+	client->buf_len += len;
+	return 0;
+}
+
+static int client_send_or_queue(struct client *client, uint8_t *buf, size_t len)
+{
+	ssize_t rc;
+
+	rc = client_write_data(client, buf, len);
+	if (rc < 0)
+		return -1;
+
+	if ((size_t)rc < len) {
+		rc = client_queue_data(client, buf + rc, len - rc);
+		if (rc)
+			return -1;
+	}
+
+	return 0;
 }
 
 static enum poller_ret socket_poll(struct handler *handler,
@@ -108,18 +192,22 @@
 	if (!(events & POLLIN))
 		return POLLER_OK;
 
-	fd = accept(sh->sd, NULL, NULL);
+	fd = accept4(sh->sd, NULL, NULL, SOCK_NONBLOCK);
 	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 = malloc(sizeof(*client));
+	memset(client, 0, sizeof(*client));
 
 	client->fd = fd;
 	client->poller = console_register_poller(sh->console, handler,
 			client_poll, client->fd, POLLIN, client);
 
+	n = sh->n_clients++;
+	sh->clients = realloc(sh->clients,
+			sizeof(*sh->clients) * sh->n_clients);
+	sh->clients[n] = client;
+
 	return POLLER_OK;
 
 }
@@ -167,11 +255,20 @@
 static int socket_data(struct handler *handler, uint8_t *buf, size_t len)
 {
 	struct socket_handler *sh = to_socket_handler(handler);
-	int i;
+	int i, rc;
 
 	for (i = 0; i < sh->n_clients; i++) {
-		struct client *client = &sh->clients[i];
-		client_send_data(sh, client, buf, len);
+		struct client *client = sh->clients[i];
+		rc = client_send_or_queue(client, buf, len);
+		if (!rc)
+			continue;
+
+		/* if we failed to send data, close the client. This will
+		 * remove it from the clients array, so skip back to the item
+		 * that has taken its place
+		 */
+		client_close(sh, client);
+		i--;
 	}
 	return 0;
 }
@@ -182,7 +279,7 @@
 	int i;
 
 	for (i = 0; i < sh->n_clients; i++)
-		client_close(sh, &sh->clients[i]);
+		client_close(sh, sh->clients[i]);
 
 	if (sh->poller)
 		console_unregister_poller(sh->console, sh->poller);