Mailing List Archive

[PATCH] allow connecting to xenconsole from remote hosts
The attached patch makes xenconsole send and receive console messages
over a remote connection, instead of via stdin/stdout - if given the
--remote switch. It is useful for proxy'ing real console message or
other protocol messages (such as gdb) between dom0 and a remote
host. We're currently using it for proxying gdb between gdbstub in a
partition that talks gdb over the console page to a remote host
running gdb.

Changes from previous version:
- removed 'noecho' support, per Keir's comments
- fixed a bug in write_all to handle EOF properly

To use:

In dom0: xenconsole --remote --gateway --port $PORT $DOMID

In remote host: telnet $DOM0IP $PORT, or # nc $DOM0IP $PORT, or gdb's
target remote command

Signed-off-by: Muli Ben-Yehuda <muli@il.ibm.com>

diff -r ec03b24a2d83 -r 4ef3855d67a1 tools/console/client/main.c
--- a/tools/console/client/main.c Tue Aug 15 19:53:55 2006 +0100
+++ b/tools/console/client/main.c Thu Sep 07 13:12:44 2006 +0300
@@ -1,8 +1,9 @@
/*\
- * Copyright (C) International Business Machines Corp., 2005
- * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) International Business Machines Corp., 2005, 2006
+ * Author: Anthony Liguori <aliguori@us.ibm.com>
+ * Author: Muli Ben-Yehuda <muli@il.ibm.com>
*
- * Xen Console Daemon
+ * Xen Console Client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,32 +36,56 @@
#include <err.h>
#include <errno.h>
#include <pty.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdarg.h>

#include "xs.h"

#define ESCAPE_CHARACTER 0x1d
+#define DEFAULT_LISTEN_PORT 7890
+#define MSG_SIZE 512
+
+struct remote {
+ long port;
+ int do_listen;
+ int gateway;
+ int server; /* server socket */
+};
+
+struct message {
+ struct message *next;
+ char* data;
+ size_t len;
+};
+
+struct queue {
+ struct message *head;
+ struct message *tail;
+};

static volatile sig_atomic_t received_signal = 0;

+static int debug;
+
+#define dbg(fmt, args...) do { \
+ if (debug) \
+ _dbg("[%s] " fmt, __func__, ##args); \
+} while (0)
+
+static int _dbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fflush(stderr);
+}
+
static void sighandler(int signum)
{
received_signal = 1;
-}
-
-static bool write_sync(int fd, const void *data, size_t size)
-{
- size_t offset = 0;
- ssize_t len;
-
- while (offset < size) {
- len = write(fd, data + offset, size - offset);
- if (len < 1) {
- return false;
- }
- offset += len;
- }
-
- return true;
}

static void usage(const char *program) {
@@ -68,7 +93,33 @@ static void usage(const char *program) {
"Attaches to a virtual domain console\n"
"\n"
" -h, --help display this help and exit\n"
+ " -r, --remote wait for connections from local clients\n"
+ " -g, --gateway allow connections from any host\n"
+ " -d, --debug enable debug output\n"
, program);
+}
+
+ssize_t write_all(int fd, const void *buf, size_t count)
+{
+ const unsigned char* b = (const unsigned char*)buf;
+ ssize_t sum = 0;
+ ssize_t ret = 0;
+
+ while (count) {
+ ret = write(fd, b, count);
+ if (ret == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ ret = -errno;
+ break;
+ }
+ if (ret == 0)
+ break;
+ count -= ret;
+ b += ret;
+ sum += ret;
+ }
+ return (ret >= 0 ? sum : ret);
}

/* don't worry too much if setting terminal attributes fail */
@@ -91,79 +142,333 @@ static void restore_term(int fd, struct
tcsetattr(fd, TCSAFLUSH, old);
}

