# HG changeset patch # User John Levon <john.levon@xxxxxxx> # Date 1231946128 28800 # Node ID dd17b3062611925baa2698ff5923579d0f2cd34e # Parent a0d98d39955f4f304d318c7c780742ab929eb351 Introduce virt-console Separate console handling out into a separate binary to allow management of privileges. Signed-off-by: John Levon <john.levon@xxxxxxx> diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -479,12 +479,16 @@ libvirt_test_la_LDFLAGS = $(test_LDFLAGS libvirt_test_la_LDFLAGS = $(test_LDFLAGS) libvirt_test_la_CFLAGS = $(COVERAGE_CFLAGS) +libexec_PROGRAMS = virt-console + +virt_console_SOURCES = console.c +virt_console_CFLAGS = $(COVERAGE_CFLAGS) +virt_console_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDFLAGS) +virt_console_LDADD = libvirt.la ../gnulib/lib/libgnu.la $(VIRSH_LIBS) + bin_PROGRAMS = virsh -virsh_SOURCES = \ - console.c console.h \ - virsh.c - +virsh_SOURCES = virsh.c virsh_LDFLAGS = $(WARN_CFLAGS) $(COVERAGE_LDFLAGS) virsh_LDADD = \ $(STATIC_BINARIES) \ @@ -554,8 +558,6 @@ virsh_win_icon.$(OBJEXT): virsh_win_icon --output-format coff --output $@ endif -libexec_PROGRAMS = - if WITH_STORAGE_DISK if WITH_LIBVIRTD libexec_PROGRAMS += libvirt_parthelper diff --git a/src/console.c b/src/console.c --- a/src/console.c +++ b/src/console.c @@ -1,7 +1,10 @@ /* - * console.c: A dumb serial console client + * virt-console: client for connecting to domain consoles. * * Copyright (C) 2007, 2008 Red Hat, Inc. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,34 +25,65 @@ #include <config.h> -#ifndef __MINGW32__ - #include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <termios.h> +#include <signal.h> #include <sys/types.h> -#include <sys/stat.h> +#include <unistd.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 <getopt.h> +#include <locale.h> + +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> + #include "internal.h" +#include "memory.h" #include "util.h" +#include "xml.h" /* ie Ctrl-] as per telnet */ #define CTRL_CLOSE_BRACKET '\35' -static int got_signal = 0; -static void do_signal(int sig ATTRIBUTE_UNUSED) { +static int got_signal; +static int verbose; +static const char *dom_name; +static const char *conn_name; + +#ifdef __sun +#include <stropts.h> +#include <priv.h> + +#define PU_RESETGROUPS 0x0001 /* Remove supplemental groups */ +#define PU_LIMITPRIVS 0x0002 /* L=P */ +#define PU_INHERITPRIVS 0x0004 /* I=P */ +#define PU_CLEARLIMITSET 0x0008 /* L=0 */ + +extern int __init_suid_priv(int, ...); +extern int __priv_bracket(priv_op_t); + +#ifndef PRIV_XVM_CONTROL +#define PRIV_XVM_CONTROL ((const char *)"xvm_control") +#endif +#ifndef PRIV_VIRT_MANAGE +#define PRIV_VIRT_MANAGE ((const char *)"virt_manage") +#endif + +#endif /* __sun */ + +static void +do_signal(int sig ATTRIBUTE_UNUSED) +{ got_signal = 1; } #ifndef HAVE_CFMAKERAW static void -cfmakeraw (struct termios *attr) +cfmakeraw(struct termios *attr) { attr->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); @@ -57,46 +91,432 @@ cfmakeraw (struct termios *attr) attr->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); attr->c_cflag &= ~(CSIZE | PARENB); attr->c_cflag |= CS8; + +#ifdef __sun + attr->c_cc[VMIN] = 0; + attr->c_cc[VTIME] = 0; +#endif } #endif /* !HAVE_CFMAKERAW */ -int vshRunConsole(const char *tty) { - int ttyfd, ret = -1; - struct termios ttyattr, rawattr; +static void make_tty_raw(int fd, struct termios *attr) +{ + struct termios ttyattr; + struct termios rawattr; + + if (attr == NULL) + attr = &ttyattr; + + if (tcgetattr(fd, attr) < 0) { + fprintf(stderr, _("Unable to get tty attributes: %s\n"), + strerror(errno)); + exit(EXIT_FAILURE); + } + + rawattr = *attr; + cfmakeraw(&rawattr); + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) { + fprintf(stderr, _("Unable to set tty attributes: %s\n"), + strerror(errno)); + exit(EXIT_FAILURE); + } +} + +#ifdef __sun + +/* + * Verify that the real user invoking this setuid-root executable has + * the needed privilege, then drop root and all the privileges we don't + * need before continuing. + */ +static void +setup_perms(void) +{ + if (seteuid(getuid()) == -1) { + perror("seteuid failed"); + exit(1); + } + + if (!priv_ineffect(PRIV_VIRT_MANAGE)) { + fprintf(stderr, "virt-console: permission denied\n"); + exit(1); + } + + if (seteuid(0) == -1) { + perror("seteuid failed"); + exit(1); + } + + /* + * We need to be able to talk to libvirt and open pty's. + */ + __init_suid_priv(PU_RESETGROUPS | PU_CLEARLIMITSET, + PRIV_VIRT_MANAGE, PRIV_FILE_DAC_READ, + PRIV_FILE_DAC_WRITE, NULL); +} + +#else +#define setup_perms() +#define __priv_bracket(a) +#endif + +static void +usage(int exitval) +{ + FILE *f = stderr; + + if (exitval == EXIT_SUCCESS) + f = stdout; + + fprintf(f, _("usage: virt-console [options] domain\n\n" + " options:\n" + " -c | --connect <uri> hypervisor connection URI\n" + " -h | --help this help\n" + " -v | --verbose be verbose\n\n")); + + exit(exitval); +} + +static void +parse_args(int argc, char *argv[]) +{ + int idx = 0; + int arg; + + struct option opt[] = { + { "connect", 1, 0, 'c' }, + { "help", 0, 0, 'h' }, + { "verbose", 0, 0, 'v' }, + { 0, 0, 0, 0 } + }; + + while ((arg = getopt_long(argc, argv, "c:hv", opt, &idx)) != -1) { + switch (arg) { + case 'c': + conn_name = optarg; + break; + case 'h': + usage(EXIT_SUCCESS); + break; + case 'v': + verbose = 1; + break; + default: + usage(EXIT_FAILURE); + break; + } + } + + if ((argc - optind) != 1) + usage(EXIT_FAILURE); + + dom_name = argv[optind]; + + if (conn_name == NULL) + conn_name = getenv("VIRSH_DEFAULT_CONNECT_URI"); +} + +static int +get_domain(virConnectPtr *conn, virDomainPtr *dom, + virDomainInfo *info, int lookup_by_id) +{ + int ret = 0; + int id; + + __priv_bracket(PRIV_ON); + + printf("1\n"); + *conn = virConnectOpenAuth(conn_name, virConnectAuthPtrDefault, + VIR_CONNECT_RO); + printf("2\n"); + if (*conn == NULL) { + fprintf(stderr, _("Failed to connect to the hypervisor")); + exit(EXIT_FAILURE); + } + + *dom = virDomainLookupByName(*conn, dom_name); + + if (*dom == NULL) + *dom = virDomainLookupByUUIDString(*conn, dom_name); + if (*dom == NULL && lookup_by_id && + virStrToLong_i(dom_name, NULL, 10, &id) == 0 && id >= 0) + *dom = virDomainLookupByID(*conn, id); + + if (*dom == NULL) + goto out; + + if (info != NULL) { + if (virDomainGetInfo(*dom, info) < 0) + goto out; + } + + ret = 1; + +out: + if (ret == 0) { + if (*dom != NULL) + virDomainFree(*dom); + virConnectClose(*conn); + } + __priv_bracket(PRIV_OFF); + return ret; +} + +static void +put_domain(virConnectPtr conn, virDomainPtr dom) +{ + __priv_bracket(PRIV_ON); + if (dom != NULL) + virDomainFree(dom); + virConnectClose(conn); + __priv_bracket(PRIV_OFF); +} + +static char * +get_domain_tty(void) +{ + xmlXPathContextPtr ctxt = NULL; + xmlDocPtr xml = NULL; + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + char *doc = NULL; + char *tty = NULL; + + if (!get_domain(&conn, &dom, NULL, 1)) { + fprintf(stderr, _("Couldn't find domain \"%s\".\n"), dom_name); + exit(EXIT_FAILURE); + } + + __priv_bracket(PRIV_ON); + doc = virDomainGetXMLDesc(dom, 0); + __priv_bracket(PRIV_OFF); + + put_domain(conn, dom); + conn = NULL; + dom = NULL; + + if (doc == NULL) + goto cleanup; + + xml = xmlReadDoc((const xmlChar *) doc, "domain.xml", NULL, + XML_PARSE_NOENT | XML_PARSE_NONET | + XML_PARSE_NOWARNING); + if (xml == NULL) + goto cleanup; + + ctxt = xmlXPathNewContext(xml); + if (ctxt == NULL) + goto cleanup; + + tty = virXPathString(conn, "string(/domain/devices/console/@tty)", ctxt); + +cleanup: + VIR_FREE(doc); + xmlXPathFreeContext(ctxt); + if (xml != NULL) + xmlFreeDoc(xml); + return tty; +} + +static int +domain_is_running(void) +{ + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + virDomainInfo info; + int ret = -1; + + if (!get_domain(&conn, &dom, &info, 1)) + return -1; + + switch (info.state) { + case VIR_DOMAIN_RUNNING: + case VIR_DOMAIN_BLOCKED: + case VIR_DOMAIN_PAUSED: + ret = 1; + break; + + case VIR_DOMAIN_NOSTATE: + case VIR_DOMAIN_CRASHED: + case VIR_DOMAIN_SHUTDOWN: + case VIR_DOMAIN_SHUTOFF: + ret = 0; + break; + + default: + break; + } + + put_domain(conn, dom); + return ret; +} + +static int +check_for_reboot(void) +{ + virConnectPtr conn = NULL; + virDomainPtr dom = NULL; + virDomainInfo info; + int tries = 0; + int ret = 0; + +retry: + if (dom != NULL) + put_domain(conn, dom); + + /* + * Domain ID will vary across reboot, so don't lookup by a given ID. + */ + if (!get_domain(&conn, &dom, &info, 0)) + return 0; + + switch (info.state) { + case VIR_DOMAIN_RUNNING: + case VIR_DOMAIN_BLOCKED: + case VIR_DOMAIN_PAUSED: + ret = 1; + goto out; + break; + + case VIR_DOMAIN_CRASHED: + if (verbose) + fprintf(stderr, _("Domain \"%s\" has crashed."), dom_name); + goto out; + break; + + case VIR_DOMAIN_NOSTATE: + default: + break; + + case VIR_DOMAIN_SHUTDOWN: + if (verbose) + fprintf(stderr, _("Domain \"%s\" is shutting down.\n"), dom_name); + tries = 0; + break; + + case VIR_DOMAIN_SHUTOFF: + if (verbose) + fprintf(stderr, _("Domain \"%s\" has shut down."), dom_name); + goto out; + break; + } + + tries++; + if (tries == 1) { + goto retry; + } else if (tries < 6) { + sleep(1); + goto retry; + } + +out: + put_domain(conn, dom); + return ret; +} + +static int +open_tty(void) +{ + struct termios ttyattr; + char *tty; + int ttyfd; + + if ((tty = get_domain_tty()) == NULL) { + if (domain_is_running() != 1) { + fprintf(stderr, _("Domain \"%s\" is not running.\n"), dom_name); + exit(EXIT_FAILURE); + } + + fprintf(stderr, + _("Couldn't get console for domain \"%s\"\n"), dom_name); + exit(EXIT_FAILURE); + } + + __priv_bracket(PRIV_ON); + if ((ttyfd = open(tty, O_NOCTTY | O_RDWR)) < 0) { + fprintf(stderr, _("Unable to open tty %s: %s\n"), + tty, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (lockf(ttyfd, F_TLOCK, 0) == -1) { + if (errno == EACCES || errno == EAGAIN) { + fprintf(stderr, + _("Console for domain \"%s\" is in use.\n"), dom_name); + } else { + fprintf(stderr, _("Unable to lock tty %s: %s\n"), + tty, strerror(errno)); + } + exit(EXIT_FAILURE); + } + __priv_bracket(PRIV_OFF); + + VIR_FREE(tty); + +#ifdef __sun + /* + * The pty may come from either xend (with pygrub) or xenconsoled. + * It may have tty semantics set up, or not. While it isn't + * strictly necessary to have those semantics here, it is good to + * have a consistent state that is the same as under Linux. + * + * If tcgetattr fails, they have not been set up, so go ahead and + * set them up now, by pushing the ptem and ldterm streams modules. + */ + if (tcgetattr(ttyfd, &ttyattr) < 0) { + ioctl(ttyfd, I_PUSH, "ptem"); + ioctl(ttyfd, I_PUSH, "ldterm"); + tcgetattr(ttyfd, &ttyattr); + } + + cfmakeraw(&ttyattr); + tcsetattr(ttyfd, TCSANOW, &ttyattr); +#endif + + return ttyfd; +} + +static void +close_tty(int ttyfd) +{ + while (lockf(ttyfd, F_ULOCK, 0) && errno == EINTR) + ; + close(ttyfd); +} + +int +main(int argc, char *argv[]) +{ + struct termios ttyattr; + struct termios 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)); + int ret = EXIT_FAILURE; + int retrying = 0; + int ttyfd; + + setup_perms(); + + if (!setlocale(LC_ALL, "")) { + perror("setlocale"); 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; - } - + if (!bindtextdomain(GETTEXT_PACKAGE, LOCALEBASEDIR)) { + perror("bindtextdomain"); + return -1; + } + + if (!textdomain(GETTEXT_PACKAGE)) { + perror("textdomain"); + return -1; + } + + parse_args(argc, argv); /* Trap all common signals so that we can safely restore the original terminal settings on STDIN before the @@ -107,8 +527,36 @@ int vshRunConsole(const char *tty) { old_sigint = signal(SIGINT, do_signal); old_sighup = signal(SIGHUP, do_signal); old_sigpipe = signal(SIGPIPE, do_signal); - got_signal = 0; - + +retry: + + ttyfd = open_tty(); + + if (!retrying && verbose) { + printf("Connected to domain %s\n", dom_name); + printf("Escape character is '^]'\n"); + } + + /* + * 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)); + exit(EXIT_FAILURE); + } + + rawattr = ttyattr; + cfmakeraw(&rawattr); + + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) { + fprintf(stderr, _("unable to set tty attributes: %s\n"), + strerror(errno)); + exit(EXIT_FAILURE); + } /* Now lets process STDIN & tty forever.... */ for (; !got_signal ;) { @@ -127,7 +575,7 @@ int vshRunConsole(const char *tty) { if (errno == EINTR || errno == EAGAIN) continue; - fprintf(stderr, _("failure waiting for I/O: %s\n"), + fprintf(stderr, _("Failure waiting for I/O: %s\n"), strerror(errno)); goto cleanup; } @@ -142,15 +590,21 @@ int vshRunConsole(const char *tty) { int got, sent = 0, destfd; if ((got = read(fds[i].fd, buf, sizeof(buf))) < 0) { - fprintf(stderr, _("failure reading input: %s\n"), + 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)) + if (!got) { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &ttyattr); + if (!check_for_reboot()) + goto done; + close_tty(ttyfd); + retrying = 1; + goto retry; + } + + if (got == 1 && buf[0] == CTRL_CLOSE_BRACKET) goto done; /* Data from stdin goes to the TTY, @@ -162,23 +616,31 @@ int vshRunConsole(const char *tty) { while (sent < got) { int done; - if ((done = safewrite(destfd, buf + sent, got - sent)) - <= 0) { - fprintf(stderr, _("failure writing output: %s\n"), + if ((done = safewrite(destfd, buf + sent, got - sent)) <= 0) { + fprintf(stderr, _("Failure writing output: %s\n"), strerror(errno)); goto cleanup; } sent += done; } + } else if (fds[i].revents & POLLHUP) { + tcsetattr(STDIN_FILENO, TCSAFLUSH, &ttyattr); + if (!check_for_reboot()) + goto done; + close_tty(ttyfd); + retrying = 1; + goto retry; } else { /* Any other flag from poll is an error condition */ goto cleanup; } } } - done: - ret = 0; - - cleanup: + +done: + ret = EXIT_SUCCESS; +cleanup: + tcsetattr(STDIN_FILENO, TCSANOW, &ttyattr); + close_tty(ttyfd); /* Restore original signal handlers */ signal(SIGQUIT, old_sigpipe); @@ -187,14 +649,18 @@ int vshRunConsole(const char *tty) { 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; -} - -#endif /* !__MINGW32__ */ + if (verbose) + printf("\nConnection to domain %s closed.", dom_name); + printf("\n"); + + exit(ret); +} + +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff --git a/src/console.h b/src/console.h deleted file mode 100644 --- a/src/console.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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__ - -#ifndef __MINGW32__ - -int vshRunConsole(const char *tty); - -#endif /* !__MINGW32__ */ - -#endif /* __VIR_CONSOLE_H__ */ diff --git a/src/util.c b/src/util.c --- a/src/util.c +++ b/src/util.c @@ -240,7 +240,7 @@ __virExec(virConnectPtr conn, childout = *outfd; } #ifndef ENABLE_DEBUG - } else { + } else if (!(flags & VIR_EXEC_NO_PIPE)) { childout = null; #endif } @@ -271,7 +271,7 @@ __virExec(virConnectPtr conn, childerr = *errfd; } #ifndef ENABLE_DEBUG - } else { + } else if (!(flags & VIR_EXEC_NO_PIPE)) { childerr = null; #endif } @@ -652,6 +652,15 @@ virExec(virConnectPtr conn, return -1; } +int +virExecNoPipe(virConnectPtr conn, + char **argv ATTRIBUTE_UNUSED, + pid_t *retpid ATTRIBUTE_UNUSED) +{ + ReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR, __FUNCTION__); + return -1; +} + #endif /* __MINGW32__ */ /* Like gnulib's fread_file, but read no more than the specified maximum diff --git a/src/util.h b/src/util.h --- a/src/util.h +++ b/src/util.h @@ -36,6 +36,7 @@ enum { VIR_EXEC_NONE = 0, VIR_EXEC_NONBLOCK = (1 << 0), VIR_EXEC_DAEMON = (1 << 1), + VIR_EXEC_NO_PIPE = (1 << 2) }; int virExec(virConnectPtr conn, diff --git a/src/virsh.c b/src/virsh.c --- a/src/virsh.c +++ b/src/virsh.c @@ -29,6 +29,7 @@ #include <assert.h> #include <errno.h> #include <sys/stat.h> +#include <sys/wait.h> #include <inttypes.h> #include <libxml/parser.h> @@ -42,7 +43,6 @@ #include "internal.h" #include "buf.h" -#include "console.h" #include "util.h" static char *progname; @@ -469,6 +469,7 @@ static const vshCmdInfo info_console[] = static const vshCmdOptDef opts_console[] = { {"domain", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("domain name, id or uuid")}, + {"verbose", VSH_OT_BOOL, 0, gettext_noop("verbose console")}, {NULL, 0, 0, NULL} }; @@ -477,49 +478,39 @@ static int static int cmdConsole(vshControl *ctl, const vshCmd *cmd) { - xmlDocPtr xml = NULL; - xmlXPathObjectPtr obj = NULL; - xmlXPathContextPtr ctxt = NULL; - virDomainPtr dom; + const char *argv[] = { BINDIR "/virt-console", NULL, NULL, NULL, NULL }; + int verbose = vshCommandOptBool(cmd, "verbose"); int ret = FALSE; - char *doc; - - if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) - return FALSE; - - if (!(dom = vshCommandOptDomain(ctl, cmd, 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 (vshRunConsole((const char *)obj->stringval) == 0) - ret = TRUE; - } else { - vshPrintExtra(ctl, "%s", _("No console available for domain\n")); - } - xmlXPathFreeObject(obj); - - cleanup: - xmlXPathFreeContext(ctxt); - if (xml) - xmlFreeDoc(xml); - virDomainFree(dom); - return ret; + pid_t pid; + int argpos = 1; + + if (!vshConnectionUsability(ctl, ctl->conn, TRUE)) + return FALSE; + + if (verbose) + argv[argpos++] = "--verbose"; + + if (ctl->name != NULL) { + argv[argpos++] = "--connect"; + argv[argpos++] = ctl->name; + } + + if (!(argv[argpos] = vshCommandOptString(cmd, "domain", NULL))) { + vshError(ctl, FALSE, _("Undefined domain name or id.")); + return FALSE; + } + + ret = virExec(ctl->conn, argv, NULL, 0, &pid, STDIN_FILENO, NULL, + NULL, VIR_EXEC_NO_PIPE); + + if (ret == -1) { + vshError(ctl, FALSE, _("Couldn't execute virt-console.")); + return FALSE; + } + + waitpid(pid, NULL, 0); + + return TRUE; } #else /* __MINGW32__ */ @@ -4583,6 +4574,7 @@ cmdTTYConsole(vshControl *ctl, const vsh goto cleanup; } vshPrint(ctl, "%s\n", (const char *)obj->stringval); + ret = TRUE; cleanup: xmlXPathFreeObject(obj); -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list