[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-tools] [PATCH] xenrconsoled
We found it convenient to use the TCP console port that was available in Xen 2.0 on our platform. When it went away in 3.0 we created a xenrconsoled daemon that restored the functionality, its usage is as follows: usage: xenrconsoled: [OPTION] -h --help print this message -d --debug print debug messages -f --foreground run as a foreground process, i.e. not as a daemon -s --status report whether or not daemon is running -l --list list available domids / console ports -o --open <domid> open a socket for a domain's console -x --console-port <port #> use this port domain's for console socket -c --close <domid> close this domain's console socket -t --stop terminate daemon -i --ip-address <ip addr> only listen on this ip address -p --port <port #> listen on this port EXAMPLES: Start daemon on all interfaces, port (default 9600): # xenrconsoled Start daemon, on lo interface, port 9700: # xenrconsoled -i 127.0.0.1 -p 9700 Print status of daemon: # xenrconsoled --status Open a socket for domain 2's console on port 9602: # xenrconsoled -o 2 -x 9602 Close socket for domain 2: # xenrconsoled -c 2 List which domains / consoles are available: # xenrconsoled -l We attempted to make it fairly general, but it is geared towards our needs, but hopefully others will find it useful. Any comments / concerns / suggestions welcome, Pat -- Patrick O'Rourke porourke@xxxxxxxxxxx diff -u --new-file --recursive xen-unstable-clean/tools/console/Makefile xen-unstable-patched/tools/console/Makefile --- xen-unstable-clean/tools/console/Makefile 2005-08-25 23:57:08.000000000 -0400 +++ xen-unstable-patched/tools/console/Makefile 2005-08-26 10:33:36.000000000 -0400 @@ -16,13 +16,13 @@ CFLAGS += -I $(XEN_LIBXC) CFLAGS += -I $(XEN_XENSTORE) -BIN = xenconsoled xenconsole +BIN = xenconsoled xenconsole xenrconsoled all: $(BIN) clean: $(RM) *.a *.so *.o *.rpm $(BIN) - $(RM) client/*.o daemon/*.o + $(RM) client/*.o daemon/*.o remote/*.o xenconsoled: $(patsubst %.c,%.o,$(wildcard daemon/*.c)) $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_XENSTORE) \ @@ -32,8 +32,13 @@ $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_XENSTORE) \ -lxenctrl -lxenstore +xenrconsoled: $(patsubst %.c,%.o,$(wildcard remote/*.c)) + $(CC) $(CFLAGS) $^ -o $@ -L$(XEN_LIBXC) -L$(XEN_XENSTORE) \ + -lxenctrl -lxenstore + install: $(BIN) $(INSTALL_DIR) -p $(DESTDIR)/$(DAEMON_INSTALL_DIR) $(INSTALL_PROG) xenconsoled $(DESTDIR)/$(DAEMON_INSTALL_DIR) + $(INSTALL_PROG) xenrconsoled $(DESTDIR)/$(DAEMON_INSTALL_DIR) $(INSTALL_DIR) -p $(DESTDIR)/$(CLIENT_INSTALL_DIR) $(INSTALL_PROG) xenconsole $(DESTDIR)/$(CLIENT_INSTALL_DIR) diff -u --new-file --recursive xen-unstable-clean/tools/console/remote/network.c xen-unstable-patched/tools/console/remote/network.c --- xen-unstable-clean/tools/console/remote/network.c 1969-12-31 19:00:00.000000000 -0500 +++ xen-unstable-patched/tools/console/remote/network.c 2005-08-26 12:36:49.000000000 -0400 @@ -0,0 +1,259 @@ +/*\ + * Copyright (C) Egenera, Inc. 2005 + * Author(s): Patrick O'Rourke <porourke@xxxxxxxxxxx> + * + * Xen Remote Console Daemon + * + * 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 + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * network utility functions... + * +\*/ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <stdio.h> +#include <netinet/in.h> +#include <unistd.h> +#include <fcntl.h> +#include <netdb.h> +#include <errno.h> + +#include "utils.h" + +/* private functions...*/ +static int get_inet_ssocket(void); +static int get_host_ip(const char *, struct in_addr *); +static struct addrinfo *get_sockaddr(char *); + +/* + * Create an inet stream socket and bind it to specified port... + * returns new socket descriptor on success, -1 on error. + */ + int +inet_stream(const char *ipaddr, const short port) +{ + struct sockaddr_in saddr; + int sfd; + + if ((sfd = get_inet_ssocket()) < 0) { + return (sfd); + } + + if (ipaddr != NULL) { + /* fill in our IP address ... */ + if (get_host_ip(ipaddr, &saddr.sin_addr) != 0) { + printLog("inet_stream: unable to obtain our IP address"); + close(sfd); + sfd = -1; + } + } else { + saddr.sin_addr.s_addr = htonl(INADDR_ANY); + } + + if (sfd != -1) { + saddr.sin_family = AF_INET; + saddr.sin_port = htons(port); + + /* bind socket to port... */ + if (bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr))) { + printLog("inet_stream() - bind(2) failed: %s", + getErrnoMsg()); + close(sfd); + sfd = -1; + } else if (listen(sfd, 5)) { + printLog("inet_stream() - listend(2) failed: %s", + getErrnoMsg()); + close(sfd); + sfd = -1; + } + } + return (sfd); +} + + +/* + * Wait for a client to connect on socket 'sfd', put new socket + * from accept(2) into *nsfd. + * + * Returns 0 for success, 1 for interrupted, 2 for error + */ + int +inet_connection(int sfd, int *nsfd) +{ + struct sockaddr_in client; + socklen_t addrlen; + int fd; + int rc = 0; + + addrlen = sizeof(client); + fd = accept(sfd, (struct sockaddr *)&client, &addrlen); + if (fd < 0) { + rc = 1; + if (errno != EAGAIN && errno != EINTR) { + rc = 2; + printLog("inet_connection() - accept(2) failed: %s", + getErrnoMsg()); + } + } else { + *nsfd = fd; + } + return (rc); +} + +/* + * establish a connection to specified host on specified port. + * store newly allocated socket at *sk. + * + * return connected for success, -1 on error + */ + int +est_connection(char *server, const short server_port, const int options) +{ + struct addrinfo *ai; + struct sockaddr_in *saddr; + int flags; + int sock; + + sock = socket(PF_INET, SOCK_STREAM, 0); + if (sock == -1) { + printLog("est_connection(): socket failed: %s", + getErrnoMsg()); + return sock; + } + + ai = get_sockaddr(server); + if (ai == NULL) { + printLog("est_connection(): get_sockaddr failed: %s", + getErrnoMsg()); + close(sock); + return -1; + } + + saddr = (struct sockaddr_in *) ai->ai_addr; + saddr->sin_port = htons(server_port); + if (connect(sock, (struct sockaddr *)saddr, sizeof(*saddr))) { + printLog("est_connection(): connect error: %s", + getErrnoMsg()); + close(sock); + return -1; + } + + // connect worked, set options... + if (options != 0) { + int error = 1; // assume error + flags = fcntl(sock, F_GETFL, 0); + if (flags >= 0) { + flags |= options; + if (fcntl(sock, F_SETFL, flags) < 0) { + printLog("est_connection() fcntl: %s", + getErrnoMsg()); + } else { + error = 0; + } + } + if (error) { + printLog("est_connection: could set options: %s", + getErrnoMsg()); + close(sock); + return -1; + } + } + + return sock; +} + +/* + */ + int +send_data(int s, char *b, ssize_t size) +{ + int rc = 0; + if (write(s, b, size) != size) { + rc = errno; + printLog("send_data() of %d bytes failed on socket %d: %s", + size, s, getErrnoMsg()); + } + return rc; +} + + int +read_data(int s, char *b, int nbytes) +{ + int nleft; + int nread; + + nleft = nbytes; + while (nleft > 0) { + nread = read(s, b, nleft); + printDebug("read_data: read %d bytes from %d", nread, s); + if (nread < 0) { + printLog("read_data(): %s", getErrnoMsg()); + return (nread); + } else if (nread == 0) { + break; + } + nleft -= nread; + b += nread; + } + return (nbytes - nleft); +} + +/* + * get an inet stream socket + */ + static int +get_inet_ssocket(void) +{ + int sfd; + if ((sfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { + printLog("get_inet_ssocket(): %s", getErrnoMsg()); + } + return sfd; +} + +/* + * Get ip address of specified host... + * + * 0 = success + * 1 = failure + */ + static int +get_host_ip(const char *hostname, struct in_addr *in) +{ + struct hostent *host; + int rc = 0; + + host = gethostbyname(hostname); + if (host == NULL) { + printLog("gethostbyname (%d - %s)", + h_errno, hstrerror(h_errno)); + rc = h_errno; + } else { + *in = *((struct in_addr *)host->h_addr_list[0]); + } + return rc; +} + + static struct addrinfo * +get_sockaddr(char *h) +{ + struct addrinfo *res; + if (getaddrinfo(h, NULL, NULL, &res)) { + printLog("get_sockaddr() getaddrinfo failed: %s", + getErrnoMsg()); + res = NULL; + } + return res; +} diff -u --new-file --recursive xen-unstable-clean/tools/console/remote/network.h xen-unstable-patched/tools/console/remote/network.h --- xen-unstable-clean/tools/console/remote/network.h 1969-12-31 19:00:00.000000000 -0500 +++ xen-unstable-patched/tools/console/remote/network.h 2005-08-26 12:36:49.000000000 -0400 @@ -0,0 +1,30 @@ +/*\ + * Copyright (C) Egenera, Inc. 2005 + * Author(s): Patrick O'Rourke <porourke@xxxxxxxxxxx> + * + * Xen Remote Console Daemon + * + * 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 + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ +#ifndef _NETWORK_H_ +#define NETWORK 1 + +int inet_stream(const char *, const short); +int inet_connection(int, int *); +int est_connection(char *, const short, const int); +int send_data(int, char *, ssize_t); +int read_data(int , char *, int); + +#endif /* _NETWORK_H_ */ + diff -u --new-file --recursive xen-unstable-clean/tools/console/remote/rconsoled.c xen-unstable-patched/tools/console/remote/rconsoled.c --- xen-unstable-clean/tools/console/remote/rconsoled.c 1969-12-31 19:00:00.000000000 -0500 +++ xen-unstable-patched/tools/console/remote/rconsoled.c 2005-08-26 12:36:49.000000000 -0400 @@ -0,0 +1,1053 @@ +/*\ + * Copyright (C) Egenera, Inc. 2005 + * Author(s): Patrick O'Rourke <porourke@xxxxxxxxxxx> + * + * Xen Remote Console Daemon + * + * 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 + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <libgen.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <getopt.h> +#include <signal.h> + +// local headers... +#include "utils.h" +#include "network.h" +#include "xs.h" + +#define CONSOLE_PORT 9600 +#define CMD_SIZE 80 +#define OPTIONS "dhfstlc:i:p:o:x:" +#define PIDFILE "/var/run/xenrconsoled.pid" +#define ESCAPE_CHARACTER 0x1d + +// accepted command strings +#define SHUTDOWN_STR "terminate" +#define NEW_STR "new" +#define STOP_STR "stop" +#define LIST_STR "list" + +typedef struct server { + struct server *s_next; + pid_t s_pid; + int s_domid; + int s_port; + int s_terminating; +} server_t; + +typedef enum cmd { + CMD_UNKNOWN, /* catch all for bad input */ + CMD_NEW, /* make a new server */ + CMD_STOP, /* stop an existing one */ + CMD_SHUTDOWN, /* shutdown the daemon */ + CMD_LIST /* return a list of running domids, ports*/ +} cmd_t; + + +// global state +char *progname; +server_t *all_servers = NULL; +static volatile sig_atomic_t child_exited = 0; + +static struct option loptions[] = { + { "help", 0, NULL, 'h' }, + { "debug", 0, NULL, 'd' }, + { "foreground", 0, NULL, 'f' }, + { "status", 0, NULL, 's' }, + { "list", 0, NULL, 'l' }, + { "open", 1, NULL, 'o' }, + { "console-port", 1,NULL, 'x' }, + { "close", 1, NULL, 'c' }, + { "stop", 0, NULL, 't' }, + { "ip-address", 1, NULL, 'i' }, + { "port", 1, NULL, 'p' }, + { NULL, 0, NULL, 0} +}; + +// short descriptions for options, note we rely on the order of +// options being the same as the help... +static char *opt_help[] = { + "print this message", + "print debug messages", + "run as a foreground process, i.e. not as a daemon", + "report whether or not daemon is running", + "list available domids / console ports", + "<domid> open a socket for a domain's console", + "<port #> use this port domain's for console socket", + "<domid> close this domain's console socket", + "terminate daemon", + "<ip addr> only listen on this ip address", + "<port #> listen on this port", + NULL +}; + +// prototypes... +static void usage(int); +static int get_console_device(int); +static int get_status(void); +static cmd_t parse_command(int, int *, int *); +static void terminate(void); +static void start_server(char *, int, int); +static void stop_server(int); +static void list_servers(int); +static void console_loop(int, int); +static server_t *alloc_new_server(int, int); +static void console_loop(int, int); +static void free_server(int); +static void signal_init(void); +static void handle_sigchild(int); +static void reap_children(void); +static pid_t reap_child(pid_t); +static int print_servers(char *, int); +static int stop_daemon(void); +static int open_console(char *, int, int, int); +static int close_console(char *, int, int); +static server_t *lookup_server_by_pid(pid_t); +static server_t *lookup_server_by_domain(int); + + int +main(int argc, char **argv) +{ + int listenfd; + int dead = 0; + int errors = 0; + int is_debug = 0; + int is_status = 0; + int do_fork = 1; + int is_list = 0; + int is_stop = 0; + int is_open = 0; + int is_close = 0; + char *ipaddr = NULL; + int server_port = CONSOLE_PORT; + int console_port = -1; + int domain = -1; + pid_t pid; + + progname = basename(argv[0]); + + while (!errors) { + int c; + int optindex = 0; + + c = getopt_long(argc, argv, OPTIONS, loptions, &optindex); + if (c == -1) { + /* all done */ + break; + } + switch (c) { + case 'c': + is_close = 1; + domain = atoi(optarg); + if (domain <= 0) { + domain = -1; + } + break; + case 'd': + is_debug = 1; + break; + case 'i': + ipaddr = optarg; + break; + case 'f': + do_fork = 0; + break; + case 'h': + usage(1); + return 0; + /* NOTREACHED */ + case 'l': + is_list = 1; + break; + case 'o': + is_open = 1; + domain = atoi(optarg); + if (domain <= 0) { + errors++; + domain = -1; + } + break; + case 'p': + server_port = atoi(optarg); + if (server_port <= 0) { + server_port = -1; + } + break; + case 's': + is_status = 1; + break; + case 't': + is_stop = 1; + break; + case 'x': + console_port = atoi(optarg); + if (console_port <= 0) { + console_port = -1; + } + break; + case ':': + case '?': + errors++; + break; + default: + printLog("%s: getopt error", progname); + errors = 1; + break; + } /* switch */ + } /* !errors */ + + if (errors) { + usage(0); + return 1; + } + + if (is_debug) { + debug_enable(); + } + + // First handle any client requests... + if (is_status) { + return get_status(); + } + + if (is_list) { + return print_servers(ipaddr, server_port); + } + + if (is_stop) { + return stop_daemon(); + } + + if (is_open) { + char *msg = NULL; + if (domain == -1) { + msg = "no domain specified"; + } else if (console_port == -1) { + msg = "no port for domain's console"; + } else if (server_port == -1) { + msg = "invalid server port"; + } + if (msg != NULL) { + printLog("%s", msg); + usage(0); + return 1; + } + return open_console(ipaddr, server_port, domain, console_port); + } + + if (is_close) { + char *msg = NULL; + if (domain == -1) { + msg = "no domain specified"; + } else if (server_port == -1) { + msg = "invalid server port"; + } + if (msg != NULL) { + printLog("%s", msg); + usage(0); + return 1; + } + return close_console(ipaddr, server_port, domain); + } + + // okay, run as a daemon... + if ((pid = daemon_alive(PIDFILE)) > 0) { + printLog("found instance already running (pid %d)", pid); + return 1; + } + + setupUtils(progname, HANDLE_TERM_SIGNALS); + signal_init(); + + if (do_fork) { + daemonize(PIDFILE); + } else { + make_pidfile(PIDFILE); + } + + if (ipaddr == NULL) { + printLog("(pid %d) running on port %d...", getpid(), + server_port); + } else { + printLog("(pid %d) running on address: %s, port %d", + getpid(), ipaddr, server_port); + } + + if ((listenfd = inet_stream(ipaddr, server_port)) == -1) { + printLog("cannot create socket on port: %d", server_port); + return 2; + } + + // TODO - vet that we are only being connected from specific host? + // TODO - throttle? + while (!dead && !gotTermSignal()) { + int clientfd; + int rc; + int domain_port; + cmd_t command; + + // First deal with any children that may have exited... + if (child_exited) { + reap_children(); + } + + rc = inet_connection(listenfd, &clientfd); + if (rc == 2) { + printLog("connect attempt failed"); + continue; + } else if (rc == 1) { + // interrupted... + continue; + } + + command = parse_command(clientfd, &domain, &domain_port); + printDebug("got command: %d, domid == %d, port == %d", + command, domain, domain_port); + if (command == CMD_SHUTDOWN) { + dead = 1; + } else if (command == CMD_LIST) { + list_servers(clientfd); + } else if (command == CMD_STOP) { + stop_server(domain); + } else if (command == CMD_NEW) { + server_t *s = alloc_new_server(domain, domain_port); + if (s != NULL) { + pid_t p = fork(); + if (p == 0) { + // child... + close(listenfd); + close(clientfd); + start_server(ipaddr, + s->s_domid, s->s_port); + /* NOTREACHED */ + } else if (p < 0) { + printLog("cannot fork server: %s", + getErrnoMsg()); + free_server(domain); + } else { + printDebug("%d created child %d", + getpid(), p); + s->s_pid = p; + } + } else { + printLog("unable to handle new server"); + } + } else { + printDebug("ignoring bogus command"); + } + close(clientfd); + + } /* while */ + + terminate(); + close(listenfd); + printDebug("all done"); + tearDownUtils(); + return 0; +} + +/** + * Print usage info to stderr... + */ + static void +usage(int examples) +{ + int i; + + fprintf(stderr, "usage: %s: [OPTION]\n", progname); + for (i = 0; loptions[i].name != NULL; i++) { + fprintf(stderr, "\t-%c --%-15.15s %s\n", + loptions[i].val, loptions[i].name, opt_help[i]); + } + fflush(stderr); + + if (!examples) { + return; + } + + fprintf(stderr, "\nEXAMPLES:\n\n"); + fprintf(stderr, "\tStart daemon on all interfaces, port (default %d):\n", + CONSOLE_PORT); + fprintf(stderr, "\t# %s\n\n", progname); + fprintf(stderr, "\tStart daemon, on lo interface, port 9700:\n"); + fprintf(stderr, "\t# %s -i 127.0.0.1 -p 9700\n\n", progname); + fprintf(stderr, "\tPrint status of daemon:\n"); + fprintf(stderr, "\t# %s --status\n\n", progname); + fprintf(stderr, "\tOpen a socket for domain 2's console on port 9602:\n"); + fprintf(stderr, "\t# %s -o 2 -x 9602\n\n", progname); + fprintf(stderr, "\tClose socket for domain 2:\n"); + fprintf(stderr, "\t# %s -c 2\n\n", progname); + fprintf(stderr, "\tList which domains / consoles are available:\n"); + fprintf(stderr, "\t# %s -l\n\n", progname); + fflush(stderr); +} + + static void +signal_init() +{ + struct sigaction sa; + bzero(&sa, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = handle_sigchild; + if (sigaction(SIGCHLD, &sa, NULL) < 0) { + printLog("cannot establish SIGCHLD handler: %s", + getErrnoMsg()); + exit(1); + } + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, NULL) < 0) { + printLog("cannot ignore SIGPIPE: %s", getErrnoMsg()); + exit(1); + } +} + + static void +handle_sigchild(int sig) +{ + child_exited = 1; +} + + static void +reap_children() +{ + pid_t p; + int reaped = 0; + printDebug("reaping children..."); + do { + child_exited = 0; + p = reap_child(-1); + if (p > 0) { + server_t *s = lookup_server_by_pid(p); + if (s == NULL) { + printLog("no server for pid %d?", p); + continue; + } else { + free_server(s->s_domid); + } + reaped++; + } else { + // It is possible for the wait to have already + // occurred, if we were doing an explicit stop, + // that could have caught the wait for another + // process. + printDebug("no children to wait for"); + } + } while (child_exited || p > 0); + printDebug("reaped %d children", reaped); +} + +/** + * After a connection is accepted, read what the user wants us + * to do and return the appropriate command type. If the command + * has an arg, of which the only one specified is a port (to start + * listening on, or to stop listening on), pass that back in the + * 'port' parameter. + */ + static cmd_t +parse_command(int fd, int *domid, int *port) +{ + cmd_t command = CMD_UNKNOWN; + char buffer[CMD_SIZE + 1]; + int p = -1; + int d = -1; + + *domid = -1; + *port = -1; + + int bytes = read(fd, buffer, CMD_SIZE); + if (bytes <= 0) { + printDebug("parse_command: no bytes read"); + return command; + } + + buffer[bytes] = '\0'; + printDebug("parse_command - read <%s>", buffer); + + if (strstr(buffer, SHUTDOWN_STR) != NULL) { + command = CMD_SHUTDOWN; + return command; + } + + if (strstr(buffer, LIST_STR) != NULL) { + command = CMD_LIST; + return command; + } + + // must be new or stop + if ((strstr(buffer, NEW_STR) != NULL) && + (strlen(NEW_STR) < strlen(buffer)-1)) { + char *temp = buffer + strlen(NEW_STR); + int rc = sscanf(temp, "%d %d", &d, &p); + if (rc == 2) { + command = CMD_NEW; + *domid = d; + *port = p; + } + } else if ((strstr(buffer, STOP_STR) != NULL) && + (strlen(STOP_STR) < strlen(buffer)-1)) { + char *temp = buffer + strlen(STOP_STR); + int rc = sscanf(temp, "%d", &d); + if (rc == 1) { + command = CMD_STOP; + *domid = d; + } + } + + // all done + return command; +} + + static void +terminate() +{ + server_t *s; + printDebug("terminate: stopping all console servers"); + while ((s = all_servers) != NULL) { + // will remove server from list when done.... + stop_server(s->s_domid); + } +} + +/*** + */ + static void +list_servers(int fd) +{ + server_t *s = all_servers; + char buf[81]; + char *title_fmt = "%-8s %-8s %-8s\n"; + char *data_fmt = "%-8d %-8d %-8d\n"; + + printDebug("listing servers"); + + sprintf(buf, title_fmt, "domid", "port", "pid"); + send_data(fd, buf, strlen(buf)); + sprintf(buf, title_fmt, "-----", "----", "---"); + send_data(fd, buf, strlen(buf)); + + while (s != NULL) { + sprintf(buf, data_fmt, s->s_domid, s->s_port, s->s_pid); + send_data(fd, buf, strlen(buf)); + bzero(buf, sizeof(buf)); + s = s->s_next; + } +} + +/** + * Runs as a child process of the main daemon. We just listen on + * the port specified, awaiting a console connection request. We do + * not return from here. + * + * TODO - what happens if domain is shutdown / destroyed? + */ + static void +start_server(char *host, int domain, int port) +{ + int console_fd; + int listenfd; + int nconnections = 0; + + printLog("console server (%d) listening for domain %d on port %d...", + getpid(), domain, port); + + console_fd = get_console_device(domain); + // open tty device + if (console_fd == -1) { + printLog("cannot start console for domain %d on port %d", + domain, port); + exit(1); + } + // create a socket... + listenfd = inet_stream(host, port); + if (listenfd == -1) { + printLog("unable to create socket for domain %d, port %d", + domain, port); + exit(1); + } + while (!gotTermSignal()) { + int foo; + int rc = inet_connection(listenfd, &foo); + if (rc == 2) { + break; + } else if (rc == 1) { + continue; + } + printDebug("console server (%d) accepted connection %d", + getpid(), ++nconnections); + console_loop(console_fd, foo); + printDebug("console server (%d) closing connection", getpid()); + close(foo); + } + close(console_fd); + printLog("console server (%d) exiting: domain %d, port %d, conns %d", + getpid(), domain, port, nconnections); + exit(0); +} + + static void +stop_server(int domid) +{ + server_t *s; + pid_t waited_pid; + pid_t pid_to_kill; + int waits = 0; + + printLog("stop_server: domain %d...", domid); + + s = lookup_server_by_domain(domid); + if (s == NULL) { + printDebug("stop_server: domain %d not found", domid); + return; + } + s->s_terminating = 1; + pid_to_kill = s->s_pid; + + printDebug("stop_server: ending process: %d", pid_to_kill); + + if (kill(pid_to_kill, SIGTERM) < 0) { + printLog("stop_server: cannot terminate pid %d: %s", + pid_to_kill, getErrnoMsg()); + } + + // Give the child 5 seconds to exit... + do { + waits++; + waited_pid = reap_child(pid_to_kill); + if (waited_pid > 0) { + s = lookup_server_by_pid(waited_pid); + if (s == NULL) { + printLog("stop_server: orphaned pid %d?", + waited_pid); + } else { + free_server(s->s_domid); + } + } else { + sleep(1); + } + } while (waits < 5 && waited_pid != pid_to_kill); + + // did we get the pid we wanted? + if (waited_pid != pid_to_kill) { + printLog("stop_server: SIGKILL'ing pid %d for domain %d", + pid_to_kill, domid); + if (kill(pid_to_kill, SIGKILL) < 0) { + printLog("stop_server: cannot SIGKILL process %d", + pid_to_kill); + } + waits = 0; + do { + waits++; + waited_pid = reap_child(pid_to_kill); + if (waited_pid > 0) { + s = lookup_server_by_pid(waited_pid); + if (s == NULL) { + printLog("stop_server: orphan pid: %d", + waited_pid); + } else { + free_server(s->s_domid); + } + } + } while (waits < 10 && waited_pid != pid_to_kill); + + if (waited_pid != pid_to_kill) { + printLog("stop_server: forced stop of %d, domain %d", + pid_to_kill, domid); + } + } +} + +/** + * Find / open the tty device for the specified domain. Returns the + * file descriptor for the domain, -1 on error. + */ + static int +get_console_device(int domid) +{ + struct xs_handle *xs; + char tty_path[1024]; + char *tty_device; + unsigned int length; + int tty_fd = -1; + + printDebug("get_console_device: connecting to XenStore"); + if ((xs = xs_daemon_open()) == NULL) { + printLog("xs_daemon_open error: %s", getErrnoMsg()); + return tty_fd; + } + + printDebug("get_console_device: reading tty from store..."); + sprintf(tty_path, "/console/%d/tty", domid); + + tty_device = xs_read(xs, tty_path, &length); + if (tty_device == NULL) { + printLog("cannot get tty device: %s", getErrnoMsg()); + xs_daemon_close(xs); + return tty_fd; + } + + printDebug("get_console_device: dom %d's tty == %s", domid, tty_device); + + tty_fd = open(tty_device, O_RDWR | O_NOCTTY); + if (tty_fd == -1) { + printLog("unable to open tty %s for domain %d: %s", + tty_device, domid, getErrnoMsg()); + } + xs_daemon_close(xs); + return tty_fd; +} + + static void +console_loop(int console, int socket) +{ + int fds_open = 1; + do { + int ret; + fd_set fds; + int last_fd = (console > socket) ? console : socket; + + FD_ZERO(&fds); + FD_SET(console, &fds); + FD_SET(socket, &fds); + + ret = select(last_fd + 1, &fds, NULL, NULL, NULL); + if (ret == -1) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + printLog("console_loop: error %s", getErrnoMsg()); + } + + if (FD_ISSET(socket, &fds)) { + ssize_t len; + char msg[60]; + len = read(socket, msg, sizeof(msg)); + if (len == 0) { + fds_open = 0; + } else if (len > 0) { + if (len == 1 && msg[0] == ESCAPE_CHARACTER) { + return; + } + send_data(console, msg, len); + } else { + fds_open = 0; + printDebug("console_loop: socket read: %s", + getErrnoMsg()); + } + } + + if (FD_ISSET(console, &fds)) { + ssize_t len; + char msg[512]; + len = read(console, msg, sizeof(msg)); + if (len == 0) { + fds_open = 0; + } else if (len > 0) { + send_data(socket, msg, len); + } else { + fds_open = 0; + printDebug("console_loop: console read: %s", + getErrnoMsg()); + } + } + } while (fds_open && !gotTermSignal()); +} + +/** + * Called upon receipt of a SIGCHLD, do a wait to see which ones + * have exited and clean up their state. Returns pid of child that + * exited. + */ + static pid_t +reap_child(pid_t expected_pid) +{ + int status; + pid_t child = waitpid(expected_pid, &status, WNOHANG); + + if (child == 0) { + printDebug("reap_child: no child found to have exited?"); + return child; + } + + if (child < 0) { + printDebug("reap_child: wait error: %s", getErrnoMsg()); + return child; + } + + if (isDebug()) { + if (WIFEXITED(status)) { + int exit_code = WEXITSTATUS(status); + printDebug("reap_child: pid %d exited with %d", + child, exit_code); + } else if (WIFSIGNALED(status)) { + int exit_signal = WTERMSIG(status); + printDebug("reap_child: pid %d exited from signal %d", + child, exit_signal); + } else { + printDebug("reap_child: child %d bad exit status %d?", + child, status); + } + } + return child; +} + + static server_t * +alloc_new_server(int domain, int port) +{ + server_t *s = all_servers; + + // first verify that we do not have a server for this port/domain + while (s != NULL) { + if (s->s_domid == domain || s->s_port == port) { + break; + } + s = s->s_next; + } + + if (s == NULL) { + s = malloc(sizeof(*s)); + if (s != NULL) { + bzero(s, sizeof(s)); + s->s_domid = domain; + s->s_port = port; + s->s_next = all_servers; + all_servers = s; + printDebug("alloc_new_server: %lx: id: %d, port: %d", + s, s->s_domid, s->s_port); + } else { + printLog("alloc_new_server: malloc %s", + getErrnoMsg()); + } + } else { + // complain about why we failed... + char *func = "alloc_new_server"; + char *msg = "already exists for"; + if (s->s_domid == domain) { + printLog("%s: server (%d) %s domain %d", + func, s->s_pid, msg, domain); + } else { + printLog("%s: server (%d) %s port %d", + func, s->s_pid, msg, port); + } + s = NULL; + } + + return s; +} + + static void +free_server(int domid) +{ + int found = 0; + server_t *temp = all_servers; + server_t **prev = &all_servers; + + while (temp != NULL && !found) { + if (temp->s_domid == domid) { + found = 1; + *prev = temp->s_next; + printDebug("freeing server: domain %d, port %d", + temp->s_domid, temp->s_port); + free(temp); + continue; + } + prev = &temp->s_next; + temp = temp->s_next; + } +} + + static server_t * +lookup_server_by_pid(pid_t p) +{ + server_t *ans = NULL; + server_t *temp = all_servers; + while (temp != NULL) { + if (temp->s_pid == p) { + ans = temp; + break; + } + temp = temp->s_next; + } + return ans; +} + + static server_t * +lookup_server_by_domain(int domid) +{ + server_t *ans = NULL; + server_t *temp = all_servers; + while (temp != NULL) { + if (temp->s_domid == domid) { + ans = temp; + break; + } + temp = temp->s_next; + } + return ans; +} + + static int +get_status() +{ + pid_t pid = daemon_alive(PIDFILE); + int status; + + if (pid < 0) { + fprintf(stdout, "%s: unable to determine daemon status\n", + progname); + status = 1; + } else if (pid == 0) { + fprintf(stdout, "%s not running\n", progname); + status = 2; + } else { + fprintf(stdout, "%s, pid %d, is running\n", progname, pid); + status = 0; + } + + fflush(stdout); + return status; +} + + static int +print_servers(char *host, int port) +{ + int rc = 1; + int sock; + + printDebug("print servers"); + + if (host == NULL) { + host = "localhost"; + } + sock = est_connection(host, port, 0); + if (sock >= 0) { + char buf[81]; + int nread; + // tell daemon we want a list of active console ports + if (send_data(sock, LIST_STR, strlen(LIST_STR)) == 0) { + do { + if ((nread = read(sock, buf, 80)) > 0) { + buf[nread] = '\0'; + fprintf(stdout, "%s", buf); + } else if (nread < 0) { + printDebug(" read: %s", getErrnoMsg()); + } + } while (nread > 0); + rc = 0; + } else { + printLog("cannot talk to daemon"); + } + close(sock); + } else { + printLog("could not connect to %s, port %d", host, port); + } + return rc; +} + + static int +stop_daemon() +{ + int rc = 1; + pid_t pid; + + printDebug("stopping daemon..."); + pid = daemon_alive(PIDFILE); + if (pid > 0) { + printDebug("killing %d...", pid); + if (kill(pid, SIGTERM) < 0) { + fprintf(stderr, "cannot kill %d: %s\n", + pid, getErrnoMsg()); + } else { + sleep(1); + if (kill(pid, 0) == 0) { + fprintf(stderr, "could not kill %d\n", pid); + } else if (errno == ESRCH) { + fprintf(stdout, "stopped daemon process %d\n", + pid); + rc = 0; + } else { + fprintf(stderr, + "could not verify daemon stopped: %s", + getErrnoMsg()); + } + } + } else { + fprintf(stderr, "%s: daemon not running\n", progname); + } + + return rc; +} + + static int +open_console(char *ip, int sp, int domid, int cp) +{ + int rc = 1; + int sock; + + if (ip == NULL) { + ip = "localhost"; + } + + printDebug("open_console (%s.%d): domain = %d, port = %d", + ip, sp, domid, cp); + + sock = est_connection(ip, sp, 0); + if (sock >= 0) { + char buf[81]; + sprintf(buf,"%s %d %d", NEW_STR, domid, cp); + if (send_data(sock, buf, strlen(buf)) == 0) { + rc = 0; + } else { + printLog("cannot talk to daemon"); + } + } else { + printLog("could not connect to: %s, port %d", ip, sp); + } + return rc; +} + + static int +close_console(char *ip, int sp, int domid) +{ + int rc = 1; + int sock; + + if (ip == NULL) { + ip = "localhost"; + } + + printDebug("close_console (%s.%d): domain = %d", ip, sp, domid); + + sock = est_connection(ip, sp, 0); + if (sock >= 0) { + char buf[81]; + sprintf(buf,"%s %d", STOP_STR, domid); + if (send_data(sock, buf, strlen(buf)) == 0) { + rc = 0; + } else { + printLog("cannot talk to daemon"); + } + } else { + printLog("could not connect to: %s, port %d", ip, sp); + } + return rc; +} diff -u --new-file --recursive xen-unstable-clean/tools/console/remote/utils.c xen-unstable-patched/tools/console/remote/utils.c --- xen-unstable-clean/tools/console/remote/utils.c 1969-12-31 19:00:00.000000000 -0500 +++ xen-unstable-patched/tools/console/remote/utils.c 2005-08-26 12:36:49.000000000 -0400 @@ -0,0 +1,335 @@ +/*\ + * Copyright (C) Egenera, Inc. 2005 + * Author(s): Patrick O'Rourke <porourke@xxxxxxxxxxx> + * + * Xen Remote Console Daemon + * + * 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 + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ +#include <sys/types.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> + +#include "utils.h" + +static void printDate(void); +static void setupTermSignals(void); +static void handler(int); + +static char* service_name = NULL; +static int debug = 0; +static int logging_enabled = 0; +static volatile sig_atomic_t term_signal = 0; + +#define ROOT_DIR "/" + + void +setupUtils(char *svc, int do_signals) +{ + service_name = svc; + if (do_signals) { + setupTermSignals(); + } +} + + void +tearDownUtils() +{ + printLog("shutting down..."); + closelog(); +} + + int +gotTermSignal() +{ + return term_signal; +} + + void +setupTermSignals() +{ + struct sigaction sa; + bzero(&sa, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_handler = handler; + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGINT, &sa, NULL); +} + + void +handler(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + case SIGQUIT: + term_signal = 1; + break; + } +} + + void +debug_enable() +{ + debug = 1; +} + + void +debug_disable() +{ + debug = 0; +} + + int +isDebug() +{ + return debug; +} + +/** + * Make calling process a daemon process, assumes logging has + * already been configurd. Either succeeds or exits. + */ + void +daemonize(char *pidfile) +{ + int i; + int fd0, fd1, fd2; + struct rlimit r1; + pid_t pid; + struct sigaction sa; + + umask(0); + + if (getrlimit(RLIMIT_NOFILE, &r1) < 0) { + printLog("cannot get file limit: %s", getErrnoMsg()); + exit(1); + } + + pid = fork(); + if (pid < 0) { + printLog("fork failed: %s", getErrnoMsg()); + exit(1); + } else if (pid != 0) { + // have parent exit... + exit(0); + } + setsid(); + + bzero(&sa, sizeof(sa)); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + if (sigaction(SIGHUP, &sa, NULL) < 0) { + printLog("cannot ignore SIGHUP: %s", getErrnoMsg()); + exit(1); + } + pid = fork(); + if (pid < 0) { + printLog("second fork failed: %s", getErrnoMsg()); + exit(1); + } else if (pid != 0) { + exit(0); + } + + if (chdir(ROOT_DIR) < 0) { + printLog("cannot change to root '%s' - %s", + ROOT_DIR, getErrnoMsg()); + exit(1); + } + + if (r1.rlim_max == RLIM_INFINITY) { + r1.rlim_max = 1024; + } + for (i = 0; i < r1.rlim_max; i++) { + close(i); + } + + fd0 = open("/dev/null", O_RDWR); + fd1 = dup(0); + fd2 = dup(0); + + openlog(service_name, LOG_CONS|LOG_PID, LOG_DAEMON); + logging_enabled = 1; + if (fd0 != 0 || fd1 != 1 || fd2 != 2) { + printLog("unexpected file descriptors: %d %d %d", + fd0, fd1, fd2); + exit(1); + } + + make_pidfile(pidfile); +} + + void +make_pidfile(char *pidfile) +{ + int pidfd; + char buf[80]; + int bytes; + /* create pid file... */ + pidfd = open(pidfile, O_RDWR | O_CREAT); + if (pidfd == -1) { + printLog("cannot create pidfile %s: %s", + pidfile, getErrnoMsg()); + exit(1); + } + if (lockf(pidfd, F_TLOCK, 0) == -1) { + printLog("cannot lock pidfile %s: %s", + pidfile, getErrnoMsg()); + exit(1); + } + bytes = sprintf(buf, "%d", getpid()); + if (write(pidfd, buf, bytes) != bytes) { + printLog("cannot write to pidfile %s: %s", + pidfile, getErrnoMsg()); + exit(1); + } +} + +/** + * Helper function for printing log messages which will be prefixed + * with the date and our identity, if it is known + */ + void +printLog(char *format, ...) +{ + va_list ap; + va_start(ap, format); + if (logging_enabled) { + vsyslog(LOG_NOTICE, format, ap); + } else { + printDate(); + if (service_name != NULL) { + printf("%s: ", service_name); + } else { + printf(": "); + } + vprintf(format, ap); + printf("\n"); + fflush(NULL); + } + va_end(ap); +} + +/** + * Print specified msg, if debug is on + */ + void +printDebug(char *format, ...) +{ + if (debug) { + va_list ap; + va_start(ap, format); + if (logging_enabled) { + vsyslog(LOG_NOTICE, format, ap); + } else { + printDate(); + if (service_name != NULL) { + printf("%s DEBUG: ", service_name); + } else { + printf("DEBUG: "); + } + vprintf(format, ap); + printf("\n"); + fflush(NULL); + } + va_end(ap); + } +} + +/** + * Obtain / print date, used for logging. + */ + static void +printDate() +{ + struct tm *brokenTime; + time_t t; + char* fullDateString; + char* dateString; + size_t dateLen; + + t = time(NULL); + brokenTime = localtime(&t); + fullDateString = asctime(brokenTime); + dateLen = strlen(fullDateString) +1; + dateString = (char*) malloc(dateLen); + if (dateString != NULL) { + strncpy(dateString, fullDateString, dateLen-1); + dateString[dateLen-2] = '\0'; + printf("%s ", dateString); + fflush(NULL); + free(dateString); + } +} + +/** + * Obtain the corresponding errno message on error, only valid + * for last system call + */ + char * +getErrnoMsg() +{ + char *msg = strerror(errno); + if (msg == NULL) { + msg = "unknown error"; + } + return msg; +} + +/** + * Is there an instance of the specified daemon already running? + * + * returns: pid of a running daemon, 0 if not, -1 on error + */ + pid_t +daemon_alive(char *pidfile) +{ + pid_t pid = -1; + int fd; + + if ((fd = open(pidfile, O_RDONLY)) == -1) { + printDebug("pidfile %s open: %s", pidfile, getErrnoMsg()); + } else { + char buf[81]; + int bytes; + bytes = read(fd, buf, 80); + if (bytes > 0) { + buf[bytes] = '\0'; + sscanf(buf,"%d", &pid); + if ( (pid > 0) && (kill(pid, 0) == 0)) { + printDebug("pid %d, is running", pid); + } else { + if (errno != ESRCH) { + printDebug("kill(2) error: %s", + getErrnoMsg()); + } + pid = 0; + } + } + close(fd); + } + + return pid; +} diff -u --new-file --recursive xen-unstable-clean/tools/console/remote/utils.h xen-unstable-patched/tools/console/remote/utils.h --- xen-unstable-clean/tools/console/remote/utils.h 1969-12-31 19:00:00.000000000 -0500 +++ xen-unstable-patched/tools/console/remote/utils.h 2005-08-26 12:36:49.000000000 -0400 @@ -0,0 +1,39 @@ +/*\ + * Copyright (C) Egenera, Inc. 2005 + * Author(s): Patrick O'Rourke <porourke@xxxxxxxxxxx> + * + * Xen Remote Console Daemon + * + * 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 + * the Free Software Foundation; under version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ +#ifndef _UTILS_H_ +#define _UTILS_H_ 1 + +#define HANDLE_TERM_SIGNALS 1 +#define SKIP_TERM_SIGNALS 0 + +void debug_disable(void); +void debug_enable(void); +void daemonize(char *); +pid_t daemon_alive(char *); +char *getErrnoMsg(void); +int gotTermSignal(void); +int isDebug(void); +void printDebug(char *, ...); +void printLog(char *, ...); +void setupUtils(char *, int); +void tearDownUtils(void); +void make_pidfile(char *); + +#endif /* _UTILS_H */ _______________________________________________ Xen-tools mailing list Xen-tools@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-tools
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |