diff --git a/po/POTFILES.in b/po/POTFILES.in index 67769c8da8..8b4404f9ca 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -25,6 +25,7 @@ src/openvz/openvz_driver.c src/phyp/phyp_driver.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c +src/qemu/qemu_monitor_text.c src/remote/remote_driver.c src/secret/secret_driver.c src/security/security_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index 9cbec479a1..b879056b50 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -169,6 +169,8 @@ 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_monitor_text.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 25d983e1da..9f17aae799 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 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 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 0000000000..81b966600d --- /dev/null +++ b/src/qemu/qemu_monitor_text.c @@ -0,0 +1,437 @@ +/* + * qemu_monitor_text.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 + */ + +#include + +#include +#include +#include +#include +#include + +#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 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 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 0000000000..fb95704605 --- /dev/null +++ b/src/qemu/qemu_monitor_text.h @@ -0,0 +1,72 @@ +/* + * qemu_monitor_text.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 + */ + + +#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 */ +