-static int console_loop(int fd)
-{
- int ret;
+static struct message *alloc_msg(char* data, size_t len)
+{
+ struct message *msg;
+
+ msg = malloc(sizeof(*msg));
+ if (!msg)
+ return NULL;
+
+ memset(msg, 0, sizeof(*msg));
+
+ msg->next = NULL;
+ msg->data = data;
+ msg->len = len;
+
+ return msg;
+}
+
+static void destroy_msg(struct message *msg)
+{
+ msg->len = -1;
+ free(msg->data);
+ msg->data = (char*)0xBADF00D1;
+ free(msg);
+}
+
+static void __queue_msg(struct queue *q, struct message *msg)
+{
+ if (q->tail)
+ q->tail->next = msg;
+ else
+ q->head = q->tail = msg;
+}
+
+static int queue_msg(struct queue *q, char *data, size_t len)
+{
+ struct message *msg;
+
+ msg = alloc_msg(data, len);
+ if (!msg)
+ return -ENOMEM;
+
+ __queue_msg(q, msg);
+
+ return 0;
+}
+
+static int dequeue_msg(struct queue *q, struct message **pmsg)
+{
+ struct message *tmp;
+
+ tmp = q->head;
+ if (!tmp)
+ return 0; /* nothing to do */
+
+ q->head = tmp->next;
+
+ if (q->tail == tmp)
+ q->tail = tmp->next;
+
+ *pmsg = tmp;
+ return 1;
+}
+
+static int same_msg(const char *m1, size_t len1, const struct message *m2)
+{
+ int ret;
+
+ if (len1 != m2->len)
+ return 0;
+
+ ret = !memcmp(m1, m2->data, len1);
+
+ return ret;
+}
+
+static inline void dump_msg(char* prefix, int fd, const char *data, size_t len)
+{
+ dbg("%s: %u bytes on %d:\n", prefix, len, fd);
+ dbg("msg: `%s'\n", data);
+}
+
+static int handle_read_fd(int fd, struct queue* q)
+{
+ int ret;
+ ssize_t len;
+ char *msg;
+
+ msg = malloc(MSG_SIZE);
+ if (!msg)
+ return -ENOMEM;
+
+ len = read(fd, msg, MSG_SIZE);
+ if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
+ ret = ECONNRESET;
+ goto free_msg;
+ }
+
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ ret = EINTR;
+ else
+ ret = -errno;
+ goto free_msg;
+ }
+ if (len == 0) {
+ /* try to reconnect */
+ ret = EAGAIN;
+ goto free_msg;
+ }
+
+ dump_msg("read", fd, msg, len);
+
+ dbg("queing %p on queue %p\n", msg, q);
+ ret = queue_msg(q, msg, len);
+ goto done;
+
+ free_msg:
+ free(msg);
+ done:
+ return ret;
+}
+
+static int handle_write_fd(int fd, struct queue* q)
+{
+ int ret;
+ struct message *pmsg;

do {
- fd_set fds;
-
- FD_ZERO(&fds);
- FD_SET(STDIN_FILENO, &fds);
- FD_SET(fd, &fds);
-
- ret = select(fd + 1, &fds, NULL, NULL, NULL);
+ ret = dequeue_msg(q, &pmsg);
+
+ if (ret < 0) /* error */
+ goto done;
+
+ /* no more messages */
+ if (ret == 0)
+ goto done;
+
+ dump_msg("write", fd, pmsg->data, pmsg->len);
+
+ ret = write_all(fd, pmsg->data, pmsg->len);
+ if (ret < 0)
+ goto free_msg;
+
+ destroy_msg(pmsg);
+ } while (1);
+
+ free_msg:
+ destroy_msg(pmsg);
+ done:
+ return ret;
+}
+
+static int console_loop(int conspty, int infd, int outfd)
+{
+ int ret;
+ int max;
+
+ struct queue console = {
+ .head = NULL,
+ .tail = NULL,
+ };
+
+ struct queue out = {
+ .head = NULL,
+ .tail = NULL,
+ };
+
+ do {
+ fd_set rfds, wfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(infd, &rfds);
+ FD_SET(conspty, &rfds);
+
+ FD_ZERO(&wfds);
+ FD_SET(conspty, &wfds);
+ FD_SET(outfd, &wfds);
+
+ max = (conspty | infd | outfd) + 1;
+
+ ret = select(max, &rfds, &wfds, NULL, NULL);
+
if (ret == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
- return -1;
- }
-
- if (FD_ISSET(STDIN_FILENO, &fds)) {
- ssize_t len;
- char msg[60];
-
- len = read(STDIN_FILENO, msg, sizeof(msg));
- if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
+ return -errno;
+ }
+
+ if (FD_ISSET(infd, &rfds)) {
+ ret = handle_read_fd(infd, &console);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
return 0;
- }
-
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
-
- if (!write_sync(fd, msg, len)) {
- perror("write() failed");
- return -1;
- }
- }
-
- if (FD_ISSET(fd, &fds)) {
- ssize_t len;
- char msg[512];
-
- len = read(fd, msg, sizeof(msg));
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
-
- if (!write_sync(STDOUT_FILENO, msg, len)) {
- perror("write() failed");
- return -1;
- }
+ }
+
+ if (FD_ISSET(conspty, &rfds)) {
+ ret = handle_read_fd(conspty, &out);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
+ return 0;
+ }
+
+ if (FD_ISSET(outfd, &wfds)) {
+ ret = handle_write_fd(outfd, &out);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (FD_ISSET(conspty, &wfds)) {
+ ret = handle_write_fd(conspty, &console);
+ if (ret < 0)
+ return ret;
}
} while (received_signal == 0);

return 0;
}

