aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel P. Berrange <berrange@redhat.com>2009-10-09 19:07:55 +0100
committerDaniel P. Berrange <berrange@redhat.com>2009-11-10 12:14:22 +0000
commitff261941430258922b77f8ddb41439317b8fcfdb (patch)
tree557cd6e4bc0a6f2a8f5f5313c28726c6e36c8043
parentAdd a new timed condition variable wait API (diff)
downloadlibvirt-ff261941430258922b77f8ddb41439317b8fcfdb.tar.gz
libvirt-ff261941430258922b77f8ddb41439317b8fcfdb.tar.bz2
libvirt-ff261941430258922b77f8ddb41439317b8fcfdb.zip
Move code for low level QEMU monitor interaction into separate file
The qemu_driver.c code should not contain any code that interacts with the QEMU monitor at a low level. A previous commit moved all the command invocations out. This change moves out the code which actually opens the monitor device. * src/qemu/qemu_driver.c: Remove qemudOpenMonitor & methods called from it. * src/Makefile.am: Add qemu_monitor.{c,h} * src/qemu/qemu_monitor.h: Add qemuMonitorOpen() * src/qemu/qemu_monitor.c: All code for opening the monitor
-rw-r--r--po/POTFILES.in1
-rw-r--r--src/Makefile.am1
-rw-r--r--src/qemu/qemu_driver.c306
-rw-r--r--src/qemu/qemu_monitor.c268
-rw-r--r--src/qemu/qemu_monitor.h37
5 files changed, 347 insertions, 266 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e090f5857..000be0921 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -27,6 +27,7 @@ src/phyp/phyp_driver.c
src/qemu/qemu_bridge_filter.c
src/qemu/qemu_conf.c
src/qemu/qemu_driver.c
+src/qemu/qemu_monitor.c
src/qemu/qemu_monitor_text.c
src/remote/remote_driver.c
src/secret/secret_driver.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 70def38bd..92dbae4c7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -183,6 +183,7 @@ VBOX_DRIVER_EXTRA_DIST = vbox/vbox_tmpl.c vbox/README
QEMU_DRIVER_SOURCES = \
qemu/qemu_conf.c qemu/qemu_conf.h \
+ qemu/qemu_monitor.c qemu/qemu_monitor.h \
qemu/qemu_monitor_text.c \
qemu/qemu_monitor_text.h \
qemu/qemu_driver.c qemu/qemu_driver.h \
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 2a0a46817..5be731b8b 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -55,6 +55,7 @@
#include "datatypes.h"
#include "qemu_driver.h"
#include "qemu_conf.h"
+#include "qemu_monitor.h"
#include "qemu_monitor_text.h"
#include "qemu_bridge_filter.h"
#include "c-ctype.h"
@@ -291,11 +292,24 @@ qemudRemoveDomainStatus(virConnectPtr conn,
return 0;
}
+static int
+qemuConnectMonitor(virDomainObjPtr vm, int reconnect)
+{
+ int rc;
+ if ((rc = qemuMonitorOpen(vm, reconnect)) != 0) {
+ VIR_ERROR(_("Failed to connect monitor for %s: %d\n"),
+ vm->def->name, rc);
+ return -1;
+ }
-static int qemudOpenMonitor(virConnectPtr conn,
- virDomainObjPtr vm,
- int reconnect);
+ if ((vm->monitorWatch = virEventAddHandle(vm->monitor,
+ VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR,
+ qemudDispatchVMEvent,
+ vm, NULL)) < 0)
+ return -1;
+ return 0;
+}
/*
* Open an existing VM's monitor, re-detect VCPU threads
@@ -304,17 +318,13 @@ static int qemudOpenMonitor(virConnectPtr conn,
static void
qemuReconnectDomain(void *payload, const char *name ATTRIBUTE_UNUSED, void *opaque)
{
- int rc;
virDomainObjPtr obj = payload;
struct qemud_driver *driver = opaque;
virDomainObjLock(obj);
- if ((rc = qemudOpenMonitor(NULL, obj, 1)) != 0) {
- VIR_ERROR(_("Failed to reconnect monitor for %s: %d\n"),
- obj->def->name, rc);
+ if (qemuConnectMonitor(obj, 1) < 0)
goto error;
- }
if (qemuUpdateActivePciHostdevs(driver, obj->def) < 0) {
goto error;
@@ -744,89 +754,10 @@ qemudShutdown(void) {
return 0;
}
-/* Return -1 for error, 1 to continue reading and 0 for success */
-typedef int qemudHandlerMonitorOutput(virConnectPtr conn,
- virDomainObjPtr vm,
- const char *output,
- int fd);
-
-/*
- * Returns -1 for error, 0 on end-of-file, 1 for success
- */
-static int
-qemudReadMonitorOutput(virConnectPtr conn,
- virDomainObjPtr vm,
- int fd,
- char *buf,
- size_t buflen,
- qemudHandlerMonitorOutput func,
- const char *what,
- int timeout)
-{
- size_t got = 0;
- buf[0] = '\0';
- timeout *= 1000; /* poll wants milli seconds */
-
- /* Consume & discard the initial greeting */
- while (got < (buflen-1)) {
- ssize_t ret;
-
- ret = read(fd, buf+got, buflen-got-1);
-
- if (ret < 0) {
- struct pollfd pfd = { .fd = fd, .events = POLLIN };
- if (errno == EINTR)
- continue;
-
- if (errno != EAGAIN) {
- virReportSystemError(conn, errno,
- _("Failure while reading %s startup output"),
- what);
- return -1;
- }
-
- ret = poll(&pfd, 1, timeout);
- if (ret == 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Timed out while reading %s startup output"), what);
- return -1;
- } else if (ret == -1) {
- if (errno != EINTR) {
- virReportSystemError(conn, errno,
- _("Failure while reading %s startup output"),
- what);
- return -1;
- }
- } else {
- /* Make sure we continue loop & read any further data
- available before dealing with EOF */
- if (pfd.revents & (POLLIN | POLLHUP))
- continue;
-
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Failure while reading %s startup output"), what);
- return -1;
- }
- } else if (ret == 0) {
- return 0;
- } else {
- got += ret;
- buf[got] = '\0';
- ret = func(conn, vm, buf, fd);
- if (ret == -1)
- return -1;
- if (ret == 1)
- continue;
- return 1;
- }
- }
-
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Out of space while reading %s startup output"), what);
- return -1;
-
-}
-
+typedef int qemuLogHandleOutput(virConnectPtr conn,
+ virDomainObjPtr vm,
+ const char *output,
+ int fd);
/*
* Returns -1 for error, 0 on success
@@ -837,7 +768,7 @@ qemudReadLogOutput(virConnectPtr conn,
int fd,
char *buf,
size_t buflen,
- qemudHandlerMonitorOutput func,
+ qemuLogHandleOutput func,
const char *what,
int timeout)
{
@@ -892,177 +823,20 @@ qemudReadLogOutput(virConnectPtr conn,
return -1;
}
-static int
-qemudCheckMonitorPrompt(virConnectPtr conn ATTRIBUTE_UNUSED,
- virDomainObjPtr vm,
- const char *output,
- int fd)
-{
- if (strstr(output, "(qemu) ") == NULL)
- return 1; /* keep reading */
-
- vm->monitor = fd;
-
- return 0;
-}
-
-static int
-qemudOpenMonitorCommon(virConnectPtr conn,
- virDomainObjPtr vm,
- int monfd,
- int reconnect)
-{
- char buf[1024];
- int ret;
-
- if (virSetCloseExec(monfd) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unable to set monitor close-on-exec flag"));
- return -1;
- }
- if (virSetNonBlock(monfd) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unable to put monitor into non-blocking mode"));
- return -1;
- }
-
- if (!reconnect) {
- if (qemudReadMonitorOutput(conn,
- vm, monfd,
- buf, sizeof(buf),
- qemudCheckMonitorPrompt,
- "monitor", 10) <= 0)
- ret = -1;
- else
- ret = 0;
- } else {
- vm->monitor = monfd;
- ret = 0;
- }
-
- if (ret != 0)
- return ret;
-
- if ((vm->monitorWatch = virEventAddHandle(vm->monitor,
- VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR,
- qemudDispatchVMEvent,
- vm, NULL)) < 0)
- return -1;
-
- return 0;
-}
-
-static int
-qemudOpenMonitorUnix(virConnectPtr conn,
- virDomainObjPtr vm,
- const char *monitor,
- int reconnect)
-{
- struct sockaddr_un addr;
- int monfd;
- int timeout = 3; /* In seconds */
- int ret, i = 0;
-
- if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
- virReportSystemError(conn, errno,
- "%s", _("failed to create socket"));
- return -1;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_UNIX;
- if (virStrcpyStatic(addr.sun_path, monitor) == NULL) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Monitor path %s too big for destination"), monitor);
- goto error;
- }
-
- do {
- ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr));
-
- if (ret == 0)
- break;
-
- if (errno == ENOENT || errno == ECONNREFUSED) {
- /* ENOENT : Socket may not have shown up yet
- * ECONNREFUSED : Leftover socket hasn't been removed yet */
- continue;
- }
-
- virReportSystemError(conn, errno, "%s",
- _("failed to connect to monitor socket"));
- goto error;
-
- } while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0));
-
- if (ret != 0) {
- virReportSystemError(conn, errno, "%s",
- _("monitor socket did not show up."));
- goto error;
- }
-
- if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0)
- goto error;
-
- return 0;
-
-error:
- close(monfd);
- return -1;
-}
-
-static int
-qemudOpenMonitorPty(virConnectPtr conn,
- virDomainObjPtr vm,
- const char *monitor,
- int reconnect)
-{
- int monfd;
-
- if ((monfd = open(monitor, O_RDWR)) < 0) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("Unable to open monitor path %s"), monitor);
- return -1;
- }
-
- if (qemudOpenMonitorCommon(conn, vm, monfd, reconnect) < 0)
- goto error;
-
- return 0;
-
-error:
- close(monfd);
- return -1;
-}
-
-static int
-qemudOpenMonitor(virConnectPtr conn,
- virDomainObjPtr vm,
- int reconnect)
-{
- switch (vm->monitor_chr->type) {
- case VIR_DOMAIN_CHR_TYPE_UNIX:
- return qemudOpenMonitorUnix(conn, vm,
- vm->monitor_chr->data.nix.path,
- reconnect);
- case VIR_DOMAIN_CHR_TYPE_PTY:
- return qemudOpenMonitorPty(conn, vm,
- vm->monitor_chr->data.file.path,
- reconnect);
- default:
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("unable to handle monitor type: %s"),
- virDomainChrTypeToString(vm->monitor_chr->type));
- return -1;
- }
-}
-/* Returns -1 for error, 0 success, 1 continue reading */
+/*
+ * Look at a chunk of data from the QEMU stdout logs and try to
+ * find a TTY device, as indicated by a line like
+ *
+ * char device redirected to /dev/pts/3
+ *
+ * Returns -1 for error, 0 success, 1 continue reading
+ */
static int
-qemudExtractMonitorPath(virConnectPtr conn,
- const char *haystack,
- size_t *offset,
- char **path)
+qemudExtractTTYPath(virConnectPtr conn,
+ const char *haystack,
+ size_t *offset,
+ char **path)
{
static const char needle[] = "char device redirected to";
char *tmp, *dev;
@@ -1120,8 +894,8 @@ qemudFindCharDevicePTYs(virConnectPtr conn,
for (i = 0 ; i < vm->def->nserials ; i++) {
virDomainChrDefPtr chr = vm->def->serials[i];
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
- if ((ret = qemudExtractMonitorPath(conn, output, &offset,
- &chr->data.file.path)) != 0)
+ if ((ret = qemudExtractTTYPath(conn, output, &offset,
+ &chr->data.file.path)) != 0)
return ret;
}
}
@@ -1130,8 +904,8 @@ qemudFindCharDevicePTYs(virConnectPtr conn,
for (i = 0 ; i < vm->def->nparallels ; i++) {
virDomainChrDefPtr chr = vm->def->parallels[i];
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
- if ((ret = qemudExtractMonitorPath(conn, output, &offset,
- &chr->data.file.path)) != 0)
+ if ((ret = qemudExtractTTYPath(conn, output, &offset,
+ &chr->data.file.path)) != 0)
return ret;
}
}
@@ -1168,7 +942,7 @@ qemudWaitForMonitor(virConnectPtr conn,
return -1;
}
- if (qemudOpenMonitor(conn, vm, 0) < 0)
+ if (qemuConnectMonitor(vm, 0) < 0)
return -1;
return 0;
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
new file mode 100644
index 000000000..ab1a98c28
--- /dev/null
+++ b/src/qemu/qemu_monitor.c
@@ -0,0 +1,268 @@
+/*
+ * qemu_monitor.c: interaction with QEMU monitor console
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * 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
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+#include <config.h>
+
+#include <poll.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "qemu_monitor.h"
+#include "qemu_conf.h"
+#include "event.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+/* Return -1 for error, 1 to continue reading and 0 for success */
+typedef int qemuMonitorHandleOutput(virDomainObjPtr vm,
+ const char *output,
+ int fd);
+
+/*
+ * Returns -1 for error, 0 on end-of-file, 1 for success
+ */
+static int
+qemuMonitorReadOutput(virDomainObjPtr vm,
+ int fd,
+ char *buf,
+ size_t buflen,
+ qemuMonitorHandleOutput func,
+ const char *what,
+ int timeout)
+{
+ size_t got = 0;
+ buf[0] = '\0';
+ timeout *= 1000; /* poll wants milli seconds */
+
+ /* Consume & discard the initial greeting */
+ while (got < (buflen-1)) {
+ ssize_t ret;
+
+ ret = read(fd, buf+got, buflen-got-1);
+
+ if (ret < 0) {
+ struct pollfd pfd = { .fd = fd, .events = POLLIN };
+ if (errno == EINTR)
+ continue;
+
+ if (errno != EAGAIN) {
+ virReportSystemError(NULL, errno,
+ _("Failure while reading %s startup output"),
+ what);
+ return -1;
+ }
+
+ ret = poll(&pfd, 1, timeout);
+ if (ret == 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Timed out while reading %s startup output"), what);
+ return -1;
+ } else if (ret == -1) {
+ if (errno != EINTR) {
+ virReportSystemError(NULL, errno,
+ _("Failure while reading %s startup output"),
+ what);
+ return -1;
+ }
+ } else {
+ /* Make sure we continue loop & read any further data
+ available before dealing with EOF */
+ if (pfd.revents & (POLLIN | POLLHUP))
+ continue;
+
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Failure while reading %s startup output"), what);
+ return -1;
+ }
+ } else if (ret == 0) {
+ return 0;
+ } else {
+ got += ret;
+ buf[got] = '\0';
+ ret = func(vm, buf, fd);
+ if (ret == -1)
+ return -1;
+ if (ret == 1)
+ continue;
+ return 1;
+ }
+ }
+
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Out of space while reading %s startup output"), what);
+ return -1;
+
+}
+
+static int
+qemuMonitorCheckPrompt(virDomainObjPtr vm,
+ const char *output,
+ int fd)
+{
+ if (strstr(output, "(qemu) ") == NULL)
+ return 1; /* keep reading */
+
+ vm->monitor = fd;
+
+ return 0;
+}
+
+static int
+qemuMonitorOpenCommon(virDomainObjPtr vm,
+ int monfd,
+ int reconnect)
+{
+ char buf[1024];
+ int ret;
+
+ if (virSetCloseExec(monfd) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to set monitor close-on-exec flag"));
+ return -1;
+ }
+ if (virSetNonBlock(monfd) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unable to put monitor into non-blocking mode"));
+ return -1;
+ }
+
+ if (!reconnect) {
+ if (qemuMonitorReadOutput(vm, monfd,
+ buf, sizeof(buf),
+ qemuMonitorCheckPrompt,
+ "monitor", 10) <= 0)
+ ret = -1;
+ else
+ ret = 0;
+ } else {
+ vm->monitor = monfd;
+ ret = 0;
+ }
+
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int
+qemuMonitorOpenUnix(virDomainObjPtr vm,
+ const char *monitor,
+ int reconnect)
+{
+ struct sockaddr_un addr;
+ int monfd;
+ int timeout = 3; /* In seconds */
+ int ret, i = 0;
+
+ if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ virReportSystemError(NULL, errno,
+ "%s", _("failed to create socket"));
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ if (virStrcpyStatic(addr.sun_path, monitor) == NULL) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Monitor path %s too big for destination"), monitor);
+ goto error;
+ }
+
+ do {
+ ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr));
+
+ if (ret == 0)
+ break;
+
+ if (errno == ENOENT || errno == ECONNREFUSED) {
+ /* ENOENT : Socket may not have shown up yet
+ * ECONNREFUSED : Leftover socket hasn't been removed yet */
+ continue;
+ }
+
+ virReportSystemError(NULL, errno, "%s",
+ _("failed to connect to monitor socket"));
+ goto error;
+
+ } while ((++i <= timeout*5) && (usleep(.2 * 1000000) <= 0));
+
+ if (ret != 0) {
+ virReportSystemError(NULL, errno, "%s",
+ _("monitor socket did not show up."));
+ goto error;
+ }
+
+ if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ close(monfd);
+ return -1;
+}
+
+static int
+qemuMonitorOpenPty(virDomainObjPtr vm,
+ const char *monitor,
+ int reconnect)
+{
+ int monfd;
+
+ if ((monfd = open(monitor, O_RDWR)) < 0) {
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("Unable to open monitor path %s"), monitor);
+ return -1;
+ }
+
+ if (qemuMonitorOpenCommon(vm, monfd, reconnect) < 0)
+ goto error;
+
+ return 0;
+
+error:
+ close(monfd);
+ return -1;
+}
+
+int
+qemuMonitorOpen(virDomainObjPtr vm,
+ int reconnect)
+{
+ switch (vm->monitor_chr->type) {
+ case VIR_DOMAIN_CHR_TYPE_UNIX:
+ return qemuMonitorOpenUnix(vm, vm->monitor_chr->data.nix.path,
+ reconnect);
+ case VIR_DOMAIN_CHR_TYPE_PTY:
+ return qemuMonitorOpenPty(vm, vm->monitor_chr->data.file.path,
+ reconnect);
+ default:
+ qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unable to handle monitor type: %s"),
+ virDomainChrTypeToString(vm->monitor_chr->type));
+ return -1;
+ }
+}
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
new file mode 100644
index 000000000..bdeafe072
--- /dev/null
+++ b/src/qemu/qemu_monitor.h
@@ -0,0 +1,37 @@
+/*
+ * qemu_monitor.h: interaction with QEMU monitor console
+ *
+ * Copyright (C) 2006-2009 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * 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
+ *
+ * Author: Daniel P. Berrange <berrange@redhat.com>
+ */
+
+
+#ifndef QEMU_MONITOR_H
+#define QEMU_MONITOR_H
+
+#include "internal.h"
+
+#include "domain_conf.h"
+
+int qemuMonitorOpen(virDomainObjPtr vm,
+ int reconnect);
+
+
+
+#endif /* QEMU_MONITOR_H */