# HG changeset patch
# User John Levon <john.levon(a)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(a)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(a)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);