+static int start_server(struct remote *remote)
+{
+ struct sockaddr_in sa;
+ struct hostent *he = NULL;
+ int ret;
+ int on;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(remote->port);
+
+ if (!remote->gateway) {
+ he = gethostbyname("localhost");
+ if (!he)
+ err(h_errno, "could not get localhost address\n");
+ memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
+ } else
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ remote->server = socket(AF_INET, SOCK_STREAM, 0);
+ if (remote->server < 0)
+ err(errno, "socket failed\n");
+
+ on = 1;
+ ret = setsockopt(remote->server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret)
+ err(errno, "setsockopt(SO_REUSEADDR) failed\n");
+
+ ret = bind(remote->server, (struct sockaddr *)&sa, sizeof(sa));
+ if (ret)
+ err(errno, "bind failed\n");
+
+ ret = listen(remote->server, 5);
+ if (ret)
+ err(errno, "listen failed\n");
+
+ return ret;
+}
+
+static int remote_loop(int conspty, struct remote *remote)
+{
+ int in;
+ int out;
+ int socket;
+ int ret;
+
+ ret = start_server(remote);
+ if (ret)
+ return ret;
+
+ do {
+ socket = accept(remote->server, NULL, 0);
+ if (socket < 0)
+ err(errno, "accept failed\n");
+
+ in = socket;
+ out = socket;
+
+ ret = console_loop(conspty, in, out);
+ } while (ret == EAGAIN);
+
+ return ret;
+}
+
+static int main_loop(int conspty, struct remote *remote)
+{
+ int in;
+ int out;
+ int ret;
+ struct termios attr;
+
+ if (remote->do_listen)
+ return remote_loop(conspty, remote);
+
+ init_term(fileno(stdin), &attr);
+
+ in = fileno(stdin);
+ out = fileno(stdout);
+
+ ret = console_loop(conspty, in, out);
+
+ restore_term(in, &attr);
+
+ return ret;
+}
+
int main(int argc, char **argv)
{
- struct termios attr;
int domid;
- char *sopt = "h";
+ char *sopt = "rp:gdh";
int ch;
int opt_ind=0;
struct option lopt[] = {
+ { "remote", 0, NULL, 'r' },
+ { "port", 1, NULL, 'p' },
+ { "gateway", 0, NULL, 'g' },
+ { "debug", 0, NULL, 'd' },
{ "help", 0, 0, 'h' },
{ 0 },

@@ -174,12 +479,38 @@ int main(int argc, char **argv)
struct xs_handle *xs;
char *end;
time_t now;
+ struct remote remote = {
+ .port = DEFAULT_LISTEN_PORT,
+ .do_listen = 0,
+ .gateway = 0,
+ .server = -1,
+ };
+ int ret;

while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
switch(ch) {
case 'h':
usage(argv[0]);
exit(0);
+ break;
+ case 'r':
+ remote.do_listen = 1;
+ break;
+ case 'p':
+ remote.port = strtol(optarg, &end, 10);
+ if (end && *end) {
+ fprintf(stderr, "Invalid port `%s' specified\n",
+ optarg);
+ fprintf(stderr, "Try `%s --help' for more "
+ "information.\n", argv[0]);
+ exit(EINVAL);
+ }
+ break;
+ case 'g':
+ remote.gateway = 1;
+ break;
+ case 'd':
+ debug = 1;
break;
}
}
@@ -194,6 +525,14 @@ int main(int argc, char **argv)
domid = strtol(argv[optind], &end, 10);
if (end && *end) {
fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]);
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(EINVAL);
+ }
+
+ if (remote.gateway && !remote.do_listen) {
+ fprintf(stderr, "setting `gateway' requires also setting "
+ "`remote'\n");
fprintf(stderr, "Try `%s --help' for more information.\n",
argv[0]);
exit(EINVAL);
@@ -252,9 +591,9 @@ int main(int argc, char **argv)
free(str_pty);
free(path);

- init_term(STDIN_FILENO, &attr);
- console_loop(spty);
- restore_term(STDIN_FILENO, &attr);
-
- return 0;
- }
+ ret = main_loop(spty, &remote);
+
+ xs_daemon_close(xs);
+
+ return ret;
+}

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On 7/9/06 08:28, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:

> The attached patch makes xenconsole send and receive console messages
> over a remote connection, instead of via stdin/stdout - if given the
> --remote switch. It is useful for proxy'ing real console message or
> other protocol messages (such as gdb) between dom0 and a remote
> host. We're currently using it for proxying gdb between gdbstub in a
> partition that talks gdb over the console page to a remote host
> running gdb.

You still have that message/queue struct stuff. Is it needed, especially now
that noecho is gone?

-- Keir



_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On Wed, Sep 13, 2006 at 02:37:31PM +0100, Keir Fraser wrote:

> On 7/9/06 08:28, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:
>
> > The attached patch makes xenconsole send and receive console messages
> > over a remote connection, instead of via stdin/stdout - if given the
> > --remote switch. It is useful for proxy'ing real console message or
> > other protocol messages (such as gdb) between dom0 and a remote
> > host. We're currently using it for proxying gdb between gdbstub in a
> > partition that talks gdb over the console page to a remote host
> > running gdb.
>
> You still have that message/queue struct stuff. Is it needed, especially now
> that noecho is gone?

I need someplace to keep what we read from a source fd until the
destination fd becomes writable. The original code just assumed
everything is writable and wrote it immediately; this assumption may
not be correct when the destination fd is a socket.

Cheers,
Muli



_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On 13/9/06 14:59, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:

>> You still have that message/queue struct stuff. Is it needed, especially now
>> that noecho is gone?
>
> I need someplace to keep what we read from a source fd until the
> destination fd becomes writable. The original code just assumed
> everything is writable and wrote it immediately; this assumption may
> not be correct when the destination fd is a socket.

A character array would be more obvious. This will also allow an easy
implementation of console logging in the future -- the character array will
simply be an mmap'ed log file, where the log is rotated whenever the cursor
reaches the end of the buffer array.

-- Keir



_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On Wed, Sep 13, 2006 at 03:08:48PM +0100, Keir Fraser wrote:

> On 13/9/06 14:59, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:
>
> >> You still have that message/queue struct stuff. Is it needed, especially now
> >> that noecho is gone?
> >
> > I need someplace to keep what we read from a source fd until the
> > destination fd becomes writable. The original code just assumed
> > everything is writable and wrote it immediately; this assumption may
> > not be correct when the destination fd is a socket.
>
> A character array would be more obvious. This will also allow an easy
> implementation of console logging in the future -- the character array will
> simply be an mmap'ed log file, where the log is rotated whenever the cursor
> reaches the end of the buffer array.

Something like this? the ring buffer handling code is a bit dodgy but
it survives valgrind and a 'find /' with a ring size of 64, so there's
a reasonable chance that it's actually correct. If you prefer simpler
ring buffer code where we read into a temporary buffer into of into
the ring buffer directly, that's cool too.

The attached patch makes xenconsole send and receive console messages
over a remote connection, instead of via stdin/stdout - if given the
--remote switch. It is useful for proxy'ing real console message or
other protocol messages (such as gdb) between dom0 and a remote
host. We're currently using it for proxying gdb between gdbstub in a
partition that talks gdb over the console page to a remote host
running gdb

