Pull out all the QEMU monitor interaction code to a separate
file. This will make life easier when we need to drop in a
new implementation for the forthcoming QMP machine friendly
monitor support.
Next step is to add formal APIs for each monitor command,
and remove direct commands for sending/receiving generic
data.
* src/Makefile.am: Add qemu_monitor.c to build
* src/qemu/qemu_driver.c: Remove code for monitor interaction
* src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: New
file for monitor interaction
---
src/Makefile.am | 1 +
src/qemu/qemu_driver.c | 426 +----------------------------------------
src/qemu/qemu_monitor_text.c | 437 ++++++++++++++++++++++++++++++++++++++++++
src/qemu/qemu_monitor_text.h | 72 +++++++
4 files changed, 511 insertions(+), 425 deletions(-)
create mode 100644 src/qemu/qemu_monitor_text.c
create mode 100644 src/qemu/qemu_monitor_text.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 9cbec47..7520e96 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -169,6 +169,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_text.c qemu/qemu_monitortext.h\
qemu/qemu_driver.c qemu/qemu_driver.h
UML_DRIVER_SOURCES = \
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 25d983e..9f17aae 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_text.h"
#include "c-ctype.h"
#include "event.h"
#include "buf.h"
@@ -74,9 +75,6 @@
#define VIR_FROM_THIS VIR_FROM_QEMU
-#define QEMU_CMD_PROMPT "\n(qemu) "
-#define QEMU_PASSWD_PROMPT "Password: "
-
static int qemudShutdown(void);
static void qemuDriverLock(struct qemud_driver *driver)
@@ -88,12 +86,6 @@ static void qemuDriverUnlock(struct qemud_driver *driver)
virMutexUnlock(&driver->lock);
}
-/* Return -1 for error, 0 for success */
-typedef int qemudMonitorExtraPromptHandler(const virDomainObjPtr vm,
- const char *buf,
- const char *prompt,
- void *data);
-
static void qemuDomainEventFlush(int timer, void *opaque);
static void qemuDomainEventQueue(struct qemud_driver *driver,
virDomainEventPtr event);
@@ -115,28 +107,6 @@ static void qemudShutdownVMDaemon(virConnectPtr conn,
static int qemudDomainGetMaxVcpus(virDomainPtr dom);
-static int qemudMonitorCommand(const virDomainObjPtr vm,
- const char *cmd,
- char **reply);
-static int qemudMonitorCommandWithFd(const virDomainObjPtr vm,
- const char *cmd,
- int scm_fd,
- char **reply);
-static int qemudMonitorCommandWithHandler(const virDomainObjPtr vm,
- const char *cmd,
- const char *extraPrompt,
- qemudMonitorExtraPromptHandler extraHandler,
- void *handlerData,
- int scm_fd,
- char **reply);
-static int qemudMonitorCommandExtra(const virDomainObjPtr vm,
- const char *cmd,
- const char *extra,
- const char *extraPrompt,
- int scm_fd,
- char **reply);
-static int qemudMonitorSendCont(virConnectPtr conn,
- const virDomainObjPtr vm);
static int qemudDomainSetMemoryBalloon(virConnectPtr conn,
virDomainObjPtr vm,
unsigned long newmem);
@@ -2411,400 +2381,6 @@ cleanup:
}
-/* Throw away any data available on the monitor
- * This is done before executing a command, in order
- * to allow re-synchronization if something went badly
- * wrong in the past. it also deals with problem of
- * QEMU *sometimes* re-printing its initial greeting
- * when we reconnect to the monitor after restarts.
- */
-static void
-qemuMonitorDiscardPendingData(virDomainObjPtr vm) {
- char buf[1024];
- int ret = 0;
-
- /* Monitor is non-blocking, so just loop till we
- * get -1 or 0. Don't bother with detecting
- * errors, since we'll deal with that better later */
- do {
- ret = read(vm->monitor, buf, sizeof (buf)-1);
- } while (ret > 0);
-}
-
-static int
-qemudMonitorSendUnix(const virDomainObjPtr vm,
- const char *cmd,
- size_t cmdlen,
- int scm_fd)
-{
- struct msghdr msg;
- struct iovec iov[1];
- ssize_t ret;
-
- memset(&msg, 0, sizeof(msg));
-
- iov[0].iov_base = (void *)cmd;
- iov[0].iov_len = cmdlen;
-
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
-
- if (scm_fd != -1) {
- char control[CMSG_SPACE(sizeof(int))];
- struct cmsghdr *cmsg;
-
- msg.msg_control = control;
- msg.msg_controllen = sizeof(control);
-
- cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- memcpy(CMSG_DATA(cmsg), &scm_fd, sizeof(int));
- }
-
- do {
- ret = sendmsg(vm->monitor, &msg, 0);
- } while (ret < 0 && errno == EINTR);
-
- return ret == cmdlen ? 0 : -1;
-}
-
-static int
-qemudMonitorSend(const virDomainObjPtr vm,
- const char *cmd,
- int scm_fd)
-{
- char *full;
- size_t len;
- int ret = -1;
-
- if (virAsprintf(&full, "%s\r", cmd) < 0)
- return -1;
-
- len = strlen(full);
-
- switch (vm->monitor_chr->type) {
- case VIR_DOMAIN_CHR_TYPE_UNIX:
- if (qemudMonitorSendUnix(vm, full, len, scm_fd) < 0)
- goto out;
- break;
- default:
- case VIR_DOMAIN_CHR_TYPE_PTY:
- if (safewrite(vm->monitor, full, len) != len)
- goto out;
- break;
- }
-
- ret = 0;
-out:
- VIR_FREE(full);
- return ret;
-}
-
-static int
-qemudMonitorCommandWithHandler(const virDomainObjPtr vm,
- const char *cmd,
- const char *extraPrompt,
- qemudMonitorExtraPromptHandler extraHandler,
- void *handlerData,
- int scm_fd,
- char **reply) {
- int size = 0;
- char *buf = NULL;
-
- /* Should never happen, but just in case, protect
- * against null monitor (ocurrs when VM is inactive) */
- if (!vm->monitor_chr)
- return -1;
-
- qemuMonitorDiscardPendingData(vm);
-
- VIR_DEBUG("Send '%s'", cmd);
- if (qemudMonitorSend(vm, cmd, scm_fd) < 0)
- return -1;
-
- *reply = NULL;
-
- for (;;) {
- struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 };
- char *tmp;
-
- /* Read all the data QEMU has sent thus far */
- for (;;) {
- char data[1024];
- int got = read(vm->monitor, data, sizeof(data));
-
- if (got == 0)
- goto error;
- if (got < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EAGAIN)
- break;
- goto error;
- }
- if (VIR_REALLOC_N(buf, size+got+1) < 0)
- goto error;
-
- memmove(buf+size, data, got);
- buf[size+got] = '\0';
- size += got;
- }
-
- /* Look for QEMU prompt to indicate completion */
- if (buf) {
- char *foundPrompt;
-
- if (extraPrompt &&
- (foundPrompt = strstr(buf, extraPrompt)) != NULL) {
- char *promptEnd;
-
- if (extraHandler(vm, buf, foundPrompt, handlerData) < 0)
- return -1;
- /* Discard output so far, necessary to detect whether
- extraPrompt appears again. We don't need the output between
- original command and this prompt anyway. */
- promptEnd = foundPrompt + strlen(extraPrompt);
- memmove(buf, promptEnd, strlen(promptEnd)+1);
- size -= promptEnd - buf;
- } else if ((tmp = strstr(buf, QEMU_CMD_PROMPT)) != NULL) {
- char *commptr = NULL, *nlptr = NULL;
- /* Preserve the newline */
- tmp[1] = '\0';
-
- /* The monitor doesn't dump clean output after we have written to
- * it. Every character we write dumps a bunch of useless stuff,
- * so the result looks like
"cXcoXcomXcommXcommaXcommanXcommand"
- * Try to throw away everything before the first full command
- * occurence, and inbetween the command and the newline starting
- * the response
- */
- if ((commptr = strstr(buf, cmd))) {
- memmove(buf, commptr, strlen(commptr)+1);
- if ((nlptr = strchr(buf, '\n')))
- memmove(buf+strlen(cmd), nlptr, strlen(nlptr)+1);
- }
-
- break;
- }
- }
- pollagain:
- /* Need to wait for more data */
- if (poll(&fd, 1, -1) < 0) {
- if (errno == EINTR)
- goto pollagain;
- goto error;
- }
- }
- *reply = buf;
- return 0;
-
- error:
- VIR_FREE(buf);
- return -1;
-}
-
-struct extraHandlerData
-{
- const char *reply;
- bool first;
-};
-
-static int
-qemudMonitorCommandSimpleExtraHandler(const virDomainObjPtr vm,
- const char *buf ATTRIBUTE_UNUSED,
- const char *prompt ATTRIBUTE_UNUSED,
- void *data_)
-{
- struct extraHandlerData *data = data_;
-
- if (!data->first)
- return 0;
- if (qemudMonitorSend(vm, data->reply, -1) < 0)
- return -1;
- data->first = false;
- return 0;
-}
-
-static int
-qemudMonitorCommandExtra(const virDomainObjPtr vm,
- const char *cmd,
- const char *extra,
- const char *extraPrompt,
- int scm_fd,
- char **reply) {
- struct extraHandlerData data;
-
- data.reply = extra;
- data.first = true;
- return qemudMonitorCommandWithHandler(vm, cmd, extraPrompt,
- qemudMonitorCommandSimpleExtraHandler,
- &data, scm_fd, reply);
-}
-
-static int
-qemudMonitorCommandWithFd(const virDomainObjPtr vm,
- const char *cmd,
- int scm_fd,
- char **reply) {
- return qemudMonitorCommandExtra(vm, cmd, NULL, NULL, scm_fd, reply);
-}
-
-static int
-qemudMonitorCommand(const virDomainObjPtr vm,
- const char *cmd,
- char **reply) {
- return qemudMonitorCommandWithFd(vm, cmd, -1, reply);
-}
-
-static virStorageEncryptionPtr
-findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm,
- const char *path)
-{
- bool seen_volume;
- int i;
-
- seen_volume = false;
- for (i = 0; i < vm->def->ndisks; i++) {
- virDomainDiskDefPtr disk;
-
- disk = vm->def->disks[i];
- if (disk->src != NULL && STREQ(disk->src, path)) {
- seen_volume = true;
- if (disk->encryption != NULL)
- return disk->encryption;
- }
- }
- if (seen_volume)
- qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
- _("missing <encryption> for volume %s"), path);
- else
- qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
- _("unexpected passphrase request for volume %s"),
- path);
- return NULL;
-}
-
-static char *
-findVolumeQcowPassphrase(virConnectPtr conn, virDomainObjPtr vm,
- const char *path, size_t *passphrase_len)
-{
- virStorageEncryptionPtr enc;
- virSecretPtr secret;
- char *passphrase;
- unsigned char *data;
- size_t size;
-
- if (conn->secretDriver == NULL ||
- conn->secretDriver->lookupByUUID == NULL ||
- conn->secretDriver->getValue == NULL) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s",
- _("secret storage not supported"));
- return NULL;
- }
-
- enc = findDomainDiskEncryption(conn, vm, path);
- if (enc == NULL)
- return NULL;
-
- if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
- enc->nsecrets != 1 ||
- enc->secrets[0]->type !=
- VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
- qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
- _("invalid <encryption> for volume %s"), path);
- return NULL;
- }
-
- secret = conn->secretDriver->lookupByUUID(conn,
- enc->secrets[0]->uuid);
- if (secret == NULL)
- return NULL;
- data = conn->secretDriver->getValue(secret, &size,
- VIR_SECRET_GET_VALUE_INTERNAL_CALL);
- virUnrefSecret(secret);
- if (data == NULL)
- return NULL;
-
- if (memchr(data, '\0', size) != NULL) {
- memset(data, 0, size);
- VIR_FREE(data);
- qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET,
- _("format='qcow' passphrase for %s must not contain
a "
- "'\\0'"), path);
- return NULL;
- }
-
- if (VIR_ALLOC_N(passphrase, size + 1) < 0) {
- memset(data, 0, size);
- VIR_FREE(data);
- virReportOOMError(conn);
- return NULL;
- }
- memcpy(passphrase, data, size);
- passphrase[size] = '\0';
-
- memset(data, 0, size);
- VIR_FREE(data);
-
- *passphrase_len = size;
- return passphrase;
-}
-
-static int
-qemudMonitorSendVolumePassphrase(const virDomainObjPtr vm,
- const char *buf,
- const char *prompt,
- void *data)
-{
- virConnectPtr conn = data;
- char *passphrase, *path;
- const char *prompt_path;
- size_t path_len, passphrase_len = 0;
- int res;
-
- /* The complete prompt looks like this:
- ide0-hd0 (/path/to/volume) is encrypted.
- Password:
- "prompt" starts with ") is encrypted". Extract
/path/to/volume. */
- for (prompt_path = prompt; prompt_path > buf && prompt_path[-1] !=
'(';
- prompt_path--)
- ;
- if (prompt_path == buf)
- return -1;
- path_len = prompt - prompt_path;
- if (VIR_ALLOC_N(path, path_len + 1) < 0)
- return -1;
- memcpy(path, prompt_path, path_len);
- path[path_len] = '\0';
-
- passphrase = findVolumeQcowPassphrase(conn, vm, path, &passphrase_len);
- VIR_FREE(path);
- if (passphrase == NULL)
- return -1;
-
- res = qemudMonitorSend(vm, passphrase, -1);
-
- memset(passphrase, 0, passphrase_len);
- VIR_FREE(passphrase);
-
- return res;
-}
-
-static int
-qemudMonitorSendCont(virConnectPtr conn,
- const virDomainObjPtr vm) {
- char *reply;
-
- if (qemudMonitorCommandWithHandler(vm, "cont", ") is
encrypted.",
- qemudMonitorSendVolumePassphrase, conn,
- -1, &reply) < 0)
- return -1;
- qemudDebug ("%s: cont reply: %s", vm->def->name, info);
- VIR_FREE(reply);
- return 0;
-}
static virDrvOpenStatus qemudOpen(virConnectPtr conn,
virConnectAuthPtr auth ATTRIBUTE_UNUSED,
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
new file mode 100644
index 0000000..76842a5
--- /dev/null
+++ b/src/qemu/qemu_monitor_text.c
@@ -0,0 +1,437 @@
+/*
+ * 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(a)redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "qemu_monitor_text.h"
+#include "qemu_conf.h"
+#include "memory.h"
+#include "logging.h"
+#include "driver.h"
+#include "datatypes.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+/* Throw away any data available on the monitor
+ * This is done before executing a command, in order
+ * to allow re-synchronization if something went badly
+ * wrong in the past. it also deals with problem of
+ * QEMU *sometimes* re-printing its initial greeting
+ * when we reconnect to the monitor after restarts.
+ */
+static void
+qemuMonitorDiscardPendingData(virDomainObjPtr vm) {
+ char buf[1024];
+ int ret = 0;
+
+ /* Monitor is non-blocking, so just loop till we
+ * get -1 or 0. Don't bother with detecting
+ * errors, since we'll deal with that better later */
+ do {
+ ret = read(vm->monitor, buf, sizeof (buf)-1);
+ } while (ret > 0);
+}
+
+static int
+qemudMonitorSendUnix(const virDomainObjPtr vm,
+ const char *cmd,
+ size_t cmdlen,
+ int scm_fd)
+{
+ struct msghdr msg;
+ struct iovec iov[1];
+ ssize_t ret;
+
+ memset(&msg, 0, sizeof(msg));
+
+ iov[0].iov_base = (void *)cmd;
+ iov[0].iov_len = cmdlen;
+
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+
+ if (scm_fd != -1) {
+ char control[CMSG_SPACE(sizeof(int))];
+ struct cmsghdr *cmsg;
+
+ msg.msg_control = control;
+ msg.msg_controllen = sizeof(control);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ memcpy(CMSG_DATA(cmsg), &scm_fd, sizeof(int));
+ }
+
+ do {
+ ret = sendmsg(vm->monitor, &msg, 0);
+ } while (ret < 0 && errno == EINTR);
+
+ return ret == cmdlen ? 0 : -1;
+}
+
+static int
+qemudMonitorSend(const virDomainObjPtr vm,
+ const char *cmd,
+ int scm_fd)
+{
+ char *full;
+ size_t len;
+ int ret = -1;
+
+ if (virAsprintf(&full, "%s\r", cmd) < 0)
+ return -1;
+
+ len = strlen(full);
+
+ switch (vm->monitor_chr->type) {
+ case VIR_DOMAIN_CHR_TYPE_UNIX:
+ if (qemudMonitorSendUnix(vm, full, len, scm_fd) < 0)
+ goto out;
+ break;
+ default:
+ case VIR_DOMAIN_CHR_TYPE_PTY:
+ if (safewrite(vm->monitor, full, len) != len)
+ goto out;
+ break;
+ }
+
+ ret = 0;
+out:
+ VIR_FREE(full);
+ return ret;
+}
+
+int
+qemudMonitorCommandWithHandler(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extraPrompt,
+ qemudMonitorExtraPromptHandler extraHandler,
+ void *handlerData,
+ int scm_fd,
+ char **reply) {
+ int size = 0;
+ char *buf = NULL;
+
+ /* Should never happen, but just in case, protect
+ * against null monitor (ocurrs when VM is inactive) */
+ if (!vm->monitor_chr)
+ return -1;
+
+ qemuMonitorDiscardPendingData(vm);
+
+ VIR_DEBUG("Send '%s'", cmd);
+ if (qemudMonitorSend(vm, cmd, scm_fd) < 0)
+ return -1;
+
+ *reply = NULL;
+
+ for (;;) {
+ struct pollfd fd = { vm->monitor, POLLIN | POLLERR | POLLHUP, 0 };
+ char *tmp;
+
+ /* Read all the data QEMU has sent thus far */
+ for (;;) {
+ char data[1024];
+ int got = read(vm->monitor, data, sizeof(data));
+
+ if (got == 0)
+ goto error;
+ if (got < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ goto error;
+ }
+ if (VIR_REALLOC_N(buf, size+got+1) < 0)
+ goto error;
+
+ memmove(buf+size, data, got);
+ buf[size+got] = '\0';
+ size += got;
+ }
+
+ /* Look for QEMU prompt to indicate completion */
+ if (buf) {
+ char *foundPrompt;
+
+ if (extraPrompt &&
+ (foundPrompt = strstr(buf, extraPrompt)) != NULL) {
+ char *promptEnd;
+
+ if (extraHandler(vm, buf, foundPrompt, handlerData) < 0)
+ return -1;
+ /* Discard output so far, necessary to detect whether
+ extraPrompt appears again. We don't need the output between
+ original command and this prompt anyway. */
+ promptEnd = foundPrompt + strlen(extraPrompt);
+ memmove(buf, promptEnd, strlen(promptEnd)+1);
+ size -= promptEnd - buf;
+ } else if ((tmp = strstr(buf, QEMU_CMD_PROMPT)) != NULL) {
+ char *commptr = NULL, *nlptr = NULL;
+ /* Preserve the newline */
+ tmp[1] = '\0';
+
+ /* The monitor doesn't dump clean output after we have written to
+ * it. Every character we write dumps a bunch of useless stuff,
+ * so the result looks like
"cXcoXcomXcommXcommaXcommanXcommand"
+ * Try to throw away everything before the first full command
+ * occurence, and inbetween the command and the newline starting
+ * the response
+ */
+ if ((commptr = strstr(buf, cmd))) {
+ memmove(buf, commptr, strlen(commptr)+1);
+ if ((nlptr = strchr(buf, '\n')))
+ memmove(buf+strlen(cmd), nlptr, strlen(nlptr)+1);
+ }
+
+ break;
+ }
+ }
+ pollagain:
+ /* Need to wait for more data */
+ if (poll(&fd, 1, -1) < 0) {
+ if (errno == EINTR)
+ goto pollagain;
+ goto error;
+ }
+ }
+ *reply = buf;
+ return 0;
+
+ error:
+ VIR_FREE(buf);
+ return -1;
+}
+
+struct extraHandlerData
+{
+ const char *reply;
+ bool first;
+};
+
+static int
+qemudMonitorCommandSimpleExtraHandler(const virDomainObjPtr vm,
+ const char *buf ATTRIBUTE_UNUSED,
+ const char *prompt ATTRIBUTE_UNUSED,
+ void *data_)
+{
+ struct extraHandlerData *data = data_;
+
+ if (!data->first)
+ return 0;
+ if (qemudMonitorSend(vm, data->reply, -1) < 0)
+ return -1;
+ data->first = false;
+ return 0;
+}
+
+int
+qemudMonitorCommandExtra(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extra,
+ const char *extraPrompt,
+ int scm_fd,
+ char **reply) {
+ struct extraHandlerData data;
+
+ data.reply = extra;
+ data.first = true;
+ return qemudMonitorCommandWithHandler(vm, cmd, extraPrompt,
+ qemudMonitorCommandSimpleExtraHandler,
+ &data, scm_fd, reply);
+}
+
+int
+qemudMonitorCommandWithFd(const virDomainObjPtr vm,
+ const char *cmd,
+ int scm_fd,
+ char **reply) {
+ return qemudMonitorCommandExtra(vm, cmd, NULL, NULL, scm_fd, reply);
+}
+
+int
+qemudMonitorCommand(const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply) {
+ return qemudMonitorCommandWithFd(vm, cmd, -1, reply);
+}
+
+
+
+static virStorageEncryptionPtr
+findDomainDiskEncryption(virConnectPtr conn, virDomainObjPtr vm,
+ const char *path)
+{
+ bool seen_volume;
+ int i;
+
+ seen_volume = false;
+ for (i = 0; i < vm->def->ndisks; i++) {
+ virDomainDiskDefPtr disk;
+
+ disk = vm->def->disks[i];
+ if (disk->src != NULL && STREQ(disk->src, path)) {
+ seen_volume = true;
+ if (disk->encryption != NULL)
+ return disk->encryption;
+ }
+ }
+ if (seen_volume)
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("missing <encryption> for volume %s"), path);
+ else
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
+ _("unexpected passphrase request for volume %s"),
+ path);
+ return NULL;
+}
+
+static char *
+findVolumeQcowPassphrase(virConnectPtr conn, virDomainObjPtr vm,
+ const char *path, size_t *passphrase_len)
+{
+ virStorageEncryptionPtr enc;
+ virSecretPtr secret;
+ char *passphrase;
+ unsigned char *data;
+ size_t size;
+
+ if (conn->secretDriver == NULL ||
+ conn->secretDriver->lookupByUUID == NULL ||
+ conn->secretDriver->getValue == NULL) {
+ qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT, "%s",
+ _("secret storage not supported"));
+ return NULL;
+ }
+
+ enc = findDomainDiskEncryption(conn, vm, path);
+ if (enc == NULL)
+ return NULL;
+
+ if (enc->format != VIR_STORAGE_ENCRYPTION_FORMAT_QCOW ||
+ enc->nsecrets != 1 ||
+ enc->secrets[0]->type !=
+ VIR_STORAGE_ENCRYPTION_SECRET_TYPE_PASSPHRASE) {
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_DOMAIN,
+ _("invalid <encryption> for volume %s"), path);
+ return NULL;
+ }
+
+ secret = conn->secretDriver->lookupByUUID(conn,
+ enc->secrets[0]->uuid);
+ if (secret == NULL)
+ return NULL;
+ data = conn->secretDriver->getValue(secret, &size,
+ VIR_SECRET_GET_VALUE_INTERNAL_CALL);
+ virUnrefSecret(secret);
+ if (data == NULL)
+ return NULL;
+
+ if (memchr(data, '\0', size) != NULL) {
+ memset(data, 0, size);
+ VIR_FREE(data);
+ qemudReportError(conn, NULL, NULL, VIR_ERR_INVALID_SECRET,
+ _("format='qcow' passphrase for %s must not contain
a "
+ "'\\0'"), path);
+ return NULL;
+ }
+
+ if (VIR_ALLOC_N(passphrase, size + 1) < 0) {
+ memset(data, 0, size);
+ VIR_FREE(data);
+ virReportOOMError(conn);
+ return NULL;
+ }
+ memcpy(passphrase, data, size);
+ passphrase[size] = '\0';
+
+ memset(data, 0, size);
+ VIR_FREE(data);
+
+ *passphrase_len = size;
+ return passphrase;
+}
+
+static int
+qemudMonitorSendVolumePassphrase(const virDomainObjPtr vm,
+ const char *buf,
+ const char *prompt,
+ void *data)
+{
+ virConnectPtr conn = data;
+ char *passphrase, *path;
+ const char *prompt_path;
+ size_t path_len, passphrase_len = 0;
+ int res;
+
+ /* The complete prompt looks like this:
+ ide0-hd0 (/path/to/volume) is encrypted.
+ Password:
+ "prompt" starts with ") is encrypted". Extract
/path/to/volume. */
+ for (prompt_path = prompt; prompt_path > buf && prompt_path[-1] !=
'(';
+ prompt_path--)
+ ;
+ if (prompt_path == buf)
+ return -1;
+ path_len = prompt - prompt_path;
+ if (VIR_ALLOC_N(path, path_len + 1) < 0)
+ return -1;
+ memcpy(path, prompt_path, path_len);
+ path[path_len] = '\0';
+
+ passphrase = findVolumeQcowPassphrase(conn, vm, path, &passphrase_len);
+ VIR_FREE(path);
+ if (passphrase == NULL)
+ return -1;
+
+ res = qemudMonitorSend(vm, passphrase, -1);
+
+ memset(passphrase, 0, passphrase_len);
+ VIR_FREE(passphrase);
+
+ return res;
+}
+
+int
+qemudMonitorSendCont(virConnectPtr conn,
+ const virDomainObjPtr vm) {
+ char *reply;
+
+ if (qemudMonitorCommandWithHandler(vm, "cont", ") is
encrypted.",
+ qemudMonitorSendVolumePassphrase, conn,
+ -1, &reply) < 0)
+ return -1;
+ qemudDebug ("%s: cont reply: %s", vm->def->name, info);
+ VIR_FREE(reply);
+ return 0;
+}
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
new file mode 100644
index 0000000..35ca81d
--- /dev/null
+++ b/src/qemu/qemu_monitor_text.h
@@ -0,0 +1,72 @@
+/*
+ * 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(a)redhat.com>
+ */
+
+
+#ifndef QEMU_MONITOR_TEXT_H
+#define QEMU_MONITOR_TEXT_H
+
+#include "internal.h"
+
+#include "domain_conf.h"
+
+/* XXX remove these two from public header */
+#define QEMU_CMD_PROMPT "\n(qemu) "
+#define QEMU_PASSWD_PROMPT "Password: "
+
+/* Return -1 for error, 0 for success */
+typedef int qemudMonitorExtraPromptHandler(const virDomainObjPtr vm,
+ const char *buf,
+ const char *prompt,
+ void *data);
+
+/* These first 4 APIs are generic monitor interaction. They will
+ * go away eventually
+ */
+int qemudMonitorCommand(const virDomainObjPtr vm,
+ const char *cmd,
+ char **reply);
+int qemudMonitorCommandWithFd(const virDomainObjPtr vm,
+ const char *cmd,
+ int scm_fd,
+ char **reply);
+int qemudMonitorCommandWithHandler(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extraPrompt,
+ qemudMonitorExtraPromptHandler extraHandler,
+ void *handlerData,
+ int scm_fd,
+ char **reply);
+int qemudMonitorCommandExtra(const virDomainObjPtr vm,
+ const char *cmd,
+ const char *extra,
+ const char *extraPrompt,
+ int scm_fd,
+ char **reply);
+
+/* Formal APIs for each required monitor command */
+
+int qemudMonitorSendCont(virConnectPtr conn,
+ const virDomainObjPtr vm);
+
+#endif /* QEMU_MONITOR_TEXT_H */
+
--
1.6.2.5