+Fri Jan 26 06:53:52 EST 2007 Daniel Berrange <berrange@redhat.com>
+
+ * src/virshc: Added 'console' and 'vncdisplay' commands to
+ the virsh tool
+ * src/console.c, src/console.h: Impl of a simple interactive
+ serial console
+
Fri Jan 26 12:48:13 CET 2007 Daniel Veillard <veillard@redhat.com>
* src/virsh.c: Richard W.M. Jones pointed out a missing option
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)
--- /dev/null
+/*
+ * 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@redhat.com>
+ */
+
+#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:
+ */
--- /dev/null
+/*
+ * 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@redhat.com>
+ */
+
+#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:
+ */
/*
- * 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.
*
#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;
return ctl->conn ? TRUE : FALSE;
}
+/*
+ * "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
*/
return TRUE;
}
+/*
+ * "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 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},
{"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}
};