[libvirt] [PATCH 01/27] Pull QEMU monitor interaction out to separate file

Daniel P. Berrange berrange at redhat.com
Thu Sep 24 15:00:03 UTC 2009


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 at 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 at 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




More information about the libvir-list mailing list