Changes since last version:
- get rid of the 'queue of messages', instead each queueu is a ring
buffer of characters that we read into and write from directly.
- fix buglet where we would exit with a negative value.

Signed-off-by: Muli Ben-Yehuda <muli@il.ibm.com>

--- a/tools/console/client/main.c Tue Aug 15 19:53:55 2006 +0100
+++ b/tools/console/client/main.c Thu Sep 14 16:58:40 2006 +0300
@@ -1,8 +1,9 @@
/*\
- * Copyright (C) International Business Machines Corp., 2005
- * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) International Business Machines Corp., 2005, 2006
+ * Author: Anthony Liguori <aliguori@us.ibm.com>
+ * Author: Muli Ben-Yehuda <muli@il.ibm.com>
*
- * Xen Console Daemon
+ * Xen Console Client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,32 +36,78 @@
#include <err.h>
#include <errno.h>
#include <pty.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdarg.h>

#include "xs.h"

#define ESCAPE_CHARACTER 0x1d
+#define DEFAULT_LISTEN_PORT 7890
+#define MSG_SIZE 512
+
+struct remote {
+ long port;
+ int do_listen;
+ int gateway;
+ int server; /* server socket */
+};
+
+#define QUEUE_SIZE 65536
+
+struct queue {
+ size_t readp;
+ size_t writep;
+ unsigned char q[QUEUE_SIZE];
+};
+
+static size_t read_available(struct queue *q)
+{
+ if (q->readp == q->writep) {
+ if (q->readp == QUEUE_SIZE - 1)
+ return 1;
+ else
+ return QUEUE_SIZE - q->readp - 1;
+ } else if (q->readp > q->writep)
+ return QUEUE_SIZE - q->readp;
+ else { /* q->readp < q->writep */
+ return q->writep - q->readp - 1;
+ }
+}
+
+static size_t write_available(struct queue *q)
+{
+ if (q->readp == q->writep)
+ return 0;
+ else if (q->readp > q->writep)
+ return q->readp - q->writep;
+ else { /* wraparound - readp < writep */
+ return (QUEUE_SIZE - q->writep);
+ }
+}

static volatile sig_atomic_t received_signal = 0;

+static int debug;
+
+#define dbg(fmt, args...) do { \
+ if (debug) \
+ _dbg("[%s] " fmt, __func__, ##args); \
+} while (0)
+
+static int _dbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fflush(stderr);
+}
+
static void sighandler(int signum)
{
received_signal = 1;
-}
-
-static bool write_sync(int fd, const void *data, size_t size)
-{
- size_t offset = 0;
- ssize_t len;
-
- while (offset < size) {
- len = write(fd, data + offset, size - offset);
- if (len < 1) {
- return false;
- }
- offset += len;
- }
-
- return true;
}

static void usage(const char *program) {
@@ -68,7 +115,33 @@ static void usage(const char *program) {
"Attaches to a virtual domain console\n"
"\n"
" -h, --help display this help and exit\n"
+ " -r, --remote wait for connections from local clients\n"
+ " -g, --gateway allow connections from any host\n"
+ " -d, --debug enable debug output\n"
, program);
+}
+
+ssize_t write_all(int fd, const void *buf, size_t count)
+{
+ const unsigned char* b = (const unsigned char*)buf;
+ ssize_t sum = 0;
+ ssize_t ret = 0;
+
+ while (count) {
+ ret = write(fd, b, count);
+ if (ret == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ ret = -errno;
+ break;
+ }
+ if (ret == 0)
+ break;
+ count -= ret;
+ b += ret;
+ sum += ret;
+ }
+ return (ret >= 0 ? sum : ret);
}

/* don't worry too much if setting terminal attributes fail */
@@ -91,79 +164,244 @@ static void restore_term(int fd, struct
tcsetattr(fd, TCSAFLUSH, old);
}

-static int console_loop(int fd)
-{
- int ret;
+static inline void dump_msg(char* prefix, int fd, const char *data, size_t len)
+{
+ dbg("%s: %u bytes on %d:\n", prefix, len, fd);
+ dbg("msg: `%s'\n", data);
+}
+
+static int handle_read_fd(int fd, struct queue* q)
+{
+ int ret;
+ ssize_t len;
+ unsigned char *msg;
+ size_t avail;
+
+ msg = &q->q[q->readp];
+ avail = read_available(q);
+ if (avail == 0)
+ return 0;
+
+ len = read(fd, msg, avail);
+ if (len == 1 && msg[0] == ESCAPE_CHARACTER)
+ return ECONNRESET;
+
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ ret = EINTR;
+ else
+ ret = -errno;
+ return ret;
+ }
+ if (len == 0) {
+ /* try to reconnect */
+ return EAGAIN;
+ }
+
+ dump_msg("read", fd, msg, len);
+
+ q->readp = (q->readp + len) % QUEUE_SIZE;
+
+ return 0;
+}
+
+static int handle_write_fd(int fd, struct queue* q)
+{
+ int ret;
+ struct message *pmsg;
+ size_t len;
+
+ len = write_available(q);
+ while (len) {
+ dump_msg("write", fd, &q->q[q->writep], len);
+
+ ret = write_all(fd, &q->q[q->writep], len);
+ if (ret < 0)
+ return ret;
+
+ q->writep = (q->writep + len) % QUEUE_SIZE;
+ len = write_available(q);
+ }
+ return 0;
+}
+
+static int console_loop(int conspty, int infd, int outfd)
+{
+ int ret;
+ int max;
+
+ struct queue console = {
+ .readp = 0,
+ .writep = 0,
+ .q = { '\0' }
+ };
+
+ struct queue out = {
+ .readp = 0,
+ .writep = 0,
+ .q = { '\0' }
+ };

do {
- fd_set fds;
-
- FD_ZERO(&fds);
- FD_SET(STDIN_FILENO, &fds);
- FD_SET(fd, &fds);
-
- ret = select(fd + 1, &fds, NULL, NULL, NULL);
+ fd_set rfds, wfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(infd, &rfds);
+ FD_SET(conspty, &rfds);
+
+ FD_ZERO(&wfds);
+ FD_SET(conspty, &wfds);
+ FD_SET(outfd, &wfds);
+
+ max = (conspty | infd | outfd) + 1;
+
+ ret = select(max, &rfds, &wfds, NULL, NULL);
+
if (ret == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
- return -1;
- }
-
- if (FD_ISSET(STDIN_FILENO, &fds)) {
- ssize_t len;
- char msg[60];
-
- len = read(STDIN_FILENO, msg, sizeof(msg));
- if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
+ return -errno;
+ }
+
+ if (FD_ISSET(infd, &rfds)) {
+ ret = handle_read_fd(infd, &console);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
return 0;
- }
-
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
-
- if (!write_sync(fd, msg, len)) {
- perror("write() failed");
- return -1;
- }
- }
-
- if (FD_ISSET(fd, &fds)) {
- ssize_t len;
- char msg[512];
-
- len = read(fd, msg, sizeof(msg));
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
-
- if (!write_sync(STDOUT_FILENO, msg, len)) {
- perror("write() failed");
- return -1;
- }
+ }
+
+ if (FD_ISSET(conspty, &rfds)) {
+ ret = handle_read_fd(conspty, &out);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
+ return 0;
+ }
+
+ if (FD_ISSET(outfd, &wfds)) {
+ ret = handle_write_fd(outfd, &out);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (FD_ISSET(conspty, &wfds)) {
+ ret = handle_write_fd(conspty, &console);
+ if (ret < 0)
+ return ret;
}
} while (received_signal == 0);

return 0;
}

+static int start_server(struct remote *remote)
+{
+ struct sockaddr_in sa;
+ struct hostent *he = NULL;
+ int ret;
+ int on;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(remote->port);
+
+ if (!remote->gateway) {
+ he = gethostbyname("localhost");
+ if (!he)
+ err(h_errno, "could not get localhost address\n");
+ memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
+ } else
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ remote->server = socket(AF_INET, SOCK_STREAM, 0);
+ if (remote->server < 0)
+ err(errno, "socket failed\n");
+
+ on = 1;
+ ret = setsockopt(remote->server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret)
+ err(errno, "setsockopt(SO_REUSEADDR) failed\n");
+
+ ret = bind(remote->server, (struct sockaddr *)&sa, sizeof(sa));
+ if (ret)
+ err(errno, "bind failed\n");
+
+ ret = listen(remote->server, 5);
+ if (ret)
+ err(errno, "listen failed\n");
+
+ return ret;
+}
+
+static int remote_loop(int conspty, struct remote *remote)
+{
+ int in;
+ int out;
+ int socket;
+ int ret;
+
+ ret = start_server(remote);
+ if (ret)
+ return ret;
+
+ do {
+ socket = accept(remote->server, NULL, 0);
+ if (socket < 0)
+ err(errno, "accept failed\n");
+
+ in = socket;
+ out = socket;
+
+ ret = console_loop(conspty, in, out);
+ } while (ret == EAGAIN);
+
+ return ret;
+}
+
+static int main_loop(int conspty, struct remote *remote)
+{
+ int in;
+ int out;
+ int ret;
+ struct termios attr;
+
+ if (remote->do_listen)
+ return remote_loop(conspty, remote);
+
+ init_term(fileno(stdin), &attr);
+
+ in = fileno(stdin);
+ out = fileno(stdout);
+
+ ret = console_loop(conspty, in, out);
+
+ restore_term(in, &attr);
+
+ return ret;
+}
+
int main(int argc, char **argv)
{
- struct termios attr;
int domid;
- char *sopt = "h";
+ char *sopt = "rp:gdh";
int ch;
int opt_ind=0;
struct option lopt[] = {
+ { "remote", 0, NULL, 'r' },
+ { "port", 1, NULL, 'p' },
+ { "gateway", 0, NULL, 'g' },
+ { "debug", 0, NULL, 'd' },
{ "help", 0, 0, 'h' },
{ 0 },

@@ -174,12 +412,38 @@ int main(int argc, char **argv)
struct xs_handle *xs;
char *end;
time_t now;
+ struct remote remote = {
+ .port = DEFAULT_LISTEN_PORT,
+ .do_listen = 0,
+ .gateway = 0,
+ .server = -1,
+ };
+ int ret;

while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
switch(ch) {
case 'h':
usage(argv[0]);
exit(0);
+ break;
+ case 'r':
+ remote.do_listen = 1;
+ break;
+ case 'p':
+ remote.port = strtol(optarg, &end, 10);
+ if (end && *end) {
+ fprintf(stderr, "Invalid port `%s' specified\n",
+ optarg);
+ fprintf(stderr, "Try `%s --help' for more "
+ "information.\n", argv[0]);
+ exit(EINVAL);
+ }
+ break;
+ case 'g':
+ remote.gateway = 1;
+ break;
+ case 'd':
+ debug = 1;
break;
}
}
@@ -194,6 +458,14 @@ int main(int argc, char **argv)
domid = strtol(argv[optind], &end, 10);
if (end && *end) {
fprintf(stderr, "Invalid DOMID `%s'\n", argv[optind]);
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(EINVAL);
+ }
+
+ if (remote.gateway && !remote.do_listen) {
+ fprintf(stderr, "setting `gateway' requires also setting "
+ "`remote'\n");
fprintf(stderr, "Try `%s --help' for more information.\n",
argv[0]);
exit(EINVAL);
@@ -252,9 +524,9 @@ int main(int argc, char **argv)
free(str_pty);
free(path);

- init_term(STDIN_FILENO, &attr);
- console_loop(spty);
- restore_term(STDIN_FILENO, &attr);
-
- return 0;
- }
+ ret = main_loop(spty, &remote);
+
+ xs_daemon_close(xs);
+
+ return abs(ret);
+}




