diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c index 58d695ea22..90a58cd86b 100644 --- a/src/qemu/qemu_monitor_text.c +++ b/src/qemu/qemu_monitor_text.c @@ -23,1127 +23,18 @@ #include -#include -#include -#include -#include -#include #include #include "qemu_monitor_text.h" -#include "qemu_alias.h" -#include "c-ctype.h" -#include "c-strcasestr.h" #include "viralloc.h" #include "virlog.h" -#include "driver.h" -#include "datatypes.h" #include "virerror.h" -#include "virbuffer.h" -#include "virprobe.h" #include "virstring.h" -#ifdef WITH_DTRACE_PROBES -# include "libvirt_qemu_probes.h" -#endif - #define VIR_FROM_THIS VIR_FROM_QEMU VIR_LOG_INIT("qemu.qemu_monitor_text"); -#define DEBUG_IO 0 - -/* Return -1 for error, 0 for success */ -typedef int qemuMonitorExtraPromptHandler(qemuMonitorPtr mon, - const char *buf, - const char *prompt, - void *data); - - -/* When connecting to a monitor, QEMU will print a greeting like - * - * QEMU 0.11.0 monitor - type 'help' for more information - * - * Don't expect the version number bit to be stable :-) - */ -#define GREETING_PREFIX "QEMU " -#define GREETING_POSTFIX "type 'help' for more information\r\n(qemu) " -#define BASIC_PROMPT "(qemu) " -#define PASSWORD_PROMPT "Password:" -#define DISK_ENCRYPTION_PREFIX "(" -#define DISK_ENCRYPTION_POSTFIX ") is encrypted." -#define LINE_ENDING "\r\n" - -int qemuMonitorTextIOProcess(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - const char *data, - size_t len ATTRIBUTE_UNUSED, - qemuMonitorMessagePtr msg) -{ - int used = 0; - - /* Check for & discard greeting */ - if (STRPREFIX(data, GREETING_PREFIX)) { - const char *offset = strstr(data, GREETING_POSTFIX); - - /* We see the greeting prefix, but not postfix, so pretend we've - not consumed anything. We'll restart when more data arrives. */ - if (!offset) { -#if DEBUG_IO - VIR_DEBUG("Partial greeting seen, getting out & waiting for more"); -#endif - return 0; - } - - used = offset - data + strlen(GREETING_POSTFIX); - -#if DEBUG_IO - VIR_DEBUG("Discarded monitor greeting"); -#endif - } - - /* Don't print raw data in debug because its full of control chars */ - /*VIR_DEBUG("Process data %d byts of data [%s]", len - used, data + used);*/ -#if DEBUG_IO - VIR_DEBUG("Process data %d byts of data", (int)(len - used)); -#endif - - /* Look for a non-zero reply followed by prompt */ - if (msg && !msg->finished) { - char *start = NULL; - char *end = NULL; - char *skip; - - /* If we're here, we've already sent the command. We now - * strip the trailing '\r' because it makes the matching - * code that follows a little easier ie we can just strstr() - * for the original command - */ - if (msg->txLength > 0) { - char *tmp; - if ((tmp = strchr(msg->txBuffer, '\r'))) - *tmp = '\0'; - } - - /* QEMU echos the command back to us, full of control - * character junk that we don't want. We have to skip - * over this junk by looking for the first complete - * repetition of our command. Then we can look for - * the prompt that is supposed to follow - * - * NB, we can't optimize by immediately looking for - * LINE_ENDING, because QEMU 0.10 has bad problems - * when initially connecting where it might write a - * prompt in the wrong place. So we must not look - * for LINE_ENDING, or BASIC_PROMPT until we've - * seen our original command echod. - */ - skip = strstr(data + used, msg->txBuffer); - - /* After the junk we should have a line ending... */ - if (skip) - start = strstr(skip + strlen(msg->txBuffer), LINE_ENDING); - - /* ... then our command reply data, following by a (qemu) prompt */ - if (start) { - char *passwd; - start += strlen(LINE_ENDING); - - /* We might get a prompt for a password before the (qemu) prompt */ - passwd = strstr(start, PASSWORD_PROMPT); - if (passwd) { -#if DEBUG_IO - VIR_DEBUG("Seen a password prompt [%s]", data + used); -#endif - if (msg->passwordHandler) { - size_t i; - /* Try and handle the prompt. The handler is required - * to report a normal libvirt error */ - if (msg->passwordHandler(mon, msg, - start, - passwd - start + strlen(PASSWORD_PROMPT), - msg->passwordOpaque) < 0) - return -1; - - /* Blank out the password prompt so we don't re-trigger - * if we have to go back to sleep for more I/O */ - for (i = 0; i < strlen(PASSWORD_PROMPT); i++) - start[i] = ' '; - - /* Handled, so skip forward over password prompt */ - start = passwd; - } else { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("Password request seen, but no handler available")); - return -1; - } - } - - end = strstr(start, BASIC_PROMPT); - } - - if (start && end) { - int want = end - start; - /* Annoyingly some commands may not have any reply data - * at all upon success, but since we've detected the - * BASIC_PROMPT we can reasonably reliably cope */ - if (want) { - if (VIR_REALLOC_N(msg->rxBuffer, - msg->rxLength + want + 1) < 0) - return -1; - memcpy(msg->rxBuffer + msg->rxLength, start, want); - msg->rxLength += want; - msg->rxBuffer[msg->rxLength] = '\0'; -#if DEBUG_IO - VIR_DEBUG("Finished %d byte reply [%s]", want, msg->rxBuffer); - } else { - VIR_DEBUG("Finished 0 byte reply"); -#endif - } - PROBE(QEMU_MONITOR_RECV_REPLY, - "mon=%p reply=%s", - mon, msg->rxBuffer); - msg->finished = 1; - used += end - (data + used); - used += strlen(BASIC_PROMPT); - } - } - -#if DEBUG_IO - VIR_DEBUG("Total used %d", used); -#endif - return used; -} - -static int -qemuMonitorTextCommandWithHandler(qemuMonitorPtr mon, - const char *cmd, - qemuMonitorPasswordHandler passwordHandler, - void *passwordOpaque, - int scm_fd, - char **reply) -{ - int ret; - qemuMonitorMessage msg; - - *reply = NULL; - - memset(&msg, 0, sizeof(msg)); - - if (virAsprintf(&msg.txBuffer, "%s\r", cmd) < 0) - return -1; - msg.txLength = strlen(msg.txBuffer); - msg.txFD = scm_fd; - msg.passwordHandler = passwordHandler; - msg.passwordOpaque = passwordOpaque; - - VIR_DEBUG("Send command '%s' for write with FD %d", cmd, scm_fd); - - ret = qemuMonitorSend(mon, &msg); - - VIR_DEBUG("Receive command reply ret=%d rxLength=%d rxBuffer='%s'", - ret, msg.rxLength, msg.rxBuffer); - - /* Just in case buffer had some passwords in */ - memset(msg.txBuffer, 0, msg.txLength); - VIR_FREE(msg.txBuffer); - - if (ret >= 0) { - /* To make life safer for callers, already ensure there's at least an empty string */ - if (msg.rxBuffer) { - *reply = msg.rxBuffer; - } else { - if (VIR_STRDUP(*reply, "") < 0) - return -1; - } - } - - return ret; -} - -int -qemuMonitorTextCommandWithFd(qemuMonitorPtr mon, - const char *cmd, - int scm_fd, - char **reply) -{ - return qemuMonitorTextCommandWithHandler(mon, cmd, NULL, NULL, - scm_fd, reply); -} - -/* Check monitor output for evidence that the command was not recognized. - * For 'info' commands, qemu returns help text. For other commands, qemu - * returns 'unknown command:'. - */ -static int -qemuMonitorTextCommandNotFound(const char *cmd, const char *reply) -{ - if (STRPREFIX(cmd, "info ")) { - if (strstr(reply, "info version")) - return 1; - } else { - if (strstr(reply, "unknown command:")) - return 1; - } - - return 0; -} - -static int -qemuMonitorSendDiskPassphrase(qemuMonitorPtr mon, - qemuMonitorMessagePtr msg, - const char *data, - size_t len ATTRIBUTE_UNUSED, - void *opaque ATTRIBUTE_UNUSED) -{ - char *path; - char *passphrase = NULL; - size_t passphrase_len = 0; - int res; - const char *pathStart; - const char *pathEnd; - - /* - * For disk passwords: - * - * ide0-hd0 (/path/to/volume) is encrypted. - * Password: - * - */ - pathStart = strstr(data, DISK_ENCRYPTION_PREFIX); - pathEnd = strstr(data, DISK_ENCRYPTION_POSTFIX); - if (!pathStart || !pathEnd || pathStart >= pathEnd) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Unable to extract disk path from %s"), - data); - return -1; - } - - /* Extra the path */ - pathStart += strlen(DISK_ENCRYPTION_PREFIX); - if (VIR_STRNDUP(path, pathStart, pathEnd - pathStart) < 0) - return -1; - - /* Fetch the disk password if possible */ - res = qemuMonitorGetDiskSecret(mon, - path, - &passphrase, - &passphrase_len); - VIR_FREE(path); - if (res < 0) - return -1; - - /* Enlarge transmit buffer to allow for the extra data - * to be sent back */ - if (VIR_REALLOC_N(msg->txBuffer, - msg->txLength + passphrase_len + 1 + 1) < 0) { - memset(passphrase, 0, passphrase_len); - VIR_FREE(passphrase); - return -1; - } - - /* Queue the password for sending */ - memcpy(msg->txBuffer + msg->txLength, - passphrase, passphrase_len); - msg->txLength += passphrase_len; - msg->txBuffer[msg->txLength] = '\r'; - msg->txLength++; - msg->txBuffer[msg->txLength] = '\0'; - - memset(passphrase, 0, passphrase_len); - VIR_FREE(passphrase); - - return 0; -} - -int -qemuMonitorTextStartCPUs(qemuMonitorPtr mon) -{ - char *reply; - - if (qemuMonitorTextCommandWithHandler(mon, "cont", - qemuMonitorSendDiskPassphrase, - NULL, - -1, &reply) < 0) - return -1; - - VIR_FREE(reply); - return 0; -} - - -int -qemuMonitorTextStopCPUs(qemuMonitorPtr mon) -{ - char *info; - int ret; - - ret = qemuMonitorHMPCommand(mon, "stop", &info); - VIR_FREE(info); - return ret; -} - - -int -qemuMonitorTextGetStatus(qemuMonitorPtr mon, - bool *running, - virDomainPausedReason *reason) -{ - char *reply; - int ret = -1; - - if (reason) - *reason = VIR_DOMAIN_PAUSED_UNKNOWN; - - if (qemuMonitorHMPCommand(mon, "info status", &reply) < 0) - return -1; - - if (strstr(reply, "running")) { - *running = true; - } else if (strstr(reply, "paused")) { - char *status; - - if ((status = strchr(reply, '('))) { - char *end = strchr(status, ')'); - if (end) - *end = '\0'; - else - status = NULL; - } - if (!status) - VIR_DEBUG("info status was missing status details"); - else if (reason) - *reason = qemuMonitorVMStatusToPausedReason(status); - *running = false; - } else { - virReportError(VIR_ERR_OPERATION_FAILED, - _("unexpected reply from info status: %s"), reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - return ret; -} - - -int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon) -{ - char *info; - int ret; - - ret = qemuMonitorHMPCommand(mon, "system_powerdown", &info); - - VIR_FREE(info); - return ret; -} - -int -qemuMonitorTextSetLink(qemuMonitorPtr mon, - const char *name, - virDomainNetInterfaceLinkState state) -{ - char *info = NULL; - char *cmd = NULL; - const char *st_str = NULL; - - /* determine state */ - if (state == VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN) - st_str = "off"; - else - st_str = "on"; - - if (virAsprintf(&cmd, "set_link %s %s", name, st_str) < 0) - goto error; - if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) - goto error; - - /* check if set_link command is supported */ - if (strstr(info, "\nunknown ")) { - virReportError(VIR_ERR_OPERATION_UNSUPPORTED, - "%s", - _("\'set_link\' not supported by this qemu")); - goto error; - } - - /* check if qemu didn't reject device name */ - if (strstr(info, "\nDevice ")) { - virReportError(VIR_ERR_OPERATION_FAILED, - "%s", _("device name rejected")); - goto error; - } - - VIR_FREE(info); - VIR_FREE(cmd); - return 0; - - error: - VIR_FREE(info); - VIR_FREE(cmd); - - return -1; -} - -int qemuMonitorTextSystemReset(qemuMonitorPtr mon) -{ - char *info; - int ret; - - ret = qemuMonitorHMPCommand(mon, "system_reset", &info); - - VIR_FREE(info); - return ret; -} - - -int -qemuMonitorTextQueryCPUs(qemuMonitorPtr mon, - struct qemuMonitorQueryCpusEntry **entries, - size_t *nentries) -{ - char *qemucpus = NULL; - char *line; - struct qemuMonitorQueryCpusEntry *cpus = NULL; - size_t ncpus = 0; - struct qemuMonitorQueryCpusEntry cpu = {0}; - int ret = -2; /* -2 denotes a non-fatal error to get the data */ - - if (qemuMonitorHMPCommand(mon, "info cpus", &qemucpus) < 0) - return -1; - - /* - * This is the gross format we're about to parse :-{ - * - * (qemu) info cpus - * * CPU #0: pc=0x00000000000f0c4a thread_id=30019 - * CPU #1: pc=0x00000000fffffff0 thread_id=30020 - * CPU #2: pc=0x00000000fffffff0 (halted) thread_id=30021 - * - */ - line = qemucpus; - do { - char *offset = NULL; - char *end = NULL; - int cpuid = -1; - int tid = 0; - - /* extract cpu number */ - if ((offset = strstr(line, "#")) == NULL) - goto cleanup; - - if (virStrToLong_i(offset + strlen("#"), &end, 10, &cpuid) < 0) - goto cleanup; - if (end == NULL || *end != ':') - goto cleanup; - - /* Extract host Thread ID */ - if ((offset = strstr(line, "thread_id=")) == NULL) - goto cleanup; - - if (virStrToLong_i(offset + strlen("thread_id="), &end, 10, &tid) < 0) - goto cleanup; - if (end == NULL || !c_isspace(*end)) - goto cleanup; - - cpu.qemu_id = cpuid; - cpu.tid = tid; - - if (VIR_APPEND_ELEMENT_COPY(cpus, ncpus, cpu) < 0) { - ret = -1; - goto cleanup; - } - - VIR_DEBUG("tid=%d", tid); - - /* Skip to next data line */ - line = strchr(offset, '\r'); - if (line == NULL) - line = strchr(offset, '\n'); - } while (line != NULL); - - VIR_STEAL_PTR(*entries, cpus); - *nentries = ncpus; - ret = 0; - - cleanup: - qemuMonitorQueryCpusFree(cpus, ncpus); - VIR_FREE(qemucpus); - return ret; -} - - -int qemuMonitorTextGetVirtType(qemuMonitorPtr mon, - virDomainVirtType *virtType) -{ - char *reply = NULL; - - *virtType = VIR_DOMAIN_VIRT_QEMU; - - if (qemuMonitorHMPCommand(mon, "info kvm", &reply) < 0) - return -1; - - if (strstr(reply, "enabled")) - *virtType = VIR_DOMAIN_VIRT_KVM; - - VIR_FREE(reply); - return 0; -} - - -static int parseMemoryStat(char **text, unsigned int tag, - const char *search, virDomainMemoryStatPtr mstat) -{ - char *dummy; - unsigned long long value; - - if (STRPREFIX(*text, search)) { - *text += strlen(search); - if (virStrToLong_ull(*text, &dummy, 10, &value)) { - VIR_DEBUG("error reading %s: %s", search, *text); - return 0; - } - - switch (tag) { - /* Convert megabytes to kilobytes for libvirt */ - case VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON: - value <<= 10; - break; - /* Convert bytes to kilobytes for libvirt */ - case VIR_DOMAIN_MEMORY_STAT_SWAP_IN: - case VIR_DOMAIN_MEMORY_STAT_SWAP_OUT: - case VIR_DOMAIN_MEMORY_STAT_UNUSED: - case VIR_DOMAIN_MEMORY_STAT_AVAILABLE: - value >>= 10; - } - mstat->tag = tag; - mstat->val = value; - return 1; - } - return 0; -} - -/* The reply from the 'info balloon' command may contain additional memory - * statistics in the form: 'actual= [,=]*' - */ -static int qemuMonitorParseBalloonInfo(char *text, - virDomainMemoryStatPtr stats, - unsigned int nr_stats) -{ - char *p = text; - unsigned int nr_stats_found = 0; - - /* Since "actual=" always comes first in the returned string, - * and sometime we only care about the value of "actual", such - * as qemuMonitorGetBalloonInfo, we parse it outside of the - * loop. - */ - if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON, - "actual=", &stats[nr_stats_found]) == 1) { - nr_stats_found++; - } - - while (*p && nr_stats_found < nr_stats) { - if (parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_IN, - ",mem_swapped_in=", &stats[nr_stats_found]) || - parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, - ",mem_swapped_out=", &stats[nr_stats_found]) || - parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, - ",major_page_faults=", &stats[nr_stats_found]) || - parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, - ",minor_page_faults=", &stats[nr_stats_found]) || - parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_UNUSED, - ",free_mem=", &stats[nr_stats_found]) || - parseMemoryStat(&p, VIR_DOMAIN_MEMORY_STAT_AVAILABLE, - ",total_mem=", &stats[nr_stats_found])) - nr_stats_found++; - - /* Skip to the next label. When *p is ',' the last match attempt - * failed so try to match the next ','. - */ - if (*p == ',') - p++; - p = strchr(p, ','); - if (!p) break; - } - return nr_stats_found; -} - - -/* The reply from QEMU contains 'ballon: actual=421' where value is in MB */ -#define BALLOON_PREFIX "balloon: " - -int -qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, - unsigned long long *currmem) -{ - char *reply = NULL; - int ret = -1; - char *offset; - - if (qemuMonitorHMPCommand(mon, "info balloon", &reply) < 0) - return -1; - - if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { - offset += strlen(BALLOON_PREFIX); - virDomainMemoryStatStruct stats[1]; - - if (qemuMonitorParseBalloonInfo(offset, stats, 1) == 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected balloon information '%s'"), reply); - goto cleanup; - } - - if (stats[0].tag != VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected balloon information '%s'"), reply); - goto cleanup; - } - - *currmem = stats[0].val; - ret = 1; - } else { - /* We don't raise an error here, since its to be expected that - * many QEMU's don't support ballooning - */ - ret = 0; - } - - cleanup: - VIR_FREE(reply); - return ret; -} - -int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, - virDomainMemoryStatPtr stats, - unsigned int nr_stats) -{ - char *reply = NULL; - int ret = 0; - char *offset; - - if (qemuMonitorHMPCommand(mon, "info balloon", &reply) < 0) - return -1; - - if ((offset = strstr(reply, BALLOON_PREFIX)) != NULL) { - offset += strlen(BALLOON_PREFIX); - ret = qemuMonitorParseBalloonInfo(offset, stats, nr_stats); - } - - VIR_FREE(reply); - return ret; -} - - -int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon, - virHashTablePtr table) -{ - struct qemuDomainDiskInfo *info = NULL; - char *reply = NULL; - int ret = -1; - char *dummy; - char *p, *eol; - char *dev; - int tmp; - - if (qemuMonitorHMPCommand(mon, "info block", &reply) < 0) - goto cleanup; - - if (strstr(reply, "\ninfo ")) { - virReportError(VIR_ERR_OPERATION_INVALID, - "%s", - _("info block not supported by this qemu")); - goto cleanup; - } - - /* The output looks like this: - * drive-ide0-0-0: removable=0 file= ro=0 drv=raw encrypted=0 - * drive-ide0-1-0: removable=1 locked=0 file= ro=1 drv=raw encrypted=0 - */ - p = reply; - - while (*p) { - p = (char *)qemuAliasDiskDriveSkipPrefix(p); - - eol = strchr(p, '\n'); - if (!eol) - eol = p + strlen(p) - 1; - - dev = p; - p = strchr(p, ':'); - if (p && p < eol && *(p + 1) == ' ') { - if (VIR_ALLOC(info) < 0) - goto cleanup; - - *p = '\0'; - p += 2; - - while (p < eol) { - if (STRPREFIX(p, "removable=")) { - p += strlen("removable="); - if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) - VIR_DEBUG("error reading removable: %s", p); - else - info->removable = (tmp != 0); - } else if (STRPREFIX(p, "locked=")) { - p += strlen("locked="); - if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) - VIR_DEBUG("error reading locked: %s", p); - else - info->locked = (tmp != 0); - } else if (STRPREFIX(p, "tray-open=")) { - p += strlen("tray-open="); - if (virStrToLong_i(p, &dummy, 10, &tmp) == -1) - VIR_DEBUG("error reading tray-open: %s", p); - else - info->tray_open = (tmp != 0); - } else if (STRPREFIX(p, "io-status=")) { - char *end; - char c; - - p += strlen("io-status="); - end = strchr(p, ' '); - if (!end || end > eol) - end = eol; - - c = *end; - *end = '\0'; - info->io_status = qemuMonitorBlockIOStatusToError(p); - *end = c; - if (info->io_status < 0) - goto cleanup; - } else { - /* ignore because we don't parse all options */ - } - - /* skip to next label */ - p = strchr(p, ' '); - if (!p) - break; - p++; - } - - if (virHashAddEntry(table, dev, info) < 0) - goto cleanup; - else - info = NULL; - } - - /* skip to the next line */ - p = eol + 1; - } - - ret = 0; - - cleanup: - VIR_FREE(info); - VIR_FREE(reply); - return ret; -} - - -int -qemuMonitorTextGetAllBlockStatsInfo(qemuMonitorPtr mon, - virHashTablePtr hash) -{ - qemuBlockStatsPtr stats = NULL; - char *info = NULL; - const char *dev_name; - char **lines = NULL; - char **values = NULL; - char *line; - char *value; - char *key; - size_t i; - size_t j; - int ret = -1; - int nstats; - int maxstats = 0; - - if (qemuMonitorHMPCommand(mon, "info blockstats", &info) < 0) - goto cleanup; - - /* If the command isn't supported then qemu prints the supported info - * commands, so the output starts "info ". Since this is unlikely to be - * the name of a block device, we can use this to detect if qemu supports - * the command. */ - if (strstr(info, "\ninfo ")) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("'info blockstats' not supported by this qemu")); - goto cleanup; - } - - /* The output format for both qemu & KVM is: - * blockdevice: rd_bytes=% wr_bytes=% rd_operations=% wr_operations=% - * (repeated for each block device) - * where '%' is a 64 bit number. - */ - if (!(lines = virStringSplit(info, "\n", 0))) - goto cleanup; - - for (i = 0; lines[i] && *lines[i]; i++) { - line = lines[i]; - - if (VIR_ALLOC(stats) < 0) - goto cleanup; - - /* set the entries to -1, the JSON monitor enforces them, but it would - * be overly complex to achieve this here */ - stats->rd_req = -1; - stats->rd_bytes = -1; - stats->wr_req = -1; - stats->wr_bytes = -1; - stats->rd_total_times = -1; - stats->wr_total_times = -1; - stats->flush_req = -1; - stats->flush_total_times = -1; - - /* extract device name and make sure that it's followed by - * a colon and space */ - dev_name = line; - if (!(line = strchr(line, ':')) || line[1] != ' ') { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("info blockstats reply was malformed")); - goto cleanup; - } - - *line = '\0'; - line += 2; - - dev_name = qemuAliasDiskDriveSkipPrefix(dev_name); - - if (!(values = virStringSplit(line, " ", 0))) - goto cleanup; - - nstats = 0; - - for (j = 0; values[j] && *values[j]; j++) { - key = values[j]; - - if (!(value = strchr(key, '='))) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("info blockstats entry was malformed")); - goto cleanup; - } - - *value = '\0'; - value++; - -#define QEMU_MONITOR_TEXT_READ_BLOCK_STAT(NAME, VAR) \ - if (STREQ(key, NAME)) { \ - nstats++; \ - if (virStrToLong_ll(value, NULL, 10, &VAR) < 0) { \ - virReportError(VIR_ERR_INTERNAL_ERROR, \ - _("'info blockstats' contains malformed " \ - "parameter '%s' value '%s'"), NAME, value);\ - goto cleanup; \ - } \ - continue; \ - } - - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_bytes", stats->rd_bytes); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_bytes", stats->wr_bytes); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_operations", stats->rd_req); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_operations", stats->wr_req); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("rd_total_time_ns", stats->rd_total_times); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("wr_total_time_ns", stats->wr_total_times); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("flush_operations", stats->flush_req); - QEMU_MONITOR_TEXT_READ_BLOCK_STAT("flush_total_time_ns", stats->flush_total_times); -#undef QEMU_MONITOR_TEXT_READ_BLOCK_STAT - - /* log if we get statistic element different from the above */ - VIR_DEBUG("unknown block stat field '%s'", key); - } - - if (nstats > maxstats) - maxstats = nstats; - - if (virHashAddEntry(hash, dev_name, stats) < 0) - goto cleanup; - stats = NULL; - - virStringListFree(values); - values = NULL; - } - - ret = maxstats; - - cleanup: - virStringListFree(lines); - virStringListFree(values); - VIR_FREE(stats); - VIR_FREE(info); - return ret; -} - -/* Return 0 on success, -1 on failure, or -2 if not supported. Size - * is in bytes. */ -int qemuMonitorTextBlockResize(qemuMonitorPtr mon, - const char *device, - unsigned long long size) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "block_resize %s %lluB", device, size) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (strstr(reply, "unknown command:")) { - ret = -2; - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - -static int -qemuMonitorSendVNCPassphrase(qemuMonitorPtr mon ATTRIBUTE_UNUSED, - qemuMonitorMessagePtr msg, - const char *data ATTRIBUTE_UNUSED, - size_t len ATTRIBUTE_UNUSED, - void *opaque) -{ - char *passphrase = opaque; - size_t passphrase_len = strlen(passphrase); - - /* Enlarge transmit buffer to allow for the extra data - * to be sent back */ - if (VIR_REALLOC_N(msg->txBuffer, - msg->txLength + passphrase_len + 1 + 1) < 0) - return -1; - - /* Queue the password for sending */ - memcpy(msg->txBuffer + msg->txLength, - passphrase, passphrase_len); - msg->txLength += passphrase_len; - msg->txBuffer[msg->txLength] = '\r'; - msg->txLength++; - msg->txBuffer[msg->txLength] = '\0'; - - return 0; -} - -int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, - const char *password) -{ - char *info = NULL; - - if (qemuMonitorTextCommandWithHandler(mon, "change vnc password", - qemuMonitorSendVNCPassphrase, - (char *)password, - -1, &info) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("setting VNC password failed")); - return -1; - } - VIR_FREE(info); - return 0; -} - -/* Returns -1 on error, -2 if not supported */ -int qemuMonitorTextSetPassword(qemuMonitorPtr mon, - const char *protocol, - const char *password, - const char *action_if_connected) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "set_password %s \"%s\" %s", - protocol, password, action_if_connected) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (strstr(reply, "unknown command:")) { - ret = -2; - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - return ret; -} - -/* Returns -1 on error, -2 if not supported */ -int qemuMonitorTextExpirePassword(qemuMonitorPtr mon, - const char *protocol, - const char *expire_time) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "expire_password %s %s", - protocol, expire_time) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (strstr(reply, "unknown command:")) { - ret = -2; - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - return ret; -} - - -int -qemuMonitorTextSetBalloon(qemuMonitorPtr mon, - unsigned long long newmem) -{ - char *cmd; - char *reply = NULL; - int ret = -1; - - /* - * 'newmem' is in KB, QEMU monitor works in MB, and we all wish - * we just worked in bytes with unsigned long long everywhere. - */ - if (virAsprintf(&cmd, "balloon %llu", VIR_DIV_UP(newmem, 1024)) < 0) - return -1; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) { - VIR_FREE(cmd); - return -1; - } - VIR_FREE(cmd); - - /* If the command failed qemu prints: 'unknown command' - * No message is printed on success it seems */ - if (strstr(reply, "unknown command:")) { - /* Don't set error - it is expected memory balloon fails on many qemu */ - ret = 0; - } else { - ret = 1; - } - - VIR_FREE(reply); - return ret; -} - - int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, bool online) { char *cmd; @@ -1174,696 +65,6 @@ int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, bool online) } -/** - * Run HMP command to eject a media from ejectable device. - * - * Returns: - * -1 on error - * 0 on success - */ -int qemuMonitorTextEjectMedia(qemuMonitorPtr mon, - const char *dev_name, - bool force) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "eject %s%s", force ? "-f " : "", dev_name) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* If the command failed qemu prints: - * device not found, device is locked ... - * No message is printed on success it seems */ - if (c_strcasestr(reply, "device ")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("could not eject media on %s: %s"), dev_name, reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - return ret; -} - - -int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, - const char *dev_name, - const char *newmedia, - const char *format ATTRIBUTE_UNUSED) -{ - char *cmd = NULL; - char *reply = NULL; - char *safepath = NULL; - int ret = -1; - - if (!(safepath = qemuMonitorEscapeArg(newmedia))) - goto cleanup; - - if (virAsprintf(&cmd, "change %s \"%s\"", dev_name, safepath) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* If the command failed qemu prints: - * device not found, device is locked ... - * No message is printed on success it seems */ - if (c_strcasestr(reply, "device ")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("could not change media on %s: %s"), dev_name, reply); - goto cleanup; - } - - /* Could not open message indicates bad filename */ - if (strstr(reply, "Could not open ")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("could not change media on %s: %s"), dev_name, reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - VIR_FREE(safepath); - return ret; -} - -static int qemuMonitorTextSaveMemory(qemuMonitorPtr mon, - const char *cmdtype, - unsigned long long offset, - size_t length, - const char *path) -{ - char *cmd = NULL; - char *reply = NULL; - char *safepath = NULL; - int ret = -1; - - if (!(safepath = qemuMonitorEscapeArg(path))) - goto cleanup; - - if (virAsprintf(&cmd, "%s %llu %zi \"%s\"", cmdtype, offset, length, safepath) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* XXX what is printed on failure ? */ - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - VIR_FREE(safepath); - return ret; -} - - -int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon, - unsigned long long offset, - size_t length, - const char *path) -{ - return qemuMonitorTextSaveMemory(mon, "memsave", offset, length, path); -} - -int qemuMonitorTextSavePhysicalMemory(qemuMonitorPtr mon, - unsigned long long offset, - size_t length, - const char *path) -{ - return qemuMonitorTextSaveMemory(mon, "pmemsave", offset, length, path); -} - - -int qemuMonitorTextSetMigrationSpeed(qemuMonitorPtr mon, - unsigned long bandwidth) -{ - char *cmd = NULL; - char *info = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "migrate_set_speed %lum", bandwidth) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FREE(info); - VIR_FREE(cmd); - return ret; -} - - -int qemuMonitorTextSetMigrationDowntime(qemuMonitorPtr mon, - unsigned long long downtime) -{ - char *cmd = NULL; - char *info = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "migrate_set_downtime %llums", downtime) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FREE(info); - VIR_FREE(cmd); - return ret; -} - - -#define MIGRATION_PREFIX "Migration status: " -#define MIGRATION_TRANSFER_PREFIX "transferred ram: " -#define MIGRATION_REMAINING_PREFIX "remaining ram: " -#define MIGRATION_TOTAL_PREFIX "total ram: " -#define MIGRATION_DISK_TRANSFER_PREFIX "transferred disk: " -#define MIGRATION_DISK_REMAINING_PREFIX "remaining disk: " -#define MIGRATION_DISK_TOTAL_PREFIX "total disk: " - -int qemuMonitorTextGetMigrationStats(qemuMonitorPtr mon, - qemuMonitorMigrationStatsPtr stats) -{ - char *reply; - char *tmp; - char *end; - int ret = -1; - - memset(stats, 0, sizeof(*stats)); - - if (qemuMonitorHMPCommand(mon, "info migrate", &reply) < 0) - return -1; - - if ((tmp = strstr(reply, MIGRATION_PREFIX)) != NULL) { - tmp += strlen(MIGRATION_PREFIX); - end = strchr(tmp, '\r'); - if (end == NULL) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected migration status in %s"), reply); - goto cleanup; - } - *end = '\0'; - - stats->status = qemuMonitorMigrationStatusTypeFromString(tmp); - if (stats->status < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unexpected migration status in %s"), reply); - goto cleanup; - } - - if (stats->status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) { - tmp = end + 1; - - if (!(tmp = strstr(tmp, MIGRATION_TRANSFER_PREFIX))) - goto done; - tmp += strlen(MIGRATION_TRANSFER_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, - &stats->ram_transferred) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse migration data transferred " - "statistic %s"), tmp); - goto cleanup; - } - stats->ram_transferred *= 1024; - tmp = end; - - if (!(tmp = strstr(tmp, MIGRATION_REMAINING_PREFIX))) - goto done; - tmp += strlen(MIGRATION_REMAINING_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, &stats->ram_remaining) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse migration data remaining " - "statistic %s"), tmp); - goto cleanup; - } - stats->ram_remaining *= 1024; - tmp = end; - - if (!(tmp = strstr(tmp, MIGRATION_TOTAL_PREFIX))) - goto done; - tmp += strlen(MIGRATION_TOTAL_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, &stats->ram_total) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse migration data total " - "statistic %s"), tmp); - goto cleanup; - } - stats->ram_total *= 1024; - tmp = end; - - /* - * Check for Optional Disk Migration stats - */ - if (!(tmp = strstr(tmp, MIGRATION_DISK_TRANSFER_PREFIX))) - goto done; - tmp += strlen(MIGRATION_DISK_TRANSFER_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, - &stats->disk_transferred) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse disk migration data " - "transferred statistic %s"), tmp); - goto cleanup; - } - stats->disk_transferred *= 1024; - tmp = end; - - if (!(tmp = strstr(tmp, MIGRATION_DISK_REMAINING_PREFIX))) - goto done; - tmp += strlen(MIGRATION_DISK_REMAINING_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, &stats->disk_remaining) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse disk migration data remaining " - "statistic %s"), tmp); - goto cleanup; - } - stats->disk_remaining *= 1024; - tmp = end; - - if (!(tmp = strstr(tmp, MIGRATION_DISK_TOTAL_PREFIX))) - goto done; - tmp += strlen(MIGRATION_DISK_TOTAL_PREFIX); - - if (virStrToLong_ull(tmp, &end, 10, &stats->disk_total) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("cannot parse disk migration data total " - "statistic %s"), tmp); - goto cleanup; - } - stats->disk_total *= 1024; - } - } - - done: - ret = 0; - - cleanup: - VIR_FREE(reply); - if (ret < 0) - memset(stats, 0, sizeof(*stats)); - return ret; -} - - -int qemuMonitorTextMigrate(qemuMonitorPtr mon, - unsigned int flags, - const char *dest) -{ - char *cmd = NULL; - char *info = NULL; - int ret = -1; - char *safedest = qemuMonitorEscapeArg(dest); - virBuffer extra = VIR_BUFFER_INITIALIZER; - char *extrastr = NULL; - - if (!safedest) - return -1; - - if (flags & QEMU_MONITOR_MIGRATE_BACKGROUND) - virBufferAddLit(&extra, " -d"); - if (flags & QEMU_MONITOR_MIGRATE_NON_SHARED_DISK) - virBufferAddLit(&extra, " -b"); - if (flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC) - virBufferAddLit(&extra, " -i"); - if (virBufferCheckError(&extra) < 0) - goto cleanup; - - extrastr = virBufferContentAndReset(&extra); - if (virAsprintf(&cmd, "migrate %s\"%s\"", extrastr ? extrastr : "", - safedest) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) - goto cleanup; - - /* Now check for "fail" in the output string */ - if (strstr(info, "fail") != NULL) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("migration to '%s' failed: %s"), dest, info); - goto cleanup; - } - /* If the command isn't supported then qemu prints: - * unknown command: migrate" */ - if (strstr(info, "unknown command:")) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("migration to '%s' not supported by this qemu: %s"), dest, info); - goto cleanup; - } - - - ret = 0; - - cleanup: - VIR_FREE(extrastr); - VIR_FREE(safedest); - VIR_FREE(info); - VIR_FREE(cmd); - return ret; -} - -int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon) -{ - char *info = NULL; - int ret; - - ret = qemuMonitorHMPCommand(mon, "migrate_cancel", &info); - - VIR_FREE(info); - return ret; -} - - -int qemuMonitorTextGraphicsRelocate(qemuMonitorPtr mon, - int type, - const char *hostname, - int port, - int tlsPort, - const char *tlsSubject) -{ - char *cmd; - char *info = NULL; - - if (virAsprintf(&cmd, "client_migrate_info %s %s %d %d %s", - type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE ? "spice" : "vnc", - hostname, port, tlsPort, tlsSubject ? tlsSubject : "") < 0) - return -1; - - if (qemuMonitorHMPCommand(mon, cmd, &info) < 0) { - VIR_FREE(cmd); - return -1; - } - VIR_FREE(cmd); - VIR_FREE(info); - - return 0; -} - - -int qemuMonitorTextSendFileHandle(qemuMonitorPtr mon, - const char *fdname, - int fd) -{ - char *cmd; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "getfd %s", fdname) < 0) - return -1; - - if (qemuMonitorHMPCommandWithFd(mon, cmd, fd, &reply) < 0) - goto cleanup; - - /* If the command isn't supported then qemu prints: - * unknown command: getfd" */ - if (strstr(reply, "unknown command:")) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("qemu does not support sending of file handles: %s"), - reply); - goto cleanup; - } - - if (STRNEQ(reply, "")) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to send file handle '%s': %s"), - fdname, reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - - -int qemuMonitorTextCloseFileHandle(qemuMonitorPtr mon, - const char *fdname) -{ - char *cmd; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "closefd %s", fdname) < 0) - return -1; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* If the command isn't supported then qemu prints: - * unknown command: getfd" */ - if (strstr(reply, "unknown command:")) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("qemu does not support closing of file handles: %s"), - reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - - -int qemuMonitorTextAddNetdev(qemuMonitorPtr mon, - const char *netdevstr) -{ - char *cmd; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "netdev_add %s", netdevstr) < 0) - return -1; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* XXX error messages here ? */ - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - - -int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon, - const char *alias) -{ - char *cmd; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "netdev_del %s", alias) < 0) - return -1; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* XXX error messages here ? */ - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - - -/* Parse the output of "info chardev" and return a hash of pty paths. - * - * Output is: - * foo: filename=pty:/dev/pts/7 - * monitor: filename=stdio - * serial0: filename=vc - * parallel0: filename=vc - * - * Non-pty lines are ignored. In the above example, key is 'foo', value is - * '/dev/pty/7'. The hash will contain only a single value. - */ - -int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, - virHashTablePtr info) -{ - char *reply = NULL; - qemuMonitorChardevInfoPtr entry = NULL; - int ret = -1; - - if (qemuMonitorHMPCommand(mon, "info chardev", &reply) < 0) - return -1; - - char *pos; /* The current start of searching */ - char *next = reply; /* The start of the next line */ - char *eol; /* The character which ends the current line */ - char *end = reply + strlen(reply); /* The end of the reply string */ - - while (next) { - pos = next; - - /* Split the output into lines */ - eol = memchr(pos, '\n', end - pos); - if (eol == NULL) { - eol = end; - next = NULL; - } else { - next = eol + 1; - } - - /* Ignore all whitespace immediately before eol */ - while (eol > pos && c_isspace(*(eol-1))) - eol -= 1; - - /* Look for 'filename=pty:' */ -#define NEEDLE "filename=pty:" - char *needle = memmem(pos, eol - pos, NEEDLE, strlen(NEEDLE)); - - /* If it's not there we can ignore this line */ - if (!needle) - continue; - - /* id is everything from the beginning of the line to the ':' - * find ':' and turn it into a terminator */ - char *colon = memchr(pos, ':', needle - pos); - if (colon == NULL) - continue; - *colon = '\0'; - char *id = pos; - - /* Path is everything after needle to the end of the line */ - *eol = '\0'; - - if (VIR_ALLOC(entry) < 0) - goto cleanup; - - if (VIR_STRDUP(entry->ptyPath, needle + strlen(NEEDLE)) < 0) - goto cleanup; - - if (virHashAddEntry(info, id, entry) < 0) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to save chardev path '%s'"), - entry->ptyPath); - VIR_FREE(entry->ptyPath); - goto cleanup; - } - - entry = NULL; -#undef NEEDLE - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(entry); - return ret; -} - - -int qemuMonitorTextDelDevice(qemuMonitorPtr mon, - const char *devalias) -{ - char *cmd = NULL; - char *reply = NULL; - char *safedev; - int ret = -1; - - if (!(safedev = qemuMonitorEscapeArg(devalias))) - goto cleanup; - - if (virAsprintf(&cmd, "device_del %s", safedev) < 0) - goto cleanup; - - VIR_DEBUG("TextDelDevice devalias=%s", devalias); - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (STRNEQ(reply, "")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("detaching %s device failed: %s"), devalias, reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - VIR_FREE(safedev); - return ret; -} - - -int qemuMonitorTextAddDevice(qemuMonitorPtr mon, - const char *devicestr) -{ - char *cmd = NULL; - char *reply = NULL; - char *safedev; - int ret = -1; - - if (!(safedev = qemuMonitorEscapeArg(devicestr))) - goto cleanup; - - if (virAsprintf(&cmd, "device_add %s", safedev) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - /* If the host device is hotpluged first time, qemu will output - * husb: using %s file-system with %s if the command succeeds. - */ - if (STRPREFIX(reply, "husb: using")) { - ret = 0; - goto cleanup; - } - - /* Otherwise, if the command succeeds, no output is sent. So - * any non-empty string shows an error */ - if (STRNEQ(reply, "")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("adding %s device failed: %s"), devicestr, reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - VIR_FREE(safedev); - return ret; -} - - int qemuMonitorTextAddDrive(qemuMonitorPtr mon, const char *drivestr) { @@ -1965,45 +166,6 @@ int qemuMonitorTextDriveDel(qemuMonitorPtr mon, return ret; } -int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon, - const char *alias, - const char *passphrase) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - char *safe_str; - - safe_str = qemuMonitorEscapeArg(passphrase); - if (!safe_str) - return -1; - - if (virAsprintf(&cmd, "block_passwd %s \"%s\"", alias, safe_str) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (strstr(reply, "unknown command:")) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("setting disk password is not supported")); - goto cleanup; - } else if (strstr(reply, "The entered password is invalid")) { - virReportError(VIR_ERR_OPERATION_FAILED, "%s", - _("the disk password is incorrect")); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - VIR_FREE(safe_str); - return ret; -} - - int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name) @@ -2124,277 +286,3 @@ int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name) VIR_FREE(reply); return ret; } - - -int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, - char **reply) -{ - char *safecmd = NULL; - int ret; - - if (!(safecmd = qemuMonitorEscapeArg(cmd))) - return -1; - - ret = qemuMonitorHMPCommand(mon, safecmd, reply); - - VIR_FREE(safecmd); - - return ret; -} - -int qemuMonitorTextInjectNMI(qemuMonitorPtr mon) -{ - char *reply = NULL; - - if (qemuMonitorHMPCommand(mon, "inject-nmi", &reply) < 0) - return -1; - - if (strstr(reply, "unknown command") != NULL) { - VIR_FREE(reply); - - /* fallback to 'nmi' if qemu has not supported "inject-nmi" yet. */ - if (qemuMonitorHMPCommand(mon, "nmi 0", &reply) < 0) - return -1; - } - - VIR_FREE(reply); - return 0; -} - -int qemuMonitorTextSendKey(qemuMonitorPtr mon, - unsigned int holdtime, - unsigned int *keycodes, - unsigned int nkeycodes) -{ - size_t i; - virBuffer buf = VIR_BUFFER_INITIALIZER; - char *cmd, *reply = NULL; - int ret = -1; - - if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS || nkeycodes == 0) - return -1; - - virBufferAddLit(&buf, "sendkey "); - for (i = 0; i < nkeycodes; i++) { - if (keycodes[i] > 0xffff) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("keycode %zu is invalid: 0x%X"), - i, keycodes[i]); - virBufferFreeAndReset(&buf); - return -1; - } - - if (i) - virBufferAddChar(&buf, '-'); - virBufferAsprintf(&buf, "0x%02X", keycodes[i]); - } - - if (holdtime) - virBufferAsprintf(&buf, " %u", holdtime); - - if (virBufferCheckError(&buf) < 0) - return -1; - - cmd = virBufferContentAndReset(&buf); - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (STRNEQ(reply, "")) { - virReportError(VIR_ERR_OPERATION_FAILED, - _("failed to send key '%s'"), reply); - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(reply); - return ret; -} - -/* Returns -1 on error, -2 if not supported */ -int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "screendump %s", file) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (strstr(reply, "unknown command:")) { - ret = -2; - goto cleanup; - } - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - return ret; -} - - -int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, - const char *protocol, - const char *fdname, - bool skipauth) -{ - char *cmd = NULL; - char *reply = NULL; - int ret = -1; - - if (virAsprintf(&cmd, "add_client %s %s %d", protocol, fdname, skipauth ? 0 : 1) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &reply) < 0) - goto cleanup; - - if (STRNEQ(reply, "")) - goto cleanup; - - ret = 0; - - cleanup: - VIR_FREE(reply); - VIR_FREE(cmd); - return ret; -} - - -int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, - const char *device, - virDomainBlockIoTuneInfoPtr info) -{ - char *cmd = NULL; - char *result = NULL; - int ret = -1; - const char *cmd_name = NULL; - - /* For the not specified fields, 0 by default */ - cmd_name = "block_set_io_throttle"; - if (virAsprintf(&cmd, "%s %s %llu %llu %llu %llu %llu %llu", cmd_name, - device, info->total_bytes_sec, info->read_bytes_sec, - info->write_bytes_sec, info->total_iops_sec, - info->read_iops_sec, info->write_iops_sec) < 0) - goto cleanup; - - if (qemuMonitorHMPCommand(mon, cmd, &result) < 0) - goto cleanup; - - if (qemuMonitorTextCommandNotFound(cmd_name, result)) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("Command '%s' is not found"), cmd_name); - goto cleanup; - } - ret = 0; - - cleanup: - VIR_FREE(cmd); - VIR_FREE(result); - return ret; -} - -static int -qemuMonitorTextParseBlockIoThrottle(const char *result, - const char *device, - virDomainBlockIoTuneInfoPtr reply) -{ - char *dummy = NULL; - int ret = -1; - const char *p, *eol; - int devnamelen = strlen(device); - - p = result; - - while (*p) { - if (STREQLEN(p, device, devnamelen) && - p[devnamelen] == ':' && p[devnamelen+1] == ' ') { - - eol = strchr(p, '\n'); - if (!eol) - eol = p + strlen(p); - - p += devnamelen + 2; /* Skip to first label. */ - - while (*p) { - if (STRPREFIX(p, "bps=")) { - p += strlen("bps="); - if (virStrToLong_ull(p, &dummy, 10, &reply->total_bytes_sec) == -1) - VIR_DEBUG("error reading total_bytes_sec: %s", p); - } else if (STRPREFIX(p, "bps_rd=")) { - p += strlen("bps_rd="); - if (virStrToLong_ull(p, &dummy, 10, &reply->read_bytes_sec) == -1) - VIR_DEBUG("error reading read_bytes_sec: %s", p); - } else if (STRPREFIX(p, "bps_wr=")) { - p += strlen("bps_wr="); - if (virStrToLong_ull(p, &dummy, 10, &reply->write_bytes_sec) == -1) - VIR_DEBUG("error reading write_bytes_sec: %s", p); - } else if (STRPREFIX(p, "iops=")) { - p += strlen("iops="); - if (virStrToLong_ull(p, &dummy, 10, &reply->total_iops_sec) == -1) - VIR_DEBUG("error reading total_iops_sec: %s", p); - } else if (STRPREFIX(p, "iops_rd=")) { - p += strlen("iops_rd="); - if (virStrToLong_ull(p, &dummy, 10, &reply->read_iops_sec) == -1) - VIR_DEBUG("error reading read_iops_sec: %s", p); - } else if (STRPREFIX(p, "iops_wr=")) { - p += strlen("iops_wr="); - if (virStrToLong_ull(p, &dummy, 10, &reply->write_iops_sec) == -1) - VIR_DEBUG("error reading write_iops_sec: %s", p); - } else { - VIR_DEBUG(" unknown block info %s", p); - } - - /* Skip to next label. */ - p = strchr(p, ' '); - if (!p || p >= eol) - break; - p++; - } - ret = 0; - goto cleanup; - } - - /* Skip to next line. */ - p = strchr(p, '\n'); - if (!p) - break; - p++; - } - - virReportError(VIR_ERR_INVALID_ARG, - _("No info for device '%s'"), device); - - cleanup: - return ret; -} - -int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon, - const char *device, - virDomainBlockIoTuneInfoPtr reply) -{ - char *result = NULL; - int ret = -1; - const char *cmd_name = "info block"; - - if (qemuMonitorHMPCommand(mon, cmd_name, &result) < 0) - goto cleanup; - - if (qemuMonitorTextCommandNotFound(cmd_name, result)) { - virReportError(VIR_ERR_OPERATION_INVALID, - _("Command '%s' is not found"), cmd_name); - goto cleanup; - } - - ret = qemuMonitorTextParseBlockIoThrottle(result, device, reply); - - cleanup: - VIR_FREE(result); - return ret; -} diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h index a9cdce5f61..5cb5dba441 100644 --- a/src/qemu/qemu_monitor_text.h +++ b/src/qemu/qemu_monitor_text.h @@ -29,159 +29,16 @@ # include "qemu_monitor.h" -int qemuMonitorTextIOProcess(qemuMonitorPtr mon, - const char *data, - size_t len, - qemuMonitorMessagePtr msg); - -int qemuMonitorTextCommandWithFd(qemuMonitorPtr mon, - const char *cmd, - int scm_fd, - char **reply); - -int qemuMonitorTextStartCPUs(qemuMonitorPtr mon); -int qemuMonitorTextStopCPUs(qemuMonitorPtr mon); -int qemuMonitorTextGetStatus(qemuMonitorPtr mon, - bool *running, - virDomainPausedReason *reason); - -int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon); -int qemuMonitorTextSystemReset(qemuMonitorPtr mon); - -int qemuMonitorTextQueryCPUs(qemuMonitorPtr mon, - struct qemuMonitorQueryCpusEntry **entries, - size_t *nentries); -int qemuMonitorTextGetVirtType(qemuMonitorPtr mon, - virDomainVirtType *virtType); -int qemuMonitorTextGetBalloonInfo(qemuMonitorPtr mon, - unsigned long long *currmem); -int qemuMonitorTextGetMemoryStats(qemuMonitorPtr mon, - virDomainMemoryStatPtr stats, - unsigned int nr_stats); -int qemuMonitorTextGetBlockInfo(qemuMonitorPtr mon, - virHashTablePtr table); - -int qemuMonitorTextGetAllBlockStatsInfo(qemuMonitorPtr mon, - virHashTablePtr hash); -int qemuMonitorTextBlockResize(qemuMonitorPtr mon, - const char *device, - unsigned long long size); -int qemuMonitorTextSetVNCPassword(qemuMonitorPtr mon, - const char *password); -int qemuMonitorTextSetPassword(qemuMonitorPtr mon, - const char *protocol, - const char *password, - const char *action_if_connected); -int qemuMonitorTextExpirePassword(qemuMonitorPtr mon, - const char *protocol, - const char *expire_time); -int qemuMonitorTextSetBalloon(qemuMonitorPtr mon, - unsigned long long newmem); int qemuMonitorTextSetCPU(qemuMonitorPtr mon, int cpu, bool online); -int qemuMonitorTextEjectMedia(qemuMonitorPtr mon, - const char *dev_name, - bool force); -int qemuMonitorTextChangeMedia(qemuMonitorPtr mon, - const char *dev_name, - const char *newmedia, - const char *format); - - -int qemuMonitorTextSaveVirtualMemory(qemuMonitorPtr mon, - unsigned long long offset, - size_t length, - const char *path); -int qemuMonitorTextSavePhysicalMemory(qemuMonitorPtr mon, - unsigned long long offset, - size_t length, - const char *path); - -int qemuMonitorTextSetMigrationSpeed(qemuMonitorPtr mon, - unsigned long bandwidth); - -int qemuMonitorTextSetMigrationDowntime(qemuMonitorPtr mon, - unsigned long long downtime); - -int qemuMonitorTextGetMigrationStats(qemuMonitorPtr mon, - qemuMonitorMigrationStatsPtr stats); - -int qemuMonitorTextMigrate(qemuMonitorPtr mon, - unsigned int flags, - const char *uri); - -int qemuMonitorTextMigrateCancel(qemuMonitorPtr mon); - -int qemuMonitorTextGraphicsRelocate(qemuMonitorPtr mon, - int type, - const char *hostname, - int port, - int tlsPort, - const char *tlsSubject); - -int qemuMonitorTextSendFileHandle(qemuMonitorPtr mon, - const char *fdname, - int fd); - -int qemuMonitorTextCloseFileHandle(qemuMonitorPtr mon, - const char *fdname); - -int qemuMonitorTextAddNetdev(qemuMonitorPtr mon, - const char *netdevstr); - -int qemuMonitorTextRemoveNetdev(qemuMonitorPtr mon, - const char *alias); - -int qemuMonitorTextGetChardevInfo(qemuMonitorPtr mon, - virHashTablePtr info); - -int qemuMonitorTextAddDevice(qemuMonitorPtr mon, - const char *devicestr); - -int qemuMonitorTextDelDevice(qemuMonitorPtr mon, - const char *devalias); - int qemuMonitorTextAddDrive(qemuMonitorPtr mon, const char *drivestr); int qemuMonitorTextDriveDel(qemuMonitorPtr mon, const char *drivestr); -int qemuMonitorTextSetDrivePassphrase(qemuMonitorPtr mon, - const char *alias, - const char *passphrase); - int qemuMonitorTextCreateSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextLoadSnapshot(qemuMonitorPtr mon, const char *name); int qemuMonitorTextDeleteSnapshot(qemuMonitorPtr mon, const char *name); -int qemuMonitorTextArbitraryCommand(qemuMonitorPtr mon, const char *cmd, - char **reply); - -int qemuMonitorTextInjectNMI(qemuMonitorPtr mon); - -int qemuMonitorTextSendKey(qemuMonitorPtr mon, - unsigned int holdtime, - unsigned int *keycodes, - unsigned int nkeycodes); - -int qemuMonitorTextScreendump(qemuMonitorPtr mon, const char *file); - -int qemuMonitorTextSetLink(qemuMonitorPtr mon, - const char *name, - virDomainNetInterfaceLinkState state); - -int qemuMonitorTextOpenGraphics(qemuMonitorPtr mon, - const char *protocol, - const char *fdname, - bool skipauth); - -int qemuMonitorTextSetBlockIoThrottle(qemuMonitorPtr mon, - const char *device, - virDomainBlockIoTuneInfoPtr info); - -int qemuMonitorTextGetBlockIoThrottle(qemuMonitorPtr mon, - const char *device, - virDomainBlockIoTuneInfoPtr reply); - #endif /* QEMU_MONITOR_TEXT_H */