The attached patch adds two new commands to virsh: - console - connects to the guest's serial console - vncdisplay - outputs the ip address & port number of a guest vnc display The former is another stage in eliminating the need to run 'xm' - replacing the Xen specific 'xm console' code, with the added advantage of working with any libvirt backend driver. The vncdisplay is intended to make it easier for people to launch a VNC viewer process. It prints out a IP address & port number in a format suitable for passing to vncviewer on the command line, eg vncviewer `virsh vncdisplay myguest` Regards, Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|
? src/vs.log ? src/xm.log Index: src/Makefile.am =================================================================== RCS file: /data/cvs/libvirt/src/Makefile.am,v retrieving revision 1.30 diff -u -p -r1.30 Makefile.am --- src/Makefile.am 16 Nov 2006 19:06:13 -0000 1.30 +++ src/Makefile.am 26 Jan 2007 02:53:36 -0000 @@ -32,7 +32,7 @@ libvirt_la_SOURCES = \ bin_PROGRAMS = virsh -virsh_SOURCES = virsh.c +virsh_SOURCES = virsh.c console.c console.h virsh_LDFLAGS = virsh_DEPENDENCIES = $(DEPS) virsh_LDADD = $(LDADDS) $(VIRSH_LIBS) Index: src/console.c =================================================================== RCS file: src/console.c diff -N src/console.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/console.c 26 Jan 2007 02:53:36 -0000 @@ -0,0 +1,188 @@ +/* + * console.c: A dumb serial console client + * + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Daniel Berrange <berrange@xxxxxxxxxx> + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <termios.h> +#include <poll.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <signal.h> + +#include "console.h" +#include "internal.h" + +/* ie Ctrl-] as per telnet */ +#define CTRL_CLOSE_BRACKET '\35' + +static int got_signal = 0; +static void do_signal(int sig ATTRIBUTE_UNUSED) { + got_signal = 1; +} + +int virRunConsole(const char *tty) { + int ttyfd, ret = -1; + struct termios ttyattr, rawattr; + void (*old_sigquit)(int); + void (*old_sigterm)(int); + void (*old_sigint)(int); + void (*old_sighup)(int); + void (*old_sigpipe)(int); + + + /* We do not want this to become the controlling TTY */ + if ((ttyfd = open(tty, O_NOCTTY | O_RDWR)) < 0) { + fprintf(stderr, _("unable to open tty %s: %s\n"), + tty, strerror(errno)); + return -1; + } + + /* Put STDIN into raw mode so that stuff typed + does not echo to the screen (the TTY reads will + result in it being echoed back already), and + also ensure Ctrl-C, etc is blocked, and misc + other bits */ + if (tcgetattr(STDIN_FILENO, &ttyattr) < 0) { + fprintf(stderr, _("unable to get tty attributes: %s\n"), + strerror(errno)); + goto closetty; + } + + rawattr = ttyattr; + cfmakeraw(&rawattr); + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) { + fprintf(stderr, _("unable to set tty attributes: %s\n"), + strerror(errno)); + goto closetty; + } + + + /* Trap all common signals so that we can safely restore + the original terminal settings on STDIN before the + process exits - people don't like being left with a + messed up terminal ! */ + old_sigquit = signal(SIGQUIT, do_signal); + old_sigterm = signal(SIGTERM, do_signal); + old_sigint = signal(SIGINT, do_signal); + old_sighup = signal(SIGHUP, do_signal); + old_sigpipe = signal(SIGPIPE, do_signal); + got_signal = 0; + + + /* Now lets process STDIN & tty forever.... */ + for (; !got_signal ;) { + unsigned int i; + struct pollfd fds[] = { + { STDIN_FILENO, POLLIN, 0 }, + { ttyfd, POLLIN, 0 }, + }; + + /* Wait for data to be available for reading on + STDIN or the tty */ + if (poll(fds, (sizeof(fds)/sizeof(struct pollfd)), -1) < 0) { + if (got_signal) + goto cleanup; + + if (errno == EINTR || errno == EAGAIN) + continue; + + fprintf(stderr, _("failure waiting for I/O: %s\n"), + strerror(errno)); + goto cleanup; + } + + for (i = 0 ; i < (sizeof(fds)/sizeof(struct pollfd)) ; i++) { + if (!fds[i].revents) + continue; + + /* Process incoming data available for read */ + if (fds[i].revents & POLLIN) { + char buf[4096]; + int got, sent = 0, destfd; + + if ((got = read(fds[i].fd, buf, sizeof(buf))) < 0) { + fprintf(stderr, _("failure reading input: %s\n"), + strerror(errno)); + goto cleanup; + } + + /* Quit if end of file, or we got the Ctrl-] key */ + if (!got || + (got == 1 && + buf[0] == CTRL_CLOSE_BRACKET)) + goto done; + + /* Data from stdin goes to the TTY, + data from the TTY goes to STDOUT */ + if (fds[i].fd == STDIN_FILENO) + destfd = ttyfd; + else + destfd = STDOUT_FILENO; + + while (sent < got) { + int done; + if ((done = write(destfd, buf + sent, got - sent)) <= 0) { + fprintf(stderr, _("failure writing output: %s\n"), + strerror(errno)); + goto cleanup; + } + sent += done; + } + } else { /* Any other flag from poll is an error condition */ + goto cleanup; + } + } + } + done: + ret = 0; + + cleanup: + + /* Restore original signal handlers */ + signal(SIGQUIT, old_sigpipe); + signal(SIGQUIT, old_sighup); + signal(SIGQUIT, old_sigint); + signal(SIGQUIT, old_sigterm); + signal(SIGQUIT, old_sigquit); + + /* Put STDIN back into the (sane?) state we found + it in before starting */ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &ttyattr); + + closetty: + close(ttyfd); + + return ret; +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: src/console.h =================================================================== RCS file: src/console.h diff -N src/console.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/console.h 26 Jan 2007 02:53:36 -0000 @@ -0,0 +1,45 @@ +/* + * console.c: A dumb serial console client + * + * Copyright (C) 2007 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Daniel Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_CONSOLE_H__ +#define __VIR_CONSOLE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + int virRunConsole(const char *tty); + +#ifdef __cplusplus +} +#endif + +#endif /* __VIR_CONSOLE_H__ */ + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ Index: src/virsh.c =================================================================== RCS file: /data/cvs/libvirt/src/virsh.c,v retrieving revision 1.44 diff -u -p -r1.44 virsh.c --- src/virsh.c 23 Jan 2007 14:39:45 -0000 1.44 +++ src/virsh.c 26 Jan 2007 02:53:38 -0000 @@ -1,5 +1,5 @@ /* - * virsh.c: a Xen shell used to exercise the libvir API + * virsh.c: a Xen shell used to exercise the libvirt API * * Copyright (C) 2005 Red Hat, Inc. * @@ -29,11 +29,16 @@ #include <fcntl.h> #include <locale.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + #include <readline/readline.h> #include <readline/history.h> #include "config.h" #include "internal.h" +#include "console.h" static char *progname; @@ -306,6 +311,71 @@ cmdConnect(vshControl * ctl, vshCmd * cm } /* + * "console" command + */ +static vshCmdInfo info_console[] = { + {"syntax", "console <domain>"}, + {"help", gettext_noop("connect to the guest console")}, + {"desc", + gettext_noop("Connect the virtual serial console for the guest")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_console[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdConsole(vshControl * ctl, vshCmd * cmd) +{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj = NULL; + xmlXPathContextPtr ctxt = NULL; + virDomainPtr dom; + int ret = FALSE; + char *doc; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) + return FALSE; + + doc = virDomainGetXMLDesc(dom, 0); + if (!doc) + goto cleanup; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + free(doc); + if (!xml) + goto cleanup; + ctxt = xmlXPathNewContext(xml); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/console/@tty)", ctxt); + if ((obj != NULL) && ((obj->type == XPATH_STRING) && + (obj->stringval != NULL) && (obj->stringval[0] != 0))) { + if (virRunConsole((const char *)obj->stringval) == 0) + ret = TRUE; + } else { + vshPrintExtra(ctl, _("No console available for domain\n")); + } + xmlXPathFreeObject(obj); + + cleanup: + if (ctxt) + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + virDomainFree(dom); + return ret; +} + +/* * "list" command */ static vshCmdInfo info_list[] = { @@ -1633,6 +1703,88 @@ cmdVersion(vshControl * ctl, vshCmd * cm } /* + * "dumpxml" command + */ +static vshCmdInfo info_vncdisplay[] = { + {"syntax", "vncdisplay <domain>"}, + {"help", gettext_noop("vnc display")}, + {"desc", gettext_noop("Ouput the IP address and port number for the VNC display.")}, + {NULL, NULL} +}; + +static vshCmdOptDef opts_vncdisplay[] = { + {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {NULL, 0, 0, NULL} +}; + +static int +cmdVNCDisplay(vshControl * ctl, vshCmd * cmd) +{ + xmlDocPtr xml = NULL; + xmlXPathObjectPtr obj = NULL; + xmlXPathContextPtr ctxt = NULL; + virDomainPtr dom; + int ret = FALSE; + int port = 0; + char *doc; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (!(dom = vshCommandOptDomain(ctl, cmd, "domain", NULL))) + return FALSE; + + doc = virDomainGetXMLDesc(dom, 0); + if (!doc) + goto cleanup; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + free(doc); + if (!xml) + goto cleanup; + ctxt = xmlXPathNewContext(xml); + if (!ctxt) + goto cleanup; + + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/graphics[@type='vnc']/@port)", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + goto cleanup; + } + port = strtol((const char *)obj->stringval, NULL, 10); + if (port == -1) { + goto cleanup; + } + xmlXPathFreeObject(obj); + + obj = xmlXPathEval(BAD_CAST "string(/domain/devices/graphics[@type='vnc']/@listen)", ctxt); + if ((obj == NULL) || (obj->type != XPATH_STRING) || + (obj->stringval == NULL) || (obj->stringval[0] == 0)) { + goto cleanup; + } + if (!strcmp((const char*)obj->stringval, "0.0.0.0")) { + vshPrint(ctl, ":%d\n", port-5900); + } else { + vshPrint(ctl, "%s:%d\n", (const char *)obj->stringval, port-5900); + } + xmlXPathFreeObject(obj); + obj = NULL; + + cleanup: + if (obj) + xmlXPathFreeObject(obj); + if (ctxt) + xmlXPathFreeContext(ctxt); + if (xml) + xmlFreeDoc(xml); + virDomainFree(dom); + return ret; +} + + +/* * "quit" command */ static vshCmdInfo info_quit[] = { @@ -1653,6 +1805,7 @@ cmdQuit(vshControl * ctl, vshCmd * cmd A */ static vshCmdDef commands[] = { {"connect", cmdConnect, opts_connect, info_connect}, + {"console", cmdConsole, opts_console, info_console}, {"create", cmdCreate, opts_create, info_create}, {"start", cmdStart, opts_start, info_start}, {"destroy", cmdDestroy, opts_destroy, info_destroy}, @@ -1681,6 +1834,7 @@ static vshCmdDef commands[] = { {"vcpuinfo", cmdVcpuinfo, opts_vcpuinfo, info_vcpuinfo}, {"vcpupin", cmdVcpupin, opts_vcpupin, info_vcpupin}, {"version", cmdVersion, NULL, info_version}, + {"vncdisplay", cmdVNCDisplay, opts_vncdisplay, info_vncdisplay}, {NULL, NULL, NULL, NULL} };