_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On 14/9/06 12:14, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:

> Changes since last version:
> - get rid of the 'queue of messages', instead each queueu is a ring
> buffer of characters that we read into and write from directly.

All our other ring buffers have free-running indexes (i.e., they do not wrap
to zero when they reach the end of the buffer). This makes it easier to
determine number of entries in use (it's 'producer minus consumer' -- that's
another thing, it's standard idiom for us to call the indexes producer (or
prod) and consumer (or cons)). We then mask the high-order bits when
actually indexing into the buffer.

-- Keir





_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On Thu, Sep 14, 2006 at 03:26:46PM +0100, Keir Fraser wrote:

> On 14/9/06 12:14, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:
>
> > Changes since last version:
> > - get rid of the 'queue of messages', instead each queueu is a ring
> > buffer of characters that we read into and write from directly.
>
> All our other ring buffers have free-running indexes (i.e., they do not wrap
> to zero when they reach the end of the buffer). This makes it easier to
> determine number of entries in use (it's 'producer minus consumer' -- that's
> another thing, it's standard idiom for us to call the indexes producer (or
> prod) and consumer (or cons)). We then mask the high-order bits when
> actually indexing into the buffer.

Ok, I'll make these changes. Appreciate the feedback!

Cheers,
Muli

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel
Re: [PATCH] allow connecting to xenconsole from remote hosts [ In reply to ]
On Thu, Sep 14, 2006 at 03:26:46PM +0100, Keir Fraser wrote:
>
>
>
> On 14/9/06 12:14, "Muli Ben-Yehuda" <muli@il.ibm.com> wrote:
>
> > Changes since last version:
> > - get rid of the 'queue of messages', instead each queueu is a ring
> > buffer of characters that we read into and write from directly.
>
> All our other ring buffers have free-running indexes (i.e., they do not wrap
> to zero when they reach the end of the buffer). This makes it easier to
> determine number of entries in use (it's 'producer minus consumer' -- that's
> another thing, it's standard idiom for us to call the indexes producer (or
> prod) and consumer (or cons)). We then mask the high-order bits when
> actually indexing into the buffer.

How about this? ring buffers are now "xen style". Verified to work
when the ring wraps around as well as when the ring indices
overflow. Optimizing ring_put() to not work character by character and
getting rid of the intermediate buffers is next on my list but I think
this can go in as is.

The attached patch makes xenconsole send and receive console messages
over a remote connection, instead of via stdin/stdout - if given the
--remote switch. It is useful for proxy'ing real console message or
other protocol messages (such as gdb) between dom0 and a remote
host. We're currently using it for proxying gdb between gdbstub in a
partition that talks gdb over the console page to a remote host
running gdb.

Signed-off-by: Muli Ben-Yehuda <muli@il.ibm.com>

--- vanilla/tools/console/client/main.c 2006-09-07 11:16:07.000000000 +0300
+++ gdbconsole.hg/tools/console/client/main.c 2006-09-20 13:54:56.000000000 +0300
@@ -1,8 +1,9 @@
/*\
- * Copyright (C) International Business Machines Corp., 2005
- * Author(s): Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) International Business Machines Corp., 2005, 2006
+ * Author: Anthony Liguori <aliguori@us.ibm.com>
+ * Author: Muli Ben-Yehuda <muli@il.ibm.com>
*
- * Xen Console Daemon
+ * Xen Console Client
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,32 +36,110 @@
#include <err.h>
#include <errno.h>
#include <pty.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdarg.h>

#include "xs.h"

+#define min(x,y) ({ \
+ const typeof(x) _x = (x); \
+ const typeof(y) _y = (y); \
+ (void) (&_x == &_y); \
+ _x < _y ? _x : _y; })
+
#define ESCAPE_CHARACTER 0x1d
+#define DEFAULT_LISTEN_PORT 7890

-static volatile sig_atomic_t received_signal = 0;
+struct remote {
+ long port;
+ int do_listen;
+ int gateway;
+ int server; /* server socket */
+};
+
+#define RING_SIZE (1 << 16)
+#define RING_IDX_MASK(i) ((i) & (RING_SIZE - 1))
+
+struct ring {
+ size_t prod;
+ size_t cons;
+ unsigned char ring[RING_SIZE];
+};

-static void sighandler(int signum)
+static void ring_put(struct ring* ring, char* buf, size_t len)
{
- received_signal = 1;
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ ring->ring[RING_IDX_MASK(ring->prod++)] = buf[i];
}

