console-client: Support an alternate string escape mode

This mode is intended to avoid the behavior of witholding any characters
from the console-server. It sends the entire buffer regardless of
matching the escape sequence, however it will immediately terminate
after seeing a full escape sequence and sending the remaining buffered
content.

Tested:
    Ran obmc-console-client with no arguments and ensured the ssh-like
    escape sequence still worked. Then ran `obmc-console-client -e esc`,
    typed a bunch of text into the console followed by esc and watched
    the console exit as expected.

Change-Id: I780719d6e2402f93bba81beb8c09e1c661471e22
Signed-off-by: William A. Kennington III <wak@google.com>
diff --git a/console-client.c b/console-client.c
index 57ca206..3f3f519 100644
--- a/console-client.c
+++ b/console-client.c
@@ -15,6 +15,7 @@
  */
 
 #include <err.h>
+#include <getopt.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -36,12 +37,18 @@
 
 enum esc_type {
 	ESC_TYPE_SSH,
+	ESC_TYPE_STR,
 };
 
 struct ssh_esc_state {
 	uint8_t state;
 };
 
+struct str_esc_state {
+	const uint8_t *str;
+	size_t pos;
+};
+
 struct console_client {
 	int		console_sd;
 	int		fd_in;
@@ -51,6 +58,7 @@
 	enum esc_type	esc_type;
 	union {
 		struct ssh_esc_state ssh;
+		struct str_esc_state str;
 	} esc_state;
 };
 
@@ -95,6 +103,30 @@
 	return rc < 0 ? PROCESS_ERR : PROCESS_OK;
 }
 
+static enum process_rc process_str_tty(
+		struct console_client *client, const uint8_t *buf, size_t len)
+{
+	struct str_esc_state *esc_state = &client->esc_state.str;
+	enum process_rc prc = PROCESS_OK;
+	size_t i;
+
+	for (i = 0; i < len; ++i) {
+		if (buf[i] == esc_state->str[esc_state->pos])
+			esc_state->pos++;
+		else
+			esc_state->pos = 0;
+
+		if (esc_state->str[esc_state->pos] == '\0') {
+			prc = PROCESS_EXIT;
+			break;
+		}
+	}
+
+	if (write_buf_to_fd(client->console_sd, buf, i) < 0)
+		return PROCESS_ERR;
+	return prc;
+}
+
 static enum process_rc process_tty(struct console_client *client)
 {
 	uint8_t buf[4096];
@@ -110,6 +142,8 @@
 	{
 	case ESC_TYPE_SSH:
 		return process_ssh_tty(client, buf, len);
+	case ESC_TYPE_STR:
+		return process_str_tty(client, buf, len);
 	default:
 		return PROCESS_ERR;
 	}
@@ -201,7 +235,7 @@
 	close(client->console_sd);
 }
 
-int main(void)
+int main(int argc, char *argv[])
 {
 	struct console_client _client, *client;
 	struct pollfd pollfds[2];
@@ -212,6 +246,29 @@
 	memset(client, 0, sizeof(*client));
 	client->esc_type = ESC_TYPE_SSH;
 
+	for (;;) {
+		rc = getopt(argc, argv, "e:");
+		if (rc == -1)
+			break;
+
+		switch (rc) {
+		case 'e':
+			if (optarg[0] == '\0') {
+				fprintf(stderr, "Escape str cannot be empty\n");
+				return EXIT_FAILURE;
+			}
+			client->esc_type = ESC_TYPE_STR;
+			client->esc_state.str.str = (const uint8_t*)optarg;
+			break;
+		default:
+			fprintf(stderr,
+				"Usage: %s "
+				"[-e <escape sequence>]\n",
+				argv[0]);
+			return EXIT_FAILURE;
+		}
+	}
+
 	rc = client_init(client);
 	if (rc)
 		return EXIT_FAILURE;