[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [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@xxxxxxxxxx>

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@xxxxxxxxxx>
+ *  Copyright (C) International Business Machines  Corp., 2005, 2006
+ *  Author: Anthony Liguori <aliguori@xxxxxxxxxx>
+ *  Author: Muli Ben-Yehuda <muli@xxxxxxxxxx>
  *
- *  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@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.