[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |