[libvirt] [PATCH] Introduce virt-console

john.levon at sun.com john.levon at sun.com
Wed Jan 14 15:21:10 UTC 2009


# HG changeset patch
# User John Levon <john.levon at sun.com>
# 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 at sun.com>

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 at redhat.com>
- */
-
-#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);




More information about the libvir-list mailing list