-static bool write_sync(int fd, const void *data, size_t size)
+static void ring_get(struct ring* ring, char* buf, size_t *pcount)
{
- size_t offset = 0;
- ssize_t len;
+ size_t idx, len, max, sofar, c;

- while (offset < size) {
- len = write(fd, data + offset, size - offset);
- if (len < 1) {
- return false;
- }
- offset += len;
+ max = *pcount;
+ sofar = 0;
+
+ c = ring->cons;
+ while ((c != ring->prod) && (sofar < max)) {
+ idx = RING_IDX_MASK(c);
+ len = ring->prod - c;
+ if ((idx + len) > RING_SIZE)
+ len = RING_SIZE - idx;
+ if ((sofar + len) > max)
+ len = max - sofar;
+ memcpy(buf, &ring->ring[idx], len);
+ buf += len;
+ sofar += len;
+ c += len;
}

- return true;
+ ring->cons = c;
+ *pcount = sofar;
+}
+
+static size_t ring_prod_available(struct ring *ring)
+{
+ size_t distance = ring->prod - ring->cons;
+
+ if (distance >= RING_SIZE)
+ return 0;
+
+ return (RING_SIZE - distance);
+}
+
+static size_t ring_cons_available(struct ring *ring)
+{
+ if (ring->cons == ring->prod)
+ return 0;
+
+ return ring->prod - ring->cons;
+}
+
+static volatile sig_atomic_t received_signal = 0;
+
+static int debug;
+
+#define dbg(fmt, args...) do { \
+ if (debug) \
+ _dbg("[%s] " fmt, __func__, ##args); \
+} while (0)
+
+static void _dbg(const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fflush(stderr);
+}
+
+static void sighandler(int signum)
+{
+ received_signal = 1;
}

static void usage(const char *program) {
@@ -68,9 +147,35 @@ static void usage(const char *program) {
"Attaches to a virtual domain console\n"
"\n"
" -h, --help display this help and exit\n"
+ " -r, --remote wait for connections from local clients\n"
+ " -g, --gateway allow connections from any host\n"
+ " -d, --debug enable debug output\n"
, program);
}

+ssize_t write_all(int fd, const void *buf, size_t count)
+{
+ const unsigned char* b = (const unsigned char*)buf;
+ ssize_t sum = 0;
+ ssize_t ret = 0;
+
+ while (count) {
+ ret = write(fd, b, count);
+ if (ret == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ ret = -errno;
+ break;
+ }
+ if (ret == 0)
+ break;
+ count -= ret;
+ b += ret;
+ sum += ret;
+ }
+ return (ret >= 0 ? sum : ret);
+}
+
/* don't worry too much if setting terminal attributes fail */
static void init_term(int fd, struct termios *old)
{
@@ -91,79 +196,256 @@ static void restore_term(int fd, struct
tcsetattr(fd, TCSAFLUSH, old);
}

-static int console_loop(int fd)
+static inline void dump_buf(char* prefix, int fd, const char *data, size_t len)
+{
+ dbg("%s: %u bytes on %d:\n", prefix, len, fd);
+ dbg("buf: `%s'\n", data);
+}
+
+static int handle_read_fd(int fd, struct ring* ring)
+{
+ int ret;
+ ssize_t len;
+ unsigned char* buf;
+ size_t avail;
+
+ avail = ring_prod_available(ring);
+ if (avail == 0)
+ return 0;
+
+ buf = malloc(avail);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ len = read(fd, buf, avail);
+ if (len == 1 && buf[0] == ESCAPE_CHARACTER) {
+ ret = ECONNRESET;
+ goto free;
+ }
+
+ if (len == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ ret = EINTR;
+ else
+ ret = -errno;
+ goto free;
+ }
+ if (len == 0) {
+ /* try to reconnect */
+ ret = EAGAIN;
+ goto free;
+ }
+
+ dump_buf("read", fd, buf, len);
+ ring_put(ring, buf, len);
+ ret = 0;
+free:
+ free(buf);
+done:
+ return ret;
+}
+
+static int handle_write_fd(int fd, struct ring* ring)
+{
+ int ret;
+ size_t len;
+ char buf[4096];
+
+ len = min(ring_cons_available(ring), sizeof(buf));
+
+ while (len) {
+ ring_get(ring, buf, &len);
+
+ dump_buf("write", fd, buf, len);
+
+ ret = write_all(fd, buf, len);
+ if (ret < 0)
+ return ret;
+
+ len = min(ring_cons_available(ring), sizeof(buf));
+ }
+ return 0;
+}
+
+static int console_loop(int conspty, int infd, int outfd)
{
int ret;
+ int max;
+
+ struct ring console = {
+ .prod = 0,
+ .cons = 0,
+ .ring = { '\0' }
+ };
+
+ struct ring out = {
+ .prod = 0,
+ .cons = 0,
+ .ring = { '\0' }
+ };

do {
- fd_set fds;
+ fd_set rfds, wfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(infd, &rfds);
+ FD_SET(conspty, &rfds);
+
+ FD_ZERO(&wfds);
+ FD_SET(conspty, &wfds);
+ FD_SET(outfd, &wfds);
+
+ max = (conspty | infd | outfd) + 1;

- FD_ZERO(&fds);
- FD_SET(STDIN_FILENO, &fds);
- FD_SET(fd, &fds);
+ ret = select(max, &rfds, &wfds, NULL, NULL);

- ret = select(fd + 1, &fds, NULL, NULL, NULL);
if (ret == -1) {
if (errno == EINTR || errno == EAGAIN) {
continue;
}
- return -1;
+ return -errno;
}

- if (FD_ISSET(STDIN_FILENO, &fds)) {
- ssize_t len;
- char msg[60];
-
- len = read(STDIN_FILENO, msg, sizeof(msg));
- if (len == 1 && msg[0] == ESCAPE_CHARACTER) {
+ if (FD_ISSET(infd, &rfds)) {
+ ret = handle_read_fd(infd, &console);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
return 0;
- }
-
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
+ }

- if (!write_sync(fd, msg, len)) {
- perror("write() failed");
- return -1;
- }
+ if (FD_ISSET(conspty, &rfds)) {
+ ret = handle_read_fd(conspty, &out);
+ if (ret < 0)
+ return ret;
+ if (ret == EINTR)
+ continue;
+ if (ret == EAGAIN)
+ return EAGAIN;
+ if (ret == ECONNRESET)
+ return 0;
}

- if (FD_ISSET(fd, &fds)) {
- ssize_t len;
- char msg[512];
-
- len = read(fd, msg, sizeof(msg));
- if (len == 0 || len == -1) {
- if (len == -1 &&
- (errno == EINTR || errno == EAGAIN)) {
- continue;
- }
- return -1;
- }
+ if (FD_ISSET(outfd, &wfds)) {
+ ret = handle_write_fd(outfd, &out);
+ if (ret < 0)
+ return ret;
+ }

- if (!write_sync(STDOUT_FILENO, msg, len)) {
- perror("write() failed");
- return -1;
- }
+ if (FD_ISSET(conspty, &wfds)) {
+ ret = handle_write_fd(conspty, &console);
+ if (ret < 0)
+ return ret;
}
} while (received_signal == 0);

return 0;
}

-int main(int argc, char **argv)
+static int start_server(struct remote *remote)
+{
+ struct sockaddr_in sa;
+ struct hostent *he = NULL;
+ int ret;
+ int on;
+
+ memset(&sa, 0, sizeof(sa));
+
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons(remote->port);
+
+ if (!remote->gateway) {
+ he = gethostbyname("localhost");
+ if (!he)
+ err(h_errno, "could not get localhost address\n");
+ memcpy(&sa.sin_addr, he->h_addr_list[0], he->h_length);
+ } else
+ sa.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ remote->server = socket(AF_INET, SOCK_STREAM, 0);
+ if (remote->server < 0)
+ err(errno, "socket failed\n");
+
+ on = 1;
+ ret = setsockopt(remote->server, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (ret)
+ err(errno, "setsockopt(SO_REUSEADDR) failed\n");
+
+ ret = bind(remote->server, (struct sockaddr *)&sa, sizeof(sa));
+ if (ret)
+ err(errno, "bind failed\n");
+
+ ret = listen(remote->server, 5);
+ if (ret)
+ err(errno, "listen failed\n");
+
+ return ret;
+}
+
+static int remote_loop(int conspty, struct remote *remote)
{
+ int in;
+ int out;
+ int socket;
+ int ret;
+
+ ret = start_server(remote);
+ if (ret)
+ return ret;
+
+ do {
+ socket = accept(remote->server, NULL, 0);
+ if (socket < 0)
+ err(errno, "accept failed\n");
+
+ in = socket;
+ out = socket;
+
+ ret = console_loop(conspty, in, out);
+ } while (ret == EAGAIN);
+
+ return ret;
+}
+
+static int main_loop(int conspty, struct remote *remote)
+{
+ int in;
+ int out;
+ int ret;
struct termios attr;
+
+ if (remote->do_listen)
+ return remote_loop(conspty, remote);
+
+ init_term(fileno(stdin), &attr);
+
+ in = fileno(stdin);
+ out = fileno(stdout);
+
+ ret = console_loop(conspty, in, out);
+
+ restore_term(in, &attr);
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
int domid;
- char *sopt = "h";
+ char *sopt = "rp:gdh";
int ch;
int opt_ind=0;
struct option lopt[] = {
+ { "remote", 0, NULL, 'r' },
+ { "port", 1, NULL, 'p' },
+ { "gateway", 0, NULL, 'g' },
+ { "debug", 0, NULL, 'd' },
{ "help", 0, 0, 'h' },
{ 0 },

@@ -174,6 +456,13 @@ int main(int argc, char **argv)
struct xs_handle *xs;
char *end;
time_t now;
+ struct remote remote = {
+ .port = DEFAULT_LISTEN_PORT,
+ .do_listen = 0,
+ .gateway = 0,
+ .server = -1,
+ };
+ int ret;

while((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
switch(ch) {
@@ -181,6 +470,25 @@ int main(int argc, char **argv)
usage(argv[0]);
exit(0);
break;
+ case 'r':
+ remote.do_listen = 1;
+ break;
+ case 'p':
+ remote.port = strtol(optarg, &end, 10);
+ if (end && *end) {
+ fprintf(stderr, "Invalid port `%s' specified\n",
+ optarg);
+ fprintf(stderr, "Try `%s --help' for more "
+ "information.\n", argv[0]);
+ exit(EINVAL);
+ }
+ break;
+ case 'g':
+ remote.gateway = 1;
+ break;
+ case 'd':
+ debug = 1;
+ break;
}
}

@@ -199,6 +507,14 @@ int main(int argc, char **argv)
exit(EINVAL);
}

+ if (remote.gateway && !remote.do_listen) {
+ fprintf(stderr, "setting `gateway' requires also setting "
+ "`remote'\n");
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ argv[0]);
+ exit(EINVAL);
+ }
+
xs = xs_daemon_open();
if (xs == NULL) {
err(errno, "Could not contact XenStore");
@@ -253,9 +569,9 @@ int main(int argc, char **argv)
free(str_pty);
free(path);

- init_term(STDIN_FILENO, &attr);
- console_loop(spty);
- restore_term(STDIN_FILENO, &attr);
+ ret = main_loop(spty, &remote);

- return 0;
- }
+ xs_daemon_close(xs);
+
+ return abs(ret);
+}

_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xensource.com
http://lists.xensource.com/xen-devel