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);