/* * qemu_monitor_json.c: interaction with QEMU monitor console * * Copyright (C) 2006-2016 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, see * . */ #include #include #include #include #include #include #include #include "qemu_monitor_json.h" #include "qemu_alias.h" #include "qemu_capabilities.h" #include "viralloc.h" #include "virlog.h" #include "driver.h" #include "datatypes.h" #include "virerror.h" #include "virjson.h" #include "virprobe.h" #include "virstring.h" #include "cpu/cpu_x86.h" #include "virenum.h" #ifdef WITH_DTRACE_PROBES # include "libvirt_qemu_probes.h" #endif #define VIR_FROM_THIS VIR_FROM_QEMU VIR_LOG_INIT("qemu.qemu_monitor_json"); #define QOM_CPU_PATH "/machine/unattached/device[0]" #define LINE_ENDING "\r\n" VIR_ENUM_IMPL(qemuMonitorJob, QEMU_MONITOR_JOB_TYPE_LAST, "", "commit", "stream", "mirror", "backup", "create"); VIR_ENUM_IMPL(qemuMonitorJobStatus, QEMU_MONITOR_JOB_STATUS_LAST, "", "created", "running", "paused", "ready", "standby", "waiting", "pending", "aborting", "concluded", "undefined", "null"); static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleJobStatusChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleSerialChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleMigrationStatus(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleMigrationPass(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleDumpCompleted(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data); } qemuEventHandler; static qemuEventHandler eventHandlers[] = { { "ACPI_DEVICE_OST", qemuMonitorJSONHandleAcpiOstInfo, }, { "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, }, { "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, }, { "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, }, { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, }, { "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, }, { "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold, }, { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, { "DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, { "JOB_STATUS_CHANGE", qemuMonitorJSONHandleJobStatusChange, }, { "MIGRATION", qemuMonitorJSONHandleMigrationStatus, }, { "MIGRATION_PASS", qemuMonitorJSONHandleMigrationPass, }, { "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "PR_MANAGER_STATUS_CHANGED", qemuMonitorJSONHandlePRManagerStatusChanged, }, { "RDMA_GID_STATUS_CHANGED", qemuMonitorJSONHandleRdmaGidStatusChanged, }, { "RESET", qemuMonitorJSONHandleReset, }, { "RESUME", qemuMonitorJSONHandleResume, }, { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, { "SHUTDOWN", qemuMonitorJSONHandleShutdown, }, { "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, }, { "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, }, { "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, }, { "SPICE_MIGRATE_COMPLETED", qemuMonitorJSONHandleSpiceMigrated, }, { "STOP", qemuMonitorJSONHandleStop, }, { "SUSPEND", qemuMonitorJSONHandlePMSuspend, }, { "SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk, }, { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, { "VSERPORT_CHANGE", qemuMonitorJSONHandleSerialChange, }, { "WAKEUP", qemuMonitorJSONHandlePMWakeup, }, { "WATCHDOG", qemuMonitorJSONHandleWatchdog, }, /* We use bsearch, so keep this list sorted. */ }; static int qemuMonitorEventCompare(const void *key, const void *elt) { const char *type = key; const qemuEventHandler *handler = elt; return strcmp(type, handler->type); } static int qemuMonitorJSONIOProcessEvent(qemuMonitorPtr mon, virJSONValuePtr obj) { const char *type; qemuEventHandler *handler; virJSONValuePtr data; char *details = NULL; virJSONValuePtr timestamp; long long seconds = -1; unsigned int micros = 0; VIR_DEBUG("mon=%p obj=%p", mon, obj); type = virJSONValueObjectGetString(obj, "event"); if (!type) { VIR_WARN("missing event type in message"); errno = EINVAL; return -1; } /* Not all events have data; and event reporting is best-effort only */ if ((data = virJSONValueObjectGet(obj, "data"))) details = virJSONValueToString(data, false); if ((timestamp = virJSONValueObjectGet(obj, "timestamp"))) { ignore_value(virJSONValueObjectGetNumberLong(timestamp, "seconds", &seconds)); ignore_value(virJSONValueObjectGetNumberUint(timestamp, "microseconds", µs)); } qemuMonitorEmitEvent(mon, type, seconds, micros, details); VIR_FREE(details); handler = bsearch(type, eventHandlers, G_N_ELEMENTS(eventHandlers), sizeof(eventHandlers[0]), qemuMonitorEventCompare); if (handler) { VIR_DEBUG("handle %s handler=%p data=%p", type, handler->handler, data); (handler->handler)(mon, data); } return 0; } int qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon, const char *line, qemuMonitorMessagePtr msg) { virJSONValuePtr obj = NULL; int ret = -1; VIR_DEBUG("Line [%s]", line); if (!(obj = virJSONValueFromString(line))) goto cleanup; if (virJSONValueGetType(obj) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Parsed JSON reply '%s' isn't an object"), line); goto cleanup; } if (virJSONValueObjectHasKey(obj, "QMP") == 1) { ret = 0; } else if (virJSONValueObjectHasKey(obj, "event") == 1) { PROBE(QEMU_MONITOR_RECV_EVENT, "mon=%p event=%s", mon, line); ret = qemuMonitorJSONIOProcessEvent(mon, obj); } else if (virJSONValueObjectHasKey(obj, "error") == 1 || virJSONValueObjectHasKey(obj, "return") == 1) { PROBE(QEMU_MONITOR_RECV_REPLY, "mon=%p reply=%s", mon, line); if (msg) { msg->rxObject = obj; msg->finished = 1; obj = NULL; ret = 0; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unexpected JSON reply '%s'"), line); } } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unknown JSON reply '%s'"), line); } cleanup: virJSONValueFree(obj); return ret; } int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, const char *data, size_t len, qemuMonitorMessagePtr msg) { int used = 0; /*VIR_DEBUG("Data %d bytes [%s]", len, data);*/ while (used < len) { char *nl = strstr(data + used, LINE_ENDING); if (nl) { int got = nl - (data + used); char *line; if (VIR_STRNDUP(line, data + used, got) < 0) return -1; used += got + strlen(LINE_ENDING); line[got] = '\0'; /* kill \n */ if (qemuMonitorJSONIOProcessLine(mon, line, msg) < 0) { VIR_FREE(line); return -1; } VIR_FREE(line); } else { break; } } #if DEBUG_IO VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len); #endif return used; } static int qemuMonitorJSONCommandWithFd(qemuMonitorPtr mon, virJSONValuePtr cmd, int scm_fd, virJSONValuePtr *reply) { int ret = -1; qemuMonitorMessage msg; g_auto(virBuffer) cmdbuf = VIR_BUFFER_INITIALIZER; char *id = NULL; *reply = NULL; memset(&msg, 0, sizeof(msg)); if (virJSONValueObjectHasKey(cmd, "execute") == 1) { if (!(id = qemuMonitorNextCommandID(mon))) goto cleanup; if (virJSONValueObjectAppendString(cmd, "id", id) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to append command 'id' string")); goto cleanup; } } if (virJSONValueToBuffer(cmd, &cmdbuf, false) < 0) goto cleanup; virBufferAddLit(&cmdbuf, "\r\n"); msg.txLength = virBufferUse(&cmdbuf); msg.txBuffer = virBufferContentAndReset(&cmdbuf); msg.txFD = scm_fd; ret = qemuMonitorSend(mon, &msg); if (ret == 0) { if (!msg.rxObject) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing monitor reply object")); ret = -1; } else { *reply = msg.rxObject; } } cleanup: VIR_FREE(id); VIR_FREE(msg.txBuffer); return ret; } static int qemuMonitorJSONCommand(qemuMonitorPtr mon, virJSONValuePtr cmd, virJSONValuePtr *reply) { return qemuMonitorJSONCommandWithFd(mon, cmd, -1, reply); } /* Ignoring OOM in this method, since we're already reporting * a more important error * * XXX see qerror.h for different klasses & fill out useful params */ static const char * qemuMonitorJSONStringifyError(virJSONValuePtr error) { const char *klass = virJSONValueObjectGetString(error, "class"); const char *detail = NULL; /* The QMP 'desc' field is usually sufficient for our generic * error reporting needs. */ if (klass) detail = virJSONValueObjectGetString(error, "desc"); if (!detail) detail = "unknown QEMU command error"; return detail; } static const char * qemuMonitorJSONCommandName(virJSONValuePtr cmd) { const char *name = virJSONValueObjectGetString(cmd, "execute"); if (name) return name; else return ""; } static int qemuMonitorJSONCheckError(virJSONValuePtr cmd, virJSONValuePtr reply) { if (virJSONValueObjectHasKey(reply, "error")) { virJSONValuePtr error = virJSONValueObjectGet(reply, "error"); char *cmdstr = virJSONValueToString(cmd, false); char *replystr = virJSONValueToString(reply, false); /* Log the full JSON formatted command & error */ VIR_DEBUG("unable to execute QEMU command %s: %s", NULLSTR(cmdstr), NULLSTR(replystr)); /* Only send the user the command name + friendly error */ if (!error) virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to execute QEMU command '%s'"), qemuMonitorJSONCommandName(cmd)); else virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to execute QEMU command '%s': %s"), qemuMonitorJSONCommandName(cmd), qemuMonitorJSONStringifyError(error)); VIR_FREE(cmdstr); VIR_FREE(replystr); return -1; } else if (!virJSONValueObjectHasKey(reply, "return")) { char *cmdstr = virJSONValueToString(cmd, false); char *replystr = virJSONValueToString(reply, false); VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s", NULLSTR(cmdstr), NULLSTR(replystr)); virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to execute QEMU command '%s'"), qemuMonitorJSONCommandName(cmd)); VIR_FREE(cmdstr); VIR_FREE(replystr); return -1; } return 0; } static int qemuMonitorJSONCheckReply(virJSONValuePtr cmd, virJSONValuePtr reply, virJSONType type) { virJSONValuePtr data; if (qemuMonitorJSONCheckError(cmd, reply) < 0) return -1; data = virJSONValueObjectGet(reply, "return"); if (virJSONValueGetType(data) != type) { char *cmdstr = virJSONValueToString(cmd, false); char *retstr = virJSONValueToString(data, false); VIR_DEBUG("Unexpected return type %d (expecting %d) for command %s: %s", virJSONValueGetType(data), type, cmdstr, retstr); virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected type returned by QEMU command '%s'"), qemuMonitorJSONCommandName(cmd)); VIR_FREE(cmdstr); VIR_FREE(retstr); return -1; } return 0; } static bool qemuMonitorJSONErrorIsClass(virJSONValuePtr error, const char *klass) { return STREQ_NULLABLE(virJSONValueObjectGetString(error, "class"), klass); } static bool qemuMonitorJSONHasError(virJSONValuePtr reply, const char *klass) { virJSONValuePtr error; if (!(error = virJSONValueObjectGet(reply, "error"))) return false; return qemuMonitorJSONErrorIsClass(error, klass); } /** * qemuMonitorJSONTransactionAdd: * @actions: array of actions for the 'transaction' command * @cmdname: command to add to @actions * @...: arguments for @cmdname (see virJSONValueObjectAddVArgs for formatting) * * Add a new command with arguments to the existing ones. The resulting array * is intended to be used as argument for the 'transaction' command. * * Returns 0 on success and -1 on error. */ static int qemuMonitorJSONTransactionAdd(virJSONValuePtr actions, const char *cmdname, ...) { virJSONValuePtr entry = NULL; virJSONValuePtr data = NULL; va_list args; int ret = -1; va_start(args, cmdname); if (virJSONValueObjectCreateVArgs(&data, args) < 0) goto cleanup; if (virJSONValueObjectCreate(&entry, "s:type", cmdname, "A:data", &data, NULL) < 0) goto cleanup; if (virJSONValueArrayAppend(actions, entry) < 0) goto cleanup; entry = NULL; ret = 0; cleanup: virJSONValueFree(entry); virJSONValueFree(data); va_end(args); return ret; } /** * qemuMonitorJSONMakeCommandInternal: * @cmdname: QMP command name * @arguments: a JSON object containing command arguments or NULL * * Create a JSON object used on the QMP monitor to call a command. * * Note that @arguments is always consumed and should not be referenced after * the call to this function. */ static virJSONValuePtr qemuMonitorJSONMakeCommandInternal(const char *cmdname, virJSONValuePtr arguments) { virJSONValuePtr ret = NULL; ignore_value(virJSONValueObjectCreate(&ret, "s:execute", cmdname, "A:arguments", &arguments, NULL)); virJSONValueFree(arguments); return ret; } static virJSONValuePtr G_GNUC_NULL_TERMINATED qemuMonitorJSONMakeCommand(const char *cmdname, ...) { virJSONValuePtr obj = NULL; virJSONValuePtr jargs = NULL; va_list args; va_start(args, cmdname); if (virJSONValueObjectCreateVArgs(&jargs, args) < 0) goto cleanup; obj = qemuMonitorJSONMakeCommandInternal(cmdname, jargs); cleanup: va_end(args); return obj; } static void qemuMonitorJSONParseKeywordsFree(int nkeywords, char **keywords, char **values) { size_t i; for (i = 0; i < nkeywords; i++) { VIR_FREE(keywords[i]); VIR_FREE(values[i]); } VIR_FREE(keywords); VIR_FREE(values); } /* * Takes a string containing a set of key=value,key=value,key... * parameters and splits them up, returning two arrays with * the individual keys and values. If allowEmptyValue is nonzero, * the "=value" part is optional and if a key with no value is found, * NULL is be placed into corresponding place in retvalues. */ static int qemuMonitorJSONParseKeywords(const char *str, char ***retkeywords, char ***retvalues, int *retnkeywords, int allowEmptyValue) { int keywordCount = 0; int keywordAlloc = 0; char **keywords = NULL; char **values = NULL; const char *start = str; const char *end; *retkeywords = NULL; *retvalues = NULL; *retnkeywords = 0; end = start + strlen(str); while (start) { const char *separator; const char *endmark; char *keyword; char *value = NULL; endmark = start; do { /* QEMU accepts ',,' as an escape for a literal comma; * skip past those here while searching for the end of the * value, then strip them down below */ endmark = strchr(endmark, ','); } while (endmark && endmark[1] == ',' && (endmark += 2)); if (!endmark) endmark = end; if (!(separator = strchr(start, '='))) separator = end; if (separator >= endmark) { if (!allowEmptyValue) { virReportError(VIR_ERR_INTERNAL_ERROR, _("malformed keyword arguments in '%s'"), str); goto error; } separator = endmark; } keyword = g_strndup(start, separator - start); if (separator < endmark) { separator++; value = g_strndup(separator, endmark - separator); if (strchr(value, ',')) { char *p = strchr(value, ',') + 1; char *q = p + 1; while (*q) { if (*q == ',') q++; *p++ = *q++; } *p = '\0'; } } if (keywordAlloc == keywordCount) { if (VIR_REALLOC_N(keywords, keywordAlloc + 10) < 0 || VIR_REALLOC_N(values, keywordAlloc + 10) < 0) { VIR_FREE(keyword); VIR_FREE(value); goto error; } keywordAlloc += 10; } keywords[keywordCount] = keyword; values[keywordCount] = value; keywordCount++; start = endmark < end ? endmark + 1 : NULL; } *retkeywords = keywords; *retvalues = values; *retnkeywords = keywordCount; return 0; error: qemuMonitorJSONParseKeywordsFree(keywordCount, keywords, values); return -1; } static virJSONValuePtr qemuMonitorJSONKeywordStringToJSON(const char *str, const char *firstkeyword) { virJSONValuePtr ret = NULL; char **keywords = NULL; char **values = NULL; int nkeywords = 0; size_t i; if (!(ret = virJSONValueNewObject())) return NULL; if (qemuMonitorJSONParseKeywords(str, &keywords, &values, &nkeywords, 1) < 0) goto error; for (i = 0; i < nkeywords; i++) { if (values[i] == NULL) { if (i != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected empty keyword in %s"), str); goto error; } else { /* This 3rd arg isn't a typo - the way the parser works is * that the value ended up in the keyword field */ if (virJSONValueObjectAppendString(ret, firstkeyword, keywords[i]) < 0) goto error; } } else { if (virJSONValueObjectAppendString(ret, keywords[i], values[i]) < 0) goto error; } } qemuMonitorJSONParseKeywordsFree(nkeywords, keywords, values); return ret; error: qemuMonitorJSONParseKeywordsFree(nkeywords, keywords, values); virJSONValueFree(ret); return NULL; } static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data) { bool guest = false; virTristateBool guest_initiated = VIR_TRISTATE_BOOL_ABSENT; if (data && virJSONValueObjectGetBoolean(data, "guest", &guest) == 0) guest_initiated = guest ? VIR_TRISTATE_BOOL_YES : VIR_TRISTATE_BOOL_NO; qemuMonitorEmitShutdown(mon, guest_initiated); } static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitReset(mon); } static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitPowerdown(mon); } static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitStop(mon); } static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitResume(mon); } static qemuMonitorEventPanicInfoPtr qemuMonitorJSONGuestPanicExtractInfoHyperv(virJSONValuePtr data) { qemuMonitorEventPanicInfoPtr ret; if (VIR_ALLOC(ret) < 0) return NULL; ret->type = QEMU_MONITOR_EVENT_PANIC_INFO_TYPE_HYPERV; if (virJSONValueObjectGetNumberUlong(data, "arg1", &ret->data.hyperv.arg1) < 0 || virJSONValueObjectGetNumberUlong(data, "arg2", &ret->data.hyperv.arg2) < 0 || virJSONValueObjectGetNumberUlong(data, "arg3", &ret->data.hyperv.arg3) < 0 || virJSONValueObjectGetNumberUlong(data, "arg4", &ret->data.hyperv.arg4) < 0 || virJSONValueObjectGetNumberUlong(data, "arg5", &ret->data.hyperv.arg5) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed hyperv panic data")); goto error; } return ret; error: qemuMonitorEventPanicInfoFree(ret); return NULL; } static qemuMonitorEventPanicInfoPtr qemuMonitorJSONGuestPanicExtractInfoS390(virJSONValuePtr data) { qemuMonitorEventPanicInfoPtr ret; int core; unsigned long long psw_mask, psw_addr; const char *reason = NULL; if (VIR_ALLOC(ret) < 0) return NULL; ret->type = QEMU_MONITOR_EVENT_PANIC_INFO_TYPE_S390; if (virJSONValueObjectGetNumberInt(data, "core", &core) < 0 || virJSONValueObjectGetNumberUlong(data, "psw-mask", &psw_mask) < 0 || virJSONValueObjectGetNumberUlong(data, "psw-addr", &psw_addr) < 0 || !(reason = virJSONValueObjectGetString(data, "reason"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed s390 panic data")); goto error; } ret->data.s390.core = core; ret->data.s390.psw_mask = psw_mask; ret->data.s390.psw_addr = psw_addr; ret->data.s390.reason = g_strdup(reason); return ret; error: qemuMonitorEventPanicInfoFree(ret); return NULL; } static qemuMonitorEventPanicInfoPtr qemuMonitorJSONGuestPanicExtractInfo(virJSONValuePtr data) { const char *type = virJSONValueObjectGetString(data, "type"); if (STREQ_NULLABLE(type, "hyper-v")) return qemuMonitorJSONGuestPanicExtractInfoHyperv(data); else if (STREQ_NULLABLE(type, "s390")) return qemuMonitorJSONGuestPanicExtractInfoS390(data); virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown panic info type '%s'"), NULLSTR(type)); return NULL; } static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data) { virJSONValuePtr infojson = virJSONValueObjectGetObject(data, "info"); qemuMonitorEventPanicInfoPtr info = NULL; if (infojson) info = qemuMonitorJSONGuestPanicExtractInfo(infojson); qemuMonitorEmitGuestPanic(mon, info); } static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data) { long long offset = 0; if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) { VIR_WARN("missing offset in RTC change event"); offset = 0; } qemuMonitorEmitRTCChange(mon, offset); } VIR_ENUM_DECL(qemuMonitorWatchdogAction); VIR_ENUM_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_LAST, "none", "pause", "reset", "poweroff", "shutdown", "debug", "inject-nmi", ); static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data) { const char *action; int actionID; if (!(action = virJSONValueObjectGetString(data, "action"))) VIR_WARN("missing action in watchdog event"); if (action) { if ((actionID = qemuMonitorWatchdogActionTypeFromString(action)) < 0) { VIR_WARN("unknown action %s in watchdog event", action); actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE; } } else { actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE; } qemuMonitorEmitWatchdog(mon, actionID); } VIR_ENUM_DECL(qemuMonitorIOErrorAction); VIR_ENUM_IMPL(qemuMonitorIOErrorAction, VIR_DOMAIN_EVENT_IO_ERROR_LAST, "ignore", "stop", "report", ); static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data) { const char *device; const char *nodename; const char *action; const char *reason = ""; bool nospc = false; int actionID; /* Throughout here we try our best to carry on upon errors, since it's important to get as much info as possible out to the application */ if ((action = virJSONValueObjectGetString(data, "action")) == NULL) { VIR_WARN("Missing action in disk io error event"); action = "ignore"; } if ((device = virJSONValueObjectGetString(data, "device")) == NULL) VIR_WARN("missing device in disk io error event"); nodename = virJSONValueObjectGetString(data, "node-name"); if (virJSONValueObjectGetBoolean(data, "nospace", &nospc) == 0 && nospc) reason = "enospc"; if ((actionID = qemuMonitorIOErrorActionTypeFromString(action)) < 0) { VIR_WARN("unknown disk io error action '%s'", action); actionID = VIR_DOMAIN_EVENT_IO_ERROR_NONE; } qemuMonitorEmitIOError(mon, device, nodename, actionID, reason); } VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily); VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST, "ipv4", "ipv6", "unix", ); static void qemuMonitorJSONHandleGraphicsVNC(qemuMonitorPtr mon, virJSONValuePtr data, int phase) { const char *localNode, *localService, *localFamily; const char *remoteNode, *remoteService, *remoteFamily; const char *authScheme, *saslUsername, *x509dname; int localFamilyID, remoteFamilyID; virJSONValuePtr client; virJSONValuePtr server; if (!(client = virJSONValueObjectGetObject(data, "client"))) { VIR_WARN("missing client info in VNC event"); return; } if (!(server = virJSONValueObjectGetObject(data, "server"))) { VIR_WARN("missing server info in VNC event"); return; } if (!(authScheme = virJSONValueObjectGetString(server, "auth"))) { /* not all events are required to contain auth scheme */ VIR_DEBUG("missing auth scheme in VNC event"); authScheme = ""; } if (!(localFamily = virJSONValueObjectGetString(server, "family"))) { VIR_WARN("missing local address family in VNC event"); return; } if (!(localNode = virJSONValueObjectGetString(server, "host"))) { VIR_WARN("missing local hostname in VNC event"); return; } if (!(localService = virJSONValueObjectGetString(server, "service"))) { VIR_WARN("missing local service in VNC event"); return; } if (!(remoteFamily = virJSONValueObjectGetString(client, "family"))) { VIR_WARN("missing remote address family in VNC event"); return; } if (!(remoteNode = virJSONValueObjectGetString(client, "host"))) { VIR_WARN("missing remote hostname in VNC event"); return; } if (!(remoteService = virJSONValueObjectGetString(client, "service"))) { VIR_WARN("missing remote service in VNC event"); return; } saslUsername = virJSONValueObjectGetString(client, "sasl_username"); x509dname = virJSONValueObjectGetString(client, "x509_dname"); if ((localFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(localFamily)) < 0) { VIR_WARN("unknown address family '%s'", localFamily); localFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; } if ((remoteFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(remoteFamily)) < 0) { VIR_WARN("unknown address family '%s'", remoteFamily); remoteFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; } qemuMonitorEmitGraphics(mon, phase, localFamilyID, localNode, localService, remoteFamilyID, remoteNode, remoteService, authScheme, x509dname, saslUsername); } static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT); } static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE); } static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT); } static void qemuMonitorJSONHandleGraphicsSPICE(qemuMonitorPtr mon, virJSONValuePtr data, int phase) { const char *lhost, *lport, *lfamily; const char *rhost, *rport, *rfamily; const char *auth = ""; int lfamilyID, rfamilyID; virJSONValuePtr client; virJSONValuePtr server; if (!(client = virJSONValueObjectGetObject(data, "client")) || !(server = virJSONValueObjectGetObject(data, "server"))) { VIR_WARN("missing server or client info in SPICE event"); return; } if (phase == VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE && !(auth = virJSONValueObjectGetString(server, "auth"))) { VIR_DEBUG("missing auth scheme in SPICE event"); auth = ""; } if (!(lfamily = virJSONValueObjectGetString(server, "family"))) { VIR_WARN("missing local address family in SPICE event"); return; } if (!(lhost = virJSONValueObjectGetString(server, "host"))) { VIR_WARN("missing local hostname in SPICE event"); return; } if (!(lport = virJSONValueObjectGetString(server, "port"))) { VIR_WARN("missing local port in SPICE event"); return; } if (!(rfamily = virJSONValueObjectGetString(client, "family"))) { VIR_WARN("missing remote address family in SPICE event"); return; } if (!(rhost = virJSONValueObjectGetString(client, "host"))) { VIR_WARN("missing remote hostname in SPICE event"); return; } if (!(rport = virJSONValueObjectGetString(client, "port"))) { VIR_WARN("missing remote service in SPICE event"); return; } if ((lfamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(lfamily)) < 0) { VIR_WARN("unknown address family '%s'", lfamily); lfamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; } if ((rfamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(rfamily)) < 0) { VIR_WARN("unknown address family '%s'", rfamily); rfamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; } qemuMonitorEmitGraphics(mon, phase, lfamilyID, lhost, lport, rfamilyID, rhost, rport, auth, NULL, NULL); } static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT); } static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE); } static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleGraphicsSPICE(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT); } static void qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon, virJSONValuePtr data, int event) { const char *device; const char *type_str; const char *error = NULL; int type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; unsigned long long offset, len; if ((device = virJSONValueObjectGetString(data, "device")) == NULL) { VIR_WARN("missing device in block job event"); goto out; } if (virJSONValueObjectGetNumberUlong(data, "offset", &offset) < 0) { VIR_WARN("missing offset in block job event"); goto out; } if (virJSONValueObjectGetNumberUlong(data, "len", &len) < 0) { VIR_WARN("missing len in block job event"); goto out; } if ((type_str = virJSONValueObjectGetString(data, "type")) == NULL) { VIR_WARN("missing type in block job event"); goto out; } if (STREQ(type_str, "stream")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; else if (STREQ(type_str, "commit")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT; else if (STREQ(type_str, "mirror")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; else if (STREQ(type_str, "backup")) type = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP; switch ((virConnectDomainEventBlockJobStatus) event) { case VIR_DOMAIN_BLOCK_JOB_COMPLETED: error = virJSONValueObjectGetString(data, "error"); /* Make sure the whole device has been processed */ if (offset != len || error) event = VIR_DOMAIN_BLOCK_JOB_FAILED; break; case VIR_DOMAIN_BLOCK_JOB_CANCELED: case VIR_DOMAIN_BLOCK_JOB_READY: break; case VIR_DOMAIN_BLOCK_JOB_FAILED: case VIR_DOMAIN_BLOCK_JOB_LAST: VIR_DEBUG("should not get here"); break; } out: qemuMonitorEmitBlockJob(mon, device, type, event, error); } static void qemuMonitorJSONHandleJobStatusChange(qemuMonitorPtr mon, virJSONValuePtr data) { const char *jobname = virJSONValueObjectGetString(data, "id"); const char *statusstr = virJSONValueObjectGetString(data, "status"); int status; if (!jobname) { VIR_WARN("missing job name in JOB_STATUS_CHANGE event"); return; } if ((status = qemuMonitorJobStatusTypeFromString(statusstr)) < 0) { VIR_WARN("unknown job status '%s' for job '%s' in JOB_STATUS_CHANGE event", statusstr, jobname); return; } qemuMonitorEmitJobStatusChange(mon, jobname, status); } static void qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon, virJSONValuePtr data) { const char *devAlias = virJSONValueObjectGetString(data, "device"); const char *devid = virJSONValueObjectGetString(data, "id"); bool trayOpened; int reason; /* drive alias is always reported but empty for -blockdev */ if (*devAlias == '\0') devAlias = NULL; if (!devAlias && !devid) { VIR_WARN("missing device in tray change event"); return; } if (virJSONValueObjectGetBoolean(data, "tray-open", &trayOpened) < 0) { VIR_WARN("missing tray-open in tray change event"); return; } if (trayOpened) reason = VIR_DOMAIN_EVENT_TRAY_CHANGE_OPEN; else reason = VIR_DOMAIN_EVENT_TRAY_CHANGE_CLOSE; qemuMonitorEmitTrayChange(mon, devAlias, devid, reason); } static void qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitPMWakeup(mon); } static void qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitPMSuspend(mon); } static void qemuMonitorJSONHandleBlockJobCompleted(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleBlockJobImpl(mon, data, VIR_DOMAIN_BLOCK_JOB_COMPLETED); } static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleBlockJobImpl(mon, data, VIR_DOMAIN_BLOCK_JOB_CANCELED); } static void qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon, virJSONValuePtr data) { qemuMonitorJSONHandleBlockJobImpl(mon, data, VIR_DOMAIN_BLOCK_JOB_READY); } static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data) { unsigned long long actual = 0; if (virJSONValueObjectGetNumberUlong(data, "actual", &actual) < 0) { VIR_WARN("missing actual in balloon change event"); return; } actual = VIR_DIV_UP(actual, 1024); qemuMonitorEmitBalloonChange(mon, actual); } static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitPMSuspendDisk(mon); } static void qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data) { const char *device; if (!(device = virJSONValueObjectGetString(data, "device"))) { VIR_DEBUG("missing device in device deleted event"); return; } qemuMonitorEmitDeviceDeleted(mon, device); } static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitorPtr mon, virJSONValuePtr data) { const char *name; if (!(name = virJSONValueObjectGetString(data, "name"))) { VIR_WARN("missing device in NIC_RX_FILTER_CHANGED event"); return; } qemuMonitorEmitNicRxFilterChanged(mon, name); } static void qemuMonitorJSONHandleSerialChange(qemuMonitorPtr mon, virJSONValuePtr data) { const char *name; bool connected; if (!(name = virJSONValueObjectGetString(data, "id"))) { VIR_WARN("missing device alias in VSERPORT_CHANGE event"); return; } if (virJSONValueObjectGetBoolean(data, "open", &connected) < 0) { VIR_WARN("missing port state for '%s' in VSERPORT_CHANGE event", name); return; } qemuMonitorEmitSerialChange(mon, name, connected); } static void qemuMonitorJSONHandleSpiceMigrated(qemuMonitorPtr mon, virJSONValuePtr data G_GNUC_UNUSED) { qemuMonitorEmitSpiceMigrated(mon); } static void qemuMonitorJSONHandleMigrationStatus(qemuMonitorPtr mon, virJSONValuePtr data) { const char *str; int status; if (!(str = virJSONValueObjectGetString(data, "status"))) { VIR_WARN("missing status in migration event"); return; } if ((status = qemuMonitorMigrationStatusTypeFromString(str)) == -1) { VIR_WARN("unknown status '%s' in migration event", str); return; } qemuMonitorEmitMigrationStatus(mon, status); } static void qemuMonitorJSONHandleMigrationPass(qemuMonitorPtr mon, virJSONValuePtr data) { int pass; if (virJSONValueObjectGetNumberInt(data, "pass", &pass) < 0) { VIR_WARN("missing dirty-sync-count in migration-pass event"); return; } qemuMonitorEmitMigrationPass(mon, pass); } static void qemuMonitorJSONHandleAcpiOstInfo(qemuMonitorPtr mon, virJSONValuePtr data) { virJSONValuePtr info; const char *alias; const char *slotType; const char *slot; unsigned int source; unsigned int status; if (!(info = virJSONValueObjectGetObject(data, "info"))) goto error; /* optional */ alias = virJSONValueObjectGetString(info, "device"); if (!(slotType = virJSONValueObjectGetString(info, "slot-type"))) goto error; if (!(slot = virJSONValueObjectGetString(info, "slot"))) goto error; if (virJSONValueObjectGetNumberUint(info, "source", &source) < 0) goto error; if (virJSONValueObjectGetNumberUint(info, "status", &status) < 0) goto error; qemuMonitorEmitAcpiOstInfo(mon, alias, slotType, slot, source, status); return; error: VIR_WARN("malformed ACPI_DEVICE_OST event"); return; } static void qemuMonitorJSONHandleBlockThreshold(qemuMonitorPtr mon, virJSONValuePtr data) { const char *nodename; unsigned long long threshold; unsigned long long excess; if (!(nodename = virJSONValueObjectGetString(data, "node-name"))) goto error; if (virJSONValueObjectGetNumberUlong(data, "write-threshold", &threshold) < 0) goto error; if (virJSONValueObjectGetNumberUlong(data, "amount-exceeded", &excess) < 0) goto error; qemuMonitorEmitBlockThreshold(mon, nodename, threshold, excess); return; error: VIR_WARN("malformed 'BLOCK_WRITE_THRESHOLD' event"); } static int qemuMonitorJSONExtractDumpStats(virJSONValuePtr result, qemuMonitorDumpStatsPtr ret) { const char *statusstr; if (!(statusstr = virJSONValueObjectGetString(result, "status"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("incomplete result, failed to get status")); return -1; } ret->status = qemuMonitorDumpStatusTypeFromString(statusstr); if (ret->status < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("incomplete result, unknown status string '%s'"), statusstr); return -1; } if (virJSONValueObjectGetNumberUlong(result, "completed", &ret->completed) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("incomplete result, failed to get completed")); return -1; } if (virJSONValueObjectGetNumberUlong(result, "total", &ret->total) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("incomplete result, failed to get total")); return -1; } return 0; } static void qemuMonitorJSONHandleDumpCompleted(qemuMonitorPtr mon, virJSONValuePtr data) { virJSONValuePtr result; int status; qemuMonitorDumpStats stats = { 0 }; const char *error = NULL; if (!(result = virJSONValueObjectGetObject(data, "result"))) { VIR_WARN("missing result in dump completed event"); return; } status = qemuMonitorJSONExtractDumpStats(result, &stats); error = virJSONValueObjectGetString(data, "error"); qemuMonitorEmitDumpCompleted(mon, status, &stats, error); } static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data) { const char *name; bool connected; if (!(name = virJSONValueObjectGetString(data, "id"))) { VIR_WARN("missing pr-manager alias in PR_MANAGER_STATUS_CHANGED event"); return; } if (virJSONValueObjectGetBoolean(data, "connected", &connected) < 0) { VIR_WARN("missing connected state for %s " "in PR_MANAGER_STATUS_CHANGED event", name); return; } qemuMonitorEmitPRManagerStatusChanged(mon, name, connected); } static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitorPtr mon, virJSONValuePtr data) { const char *netdev; bool gid_status; unsigned long long subnet_prefix, interface_id; if (!(netdev = virJSONValueObjectGetString(data, "netdev"))) { VIR_WARN("missing netdev in GID_STATUS_CHANGED event"); return; } if (virJSONValueObjectGetBoolean(data, "gid-status", &gid_status)) { VIR_WARN("missing gid-status in GID_STATUS_CHANGED event"); return; } if (virJSONValueObjectGetNumberUlong(data, "subnet-prefix", &subnet_prefix)) { VIR_WARN("missing subnet-prefix in GID_STATUS_CHANGED event"); return; } if (virJSONValueObjectGetNumberUlong(data, "interface-id", &interface_id)) { VIR_WARN("missing interface-id in GID_STATUS_CHANGED event"); return; } qemuMonitorEmitRdmaGidStatusChanged(mon, netdev, gid_status, subnet_prefix, interface_id); } int qemuMonitorJSONHumanCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str) { virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr obj; int ret = -1; cmd = qemuMonitorJSONMakeCommand("human-monitor-command", "s:command-line", cmd_str, NULL); if (!cmd || qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("Human monitor command is not available to run %s"), cmd_str); goto cleanup; } if (qemuMonitorJSONCheckError(cmd, reply)) goto cleanup; obj = virJSONValueObjectGet(reply, "return"); if (reply_str) { const char *data; data = virJSONValueGetString(obj); *reply_str = g_strdup(NULLSTR_EMPTY(data)); } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("qmp_capabilities", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon) { int ret; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cont", NULL); virJSONValuePtr reply = NULL; size_t i = 0; int timeout = 3; if (!cmd) return -1; do { ret = qemuMonitorJSONCommand(mon, cmd, &reply); if (ret != 0) break; /* If no error, we're done */ if ((ret = qemuMonitorJSONCheckError(cmd, reply)) == 0) break; /* If error class is not MigrationExpected, we're done. * Otherwise try 'cont' cmd again */ if (!qemuMonitorJSONHasError(reply, "MigrationExpected")) break; virJSONValueFree(reply); reply = NULL; g_usleep(250000); } while (++i <= timeout); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("stop", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running, virDomainPausedReason *reason) { int ret = -1; const char *status; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; if (reason) *reason = VIR_DOMAIN_PAUSED_UNKNOWN; if (!(cmd = qemuMonitorJSONMakeCommand("query-status", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (virJSONValueObjectGetBoolean(data, "running", running) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-status reply was missing running state")); goto cleanup; } if ((status = virJSONValueObjectGetString(data, "status"))) { if (!*running && reason) *reason = qemuMonitorVMStatusToPausedReason(status); } else if (!*running) { VIR_DEBUG("query-status reply was missing status details"); } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetLink(qemuMonitorPtr mon, const char *name, virDomainNetInterfaceLinkState state) { int ret = -1; virJSONValuePtr reply = NULL; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_link", "s:name", name, "b:up", state != VIR_DOMAIN_NET_INTERFACE_LINK_STATE_DOWN, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSystemReset(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_reset", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * qemuMonitorJSONExtractCPUS390Info: * @jsoncpu: pointer to a single JSON cpu entry * @cpu: pointer to a single cpu entry * * Derive the legacy cpu info 'halted' information * from the more accurate s390 cpu state. @cpu is * modified only on success. * * Note: the 'uninitialized' s390 cpu state can't be * mapped to halted yes/no. * * A s390 cpu entry could look like this * { "arch": "s390", * "cpu-index": 0, * "qom-path": "/machine/unattached/device[0]", * "thread_id": 3081, * "cpu-state": "operating" } * */ static void qemuMonitorJSONExtractCPUS390Info(virJSONValuePtr jsoncpu, struct qemuMonitorQueryCpusEntry *cpu) { const char *cpu_state = virJSONValueObjectGetString(jsoncpu, "cpu-state"); if (STREQ_NULLABLE(cpu_state, "operating") || STREQ_NULLABLE(cpu_state, "load")) cpu->halted = false; else if (STREQ_NULLABLE(cpu_state, "stopped") || STREQ_NULLABLE(cpu_state, "check-stop")) cpu->halted = true; } /** * qemuMonitorJSONExtractCPUInfo: * @data: JSON response data * @entries: filled with detected cpu entries on success * @nentries: number of entries returned * @fast: true if this is a response from query-cpus-fast * * The JSON response @data will have the following format * in case @fast == false * [{ "arch": "x86", * "current": true, * "CPU": 0, * "qom_path": "/machine/unattached/device[0]", * "pc": -2130415978, * "halted": true, * "thread_id": 2631237, * ...}, * {...} * ] * and for @fast == true * [{ "arch": "x86", * "cpu-index": 0, * "props": { * "core-id": 0, * "thread-id": 0, * "socket-id": 0 * }, * "qom-path": "/machine/unattached/device[0]", * "thread-id": 2631237, * ...}, * {...} * ] * or for s390 * [{ "arch": "s390", * "cpu-index": 0, * "props": { * "core-id": 0 * }, * "qom-path": "/machine/unattached/device[0]", * "thread-id": 1237, * "cpu-state": "operating", * ...}, * {...} * ] * * Note that since QEMU 2.13.0 the "arch" output member of the * "query-cpus-fast" command is replaced by "target". */ static int qemuMonitorJSONExtractCPUInfo(virJSONValuePtr data, struct qemuMonitorQueryCpusEntry **entries, size_t *nentries, bool fast) { const char *arch = NULL; struct qemuMonitorQueryCpusEntry *cpus = NULL; int ret = -1; size_t i; size_t ncpus; if ((ncpus = virJSONValueArraySize(data)) == 0) return -2; if (VIR_ALLOC_N(cpus, ncpus) < 0) goto cleanup; for (i = 0; i < ncpus; i++) { virJSONValuePtr entry = virJSONValueArrayGet(data, i); int cpuid = -1; int thread = 0; bool halted = false; const char *qom_path; if (!entry) { ret = -2; goto cleanup; } /* Some older qemu versions don't report the thread_id so treat this as * non-fatal, simply returning no data. * The return data of query-cpus-fast has different field names */ if (fast) { if (!(arch = virJSONValueObjectGetString(entry, "target"))) arch = virJSONValueObjectGetString(entry, "arch"); ignore_value(virJSONValueObjectGetNumberInt(entry, "cpu-index", &cpuid)); ignore_value(virJSONValueObjectGetNumberInt(entry, "thread-id", &thread)); qom_path = virJSONValueObjectGetString(entry, "qom-path"); } else { arch = virJSONValueObjectGetString(entry, "arch"); ignore_value(virJSONValueObjectGetNumberInt(entry, "CPU", &cpuid)); ignore_value(virJSONValueObjectGetNumberInt(entry, "thread_id", &thread)); ignore_value(virJSONValueObjectGetBoolean(entry, "halted", &halted)); qom_path = virJSONValueObjectGetString(entry, "qom_path"); } cpus[i].qemu_id = cpuid; cpus[i].tid = thread; cpus[i].halted = halted; cpus[i].qom_path = g_strdup(qom_path); /* process optional architecture-specific data */ if (STREQ_NULLABLE(arch, "s390") || STREQ_NULLABLE(arch, "s390x")) qemuMonitorJSONExtractCPUS390Info(entry, cpus + i); } *entries = g_steal_pointer(&cpus); *nentries = ncpus; ret = 0; cleanup: qemuMonitorQueryCpusFree(cpus, ncpus); return ret; } /** * qemuMonitorJSONQueryCPUs: * * @mon: monitor object * @entries: filled with detected entries on success * @nentries: number of entries returned * @force: force exit on error * @fast: use query-cpus-fast * * Queries qemu for cpu-related information. Failure to execute the command or * extract results does not produce an error as libvirt can continue without * this information, unless the caller has specified @force == true. * * Returns 0 on success, -1 on a fatal error (oom ...) and -2 if the * query failed gracefully. */ int qemuMonitorJSONQueryCPUs(qemuMonitorPtr mon, struct qemuMonitorQueryCpusEntry **entries, size_t *nentries, bool force, bool fast) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; if (fast) cmd = qemuMonitorJSONMakeCommand("query-cpus-fast", NULL); else cmd = qemuMonitorJSONMakeCommand("query-cpus", NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (force && qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; if (!(data = virJSONValueObjectGetArray(reply, "return"))) { ret = -2; goto cleanup; } ret = qemuMonitorJSONExtractCPUInfo(data, entries, nentries, fast); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon, virDomainVirtType *virtType) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL); virJSONValuePtr reply = NULL; virJSONValuePtr data; bool val = false; *virtType = VIR_DOMAIN_VIRT_QEMU; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (virJSONValueObjectGetBoolean(data, "enabled", &val) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("info kvm reply missing 'enabled' field")); goto cleanup; } if (val) *virtType = VIR_DOMAIN_VIRT_KVM; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * Loads correct video memory size values from QEMU and update the video * definition. * * Return 0 on success, -1 on failure and set proper error message. */ int qemuMonitorJSONUpdateVideoMemorySize(qemuMonitorPtr mon, virDomainVideoDefPtr video, char *path) { qemuMonitorJSONObjectProperty prop = { QEMU_MONITOR_OBJECT_PROPERTY_ULONG, {0} }; switch (video->type) { case VIR_DOMAIN_VIDEO_TYPE_VGA: if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'vgamem_mb'"), path); return -1; } video->vram = prop.val.ul * 1024; break; case VIR_DOMAIN_VIDEO_TYPE_QXL: if (qemuMonitorJSONGetObjectProperty(mon, path, "vram_size", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'vram_size'"), path); return -1; } video->vram = prop.val.ul / 1024; if (qemuMonitorJSONGetObjectProperty(mon, path, "ram_size", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'ram_size'"), path); return -1; } video->ram = prop.val.ul / 1024; if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'vgamem_mb'"), path); return -1; } video->vgamem = prop.val.ul * 1024; break; case VIR_DOMAIN_VIDEO_TYPE_VMVGA: if (qemuMonitorJSONGetObjectProperty(mon, path, "vgamem_mb", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'vgamem_mb'"), path); return -1; } video->vram = prop.val.ul * 1024; break; case VIR_DOMAIN_VIDEO_TYPE_CIRRUS: case VIR_DOMAIN_VIDEO_TYPE_XEN: case VIR_DOMAIN_VIDEO_TYPE_VBOX: case VIR_DOMAIN_VIDEO_TYPE_LAST: break; } return 0; } /** * Loads correct video vram64 size value from QEMU and update the video * definition. * * Return 0 on success, -1 on failure and set proper error message. */ int qemuMonitorJSONUpdateVideoVram64Size(qemuMonitorPtr mon, virDomainVideoDefPtr video, char *path) { qemuMonitorJSONObjectProperty prop = { QEMU_MONITOR_OBJECT_PROPERTY_ULONG, {0} }; switch (video->type) { case VIR_DOMAIN_VIDEO_TYPE_QXL: if (video->vram64 != 0) { if (qemuMonitorJSONGetObjectProperty(mon, path, "vram64_size_mb", &prop) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("QOM Object '%s' has no property 'vram64_size_mb'"), path); return -1; } video->vram64 = prop.val.ul * 1024; } break; case VIR_DOMAIN_VIDEO_TYPE_VGA: case VIR_DOMAIN_VIDEO_TYPE_VMVGA: case VIR_DOMAIN_VIDEO_TYPE_CIRRUS: case VIR_DOMAIN_VIDEO_TYPE_XEN: case VIR_DOMAIN_VIDEO_TYPE_VBOX: case VIR_DOMAIN_VIDEO_TYPE_LAST: break; } return 0; } int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, unsigned long long *currmem) { int ret = -1; virJSONValuePtr data; unsigned long long mem; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-balloon", NULL); virJSONValuePtr reply = NULL; *currmem = 0; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; /* See if balloon soft-failed */ if (qemuMonitorJSONHasError(reply, "DeviceNotActive") || qemuMonitorJSONHasError(reply, "KVMMissingCap")) { ret = 0; goto cleanup; } /* See if any other fatal error occurred */ if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("info balloon reply was missing balloon data")); goto cleanup; } *currmem = (mem/1024); ret = 1; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* Process the balloon driver statistics. The request and data returned * will be as follows (although the 'child[#]' entry will differ based on * where it's run). * * { "execute": "qom-get","arguments": \ * { "path": "/machine/i440fx/pci.0/child[7]","property": "guest-stats"} } * * {"return": {"stats": \ * {"stat-swap-out": 0, * "stat-free-memory": 686350336, * "stat-minor-faults": 697283, * "stat-major-faults": 951, * "stat-total-memory": 1019924480, * "stat-swap-in": 0}, * "last-update": 1371221540}} * * A value in "stats" can be -1 indicating it's never been collected/stored. * The 'last-update' value could be used in the future in order to determine * rates and/or whether data has been collected since a previous cycle. * It's currently unused. */ #define GET_BALLOON_STATS(OBJECT, FIELD, TAG, DIVISOR) \ if (virJSONValueObjectHasKey(OBJECT, FIELD) && \ (got < nr_stats)) { \ if (virJSONValueObjectGetNumberUlong(OBJECT, FIELD, &mem) < 0) { \ VIR_DEBUG("Failed to get '%s' value", FIELD); \ } else { \ /* Not being collected? No point in providing bad data */ \ if (mem != -1UL) { \ stats[got].tag = TAG; \ stats[got].val = mem / DIVISOR; \ got++; \ } \ } \ } int qemuMonitorJSONGetMemoryStats(qemuMonitorPtr mon, char *balloonpath, virDomainMemoryStatPtr stats, unsigned int nr_stats) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data; virJSONValuePtr statsdata; unsigned long long mem; int got = 0; ret = qemuMonitorJSONGetBalloonInfo(mon, &mem); if (ret == 1 && (got < nr_stats)) { stats[got].tag = VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON; stats[got].val = mem; got++; } if (!balloonpath) goto cleanup; if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", "s:path", balloonpath, "s:property", "guest-stats", NULL))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if ((data = virJSONValueObjectGetObject(reply, "error"))) { const char *klass = virJSONValueObjectGetString(data, "class"); const char *desc = virJSONValueObjectGetString(data, "desc"); if (STREQ_NULLABLE(klass, "GenericError") && STREQ_NULLABLE(desc, "guest hasn't updated any stats yet")) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("the guest hasn't updated any stats yet")); goto cleanup; } } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (!(statsdata = virJSONValueObjectGet(data, "stats"))) { VIR_DEBUG("data does not include 'stats'"); goto cleanup; } GET_BALLOON_STATS(statsdata, "stat-swap-in", VIR_DOMAIN_MEMORY_STAT_SWAP_IN, 1024); GET_BALLOON_STATS(statsdata, "stat-swap-out", VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, 1024); GET_BALLOON_STATS(statsdata, "stat-major-faults", VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, 1); GET_BALLOON_STATS(statsdata, "stat-minor-faults", VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, 1); GET_BALLOON_STATS(statsdata, "stat-free-memory", VIR_DOMAIN_MEMORY_STAT_UNUSED, 1024); GET_BALLOON_STATS(statsdata, "stat-total-memory", VIR_DOMAIN_MEMORY_STAT_AVAILABLE, 1024); GET_BALLOON_STATS(statsdata, "stat-available-memory", VIR_DOMAIN_MEMORY_STAT_USABLE, 1024); GET_BALLOON_STATS(data, "last-update", VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE, 1); GET_BALLOON_STATS(statsdata, "stat-disk-caches", VIR_DOMAIN_MEMORY_STAT_DISK_CACHES, 1024); GET_BALLOON_STATS(statsdata, "stat-htlb-pgalloc", VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGALLOC, 1); GET_BALLOON_STATS(statsdata, "stat-htlb-pgfail", VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGFAIL, 1); ret = got; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } #undef GET_BALLOON_STATS /* * Using the provided balloonpath, determine if we need to set the * collection interval property to enable statistics gathering. */ int qemuMonitorJSONSetMemoryStatsPeriod(qemuMonitorPtr mon, char *balloonpath, int period) { qemuMonitorJSONObjectProperty prop; /* Set to the value in memballoon (could enable or disable) */ memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); prop.type = QEMU_MONITOR_OBJECT_PROPERTY_INT; prop.val.iv = period; if (qemuMonitorJSONSetObjectProperty(mon, balloonpath, "guest-stats-polling-interval", &prop) < 0) { return -1; } return 0; } /* qemuMonitorJSONQueryBlock: * @mon: Monitor pointer * * This helper will attempt to make a "query-block" call and check for * errors before returning with the reply. * * Returns: NULL on error, reply on success */ static virJSONValuePtr qemuMonitorJSONQueryBlock(qemuMonitorPtr mon) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr devices = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-block", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0 || qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; devices = virJSONValueObjectStealArray(reply, "return"); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return devices; } static virJSONValuePtr qemuMonitorJSONGetBlockDev(virJSONValuePtr devices, size_t idx) { virJSONValuePtr dev = virJSONValueArrayGet(devices, idx); if (!dev || virJSONValueGetType(dev) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-block device entry was not in expected format")); return NULL; } return dev; } static const char * qemuMonitorJSONGetBlockDevDevice(virJSONValuePtr dev) { const char *thisdev; if (!(thisdev = virJSONValueObjectGetString(dev, "device"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-block device entry was not in expected format")); return NULL; } return thisdev; } static int qemuMonitorJSONBlockInfoAdd(virHashTablePtr table, struct qemuDomainDiskInfo *info, const char *entryname) { struct qemuDomainDiskInfo *tmp = NULL; int ret = -1; if (VIR_ALLOC(tmp) < 0) goto cleanup; *tmp = *info; tmp->nodename = NULL; if (info->nodename) tmp->nodename = g_strdup(info->nodename); if (virHashAddEntry(table, entryname, tmp) < 0) goto cleanup; tmp = NULL; ret = 0; cleanup: if (tmp) VIR_FREE(tmp->nodename); VIR_FREE(tmp); return ret; } int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon, virHashTablePtr table) { int ret = -1; size_t i; virJSONValuePtr devices; if (!(devices = qemuMonitorJSONQueryBlock(mon))) return -1; for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev; virJSONValuePtr image; struct qemuDomainDiskInfo info = { false }; const char *thisdev; const char *status; const char *qdev; if (!(dev = qemuMonitorJSONGetBlockDev(devices, i))) goto cleanup; if (!(thisdev = qemuMonitorJSONGetBlockDevDevice(dev))) goto cleanup; thisdev = qemuAliasDiskDriveSkipPrefix(thisdev); qdev = virJSONValueObjectGetString(dev, "qdev"); if (*thisdev == '\0') thisdev = NULL; if (!qdev && !thisdev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-block device entry was not in expected format")); goto cleanup; } if (virJSONValueObjectGetBoolean(dev, "removable", &info.removable) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot read %s value"), "removable"); goto cleanup; } /* 'tray_open' is present only if the device has a tray */ if (virJSONValueObjectGetBoolean(dev, "tray_open", &info.tray_open) == 0) info.tray = true; /* presence of 'inserted' notifies that a medium is in the device */ if ((image = virJSONValueObjectGetObject(dev, "inserted"))) { info.nodename = (char *) virJSONValueObjectGetString(image, "node-name"); } else { info.empty = true; } /* Missing io-status indicates no error */ if ((status = virJSONValueObjectGetString(dev, "io-status"))) { info.io_status = qemuMonitorBlockIOStatusToError(status); if (info.io_status < 0) goto cleanup; } if (thisdev && qemuMonitorJSONBlockInfoAdd(table, &info, thisdev) < 0) goto cleanup; if (qdev && STRNEQ_NULLABLE(thisdev, qdev) && qemuMonitorJSONBlockInfoAdd(table, &info, qdev) < 0) goto cleanup; } ret = 0; cleanup: virJSONValueFree(devices); return ret; } static qemuBlockStatsPtr qemuMonitorJSONBlockStatsCollectData(virJSONValuePtr dev, int *nstats) { g_autofree qemuBlockStatsPtr bstats = NULL; virJSONValuePtr parent; virJSONValuePtr parentstats; virJSONValuePtr stats; if ((stats = virJSONValueObjectGetObject(dev, "stats")) == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("blockstats stats entry was not " "in expected format")); return NULL; } if (VIR_ALLOC(bstats) < 0) return NULL; #define QEMU_MONITOR_BLOCK_STAT_GET(NAME, VAR, MANDATORY) \ if (MANDATORY || virJSONValueObjectHasKey(stats, NAME)) { \ (*nstats)++; \ if (virJSONValueObjectGetNumberUlong(stats, NAME, &VAR) < 0) { \ virReportError(VIR_ERR_INTERNAL_ERROR, \ _("cannot read %s statistic"), NAME); \ return NULL; \ } \ } QEMU_MONITOR_BLOCK_STAT_GET("rd_bytes", bstats->rd_bytes, true); QEMU_MONITOR_BLOCK_STAT_GET("wr_bytes", bstats->wr_bytes, true); QEMU_MONITOR_BLOCK_STAT_GET("rd_operations", bstats->rd_req, true); QEMU_MONITOR_BLOCK_STAT_GET("wr_operations", bstats->wr_req, true); QEMU_MONITOR_BLOCK_STAT_GET("rd_total_time_ns", bstats->rd_total_times, false); QEMU_MONITOR_BLOCK_STAT_GET("wr_total_time_ns", bstats->wr_total_times, false); QEMU_MONITOR_BLOCK_STAT_GET("flush_operations", bstats->flush_req, false); QEMU_MONITOR_BLOCK_STAT_GET("flush_total_time_ns", bstats->flush_total_times, false); #undef QEMU_MONITOR_BLOCK_STAT_GET if ((parent = virJSONValueObjectGetObject(dev, "parent")) && (parentstats = virJSONValueObjectGetObject(parent, "stats"))) { if (virJSONValueObjectGetNumberUlong(parentstats, "wr_highest_offset", &bstats->wr_highest_offset) == 0) bstats->wr_highest_offset_valid = true; } return g_steal_pointer(&bstats); } static int qemuMonitorJSONAddOneBlockStatsInfo(qemuBlockStatsPtr bstats, const char *name, virHashTablePtr stats) { qemuBlockStatsPtr copy = NULL; if (VIR_ALLOC(copy) < 0) return -1; if (bstats) *copy = *bstats; if (virHashAddEntry(stats, name, copy) < 0) { VIR_FREE(copy); return -1; } return 0; } static int qemuMonitorJSONGetOneBlockStatsInfo(virJSONValuePtr dev, const char *dev_name, int depth, virHashTablePtr hash, bool backingChain) { g_autofree qemuBlockStatsPtr bstats = NULL; int nstats = 0; const char *qdevname = NULL; const char *nodename = NULL; g_autofree char *devicename = NULL; virJSONValuePtr backing; if (dev_name && !(devicename = qemuDomainStorageAlias(dev_name, depth))) return -1; qdevname = virJSONValueObjectGetString(dev, "qdev"); nodename = virJSONValueObjectGetString(dev, "node-name"); if (!devicename && !qdevname && !nodename) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("blockstats device entry was not in expected format")); return -1; } if (!(bstats = qemuMonitorJSONBlockStatsCollectData(dev, &nstats))) return -1; if (devicename && qemuMonitorJSONAddOneBlockStatsInfo(bstats, devicename, hash) < 0) return -1; if (qdevname && STRNEQ_NULLABLE(qdevname, devicename) && qemuMonitorJSONAddOneBlockStatsInfo(bstats, qdevname, hash) < 0) return -1; if (nodename && qemuMonitorJSONAddOneBlockStatsInfo(bstats, nodename, hash) < 0) return -1; if (backingChain && (backing = virJSONValueObjectGetObject(dev, "backing")) && qemuMonitorJSONGetOneBlockStatsInfo(backing, dev_name, depth + 1, hash, true) < 0) return -1; return nstats; } virJSONValuePtr qemuMonitorJSONQueryBlockstats(qemuMonitorPtr mon) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr ret = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; ret = virJSONValueObjectStealArray(reply, "return"); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon, virHashTablePtr hash, bool backingChain) { int nstats = 0; int rc; size_t i; g_autoptr(virJSONValue) devices = NULL; if (!(devices = qemuMonitorJSONQueryBlockstats(mon))) return -1; for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev = virJSONValueArrayGet(devices, i); const char *dev_name; if (!dev || virJSONValueGetType(dev) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("blockstats device entry was not " "in expected format")); return -1; } if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("blockstats device entry was not " "in expected format")); return -1; } if (*dev_name == '\0') dev_name = NULL; rc = qemuMonitorJSONGetOneBlockStatsInfo(dev, dev_name, 0, hash, backingChain); if (rc < 0) return -1; if (rc > nstats) nstats = rc; } return nstats; } static int qemuMonitorJSONBlockStatsUpdateCapacityData(virJSONValuePtr image, const char *name, virHashTablePtr stats, qemuBlockStatsPtr *entry) { qemuBlockStatsPtr bstats; if (!(bstats = virHashLookup(stats, name))) { if (VIR_ALLOC(bstats) < 0) return -1; if (virHashAddEntry(stats, name, bstats) < 0) { VIR_FREE(bstats); return -1; } } if (entry) *entry = bstats; /* failures can be ignored after this point */ if (virJSONValueObjectGetNumberUlong(image, "virtual-size", &bstats->capacity) < 0) return 0; /* if actual-size is missing, image is not thin provisioned */ if (virJSONValueObjectGetNumberUlong(image, "actual-size", &bstats->physical) < 0) bstats->physical = bstats->capacity; return 0; } static int qemuMonitorJSONBlockStatsUpdateCapacityOne(virJSONValuePtr image, const char *dev_name, int depth, virHashTablePtr stats, bool backingChain) { int ret = -1; char *entry_name = qemuDomainStorageAlias(dev_name, depth); virJSONValuePtr backing; if (qemuMonitorJSONBlockStatsUpdateCapacityData(image, entry_name, stats, NULL) < 0) goto cleanup; if (backingChain && (backing = virJSONValueObjectGetObject(image, "backing-image")) && qemuMonitorJSONBlockStatsUpdateCapacityOne(backing, dev_name, depth + 1, stats, true) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(entry_name); return ret; } int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon, virHashTablePtr stats, bool backingChain) { int ret = -1; size_t i; virJSONValuePtr devices; if (!(devices = qemuMonitorJSONQueryBlock(mon))) return -1; for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev; virJSONValuePtr inserted; virJSONValuePtr image; const char *dev_name; if (!(dev = qemuMonitorJSONGetBlockDev(devices, i))) goto cleanup; if (!(dev_name = qemuMonitorJSONGetBlockDevDevice(dev))) goto cleanup; /* drive may be empty */ if (!(inserted = virJSONValueObjectGetObject(dev, "inserted")) || !(image = virJSONValueObjectGetObject(inserted, "image"))) continue; if (qemuMonitorJSONBlockStatsUpdateCapacityOne(image, dev_name, 0, stats, backingChain) < 0) goto cleanup; } ret = 0; cleanup: virJSONValueFree(devices); return ret; } static int qemuMonitorJSONBlockStatsUpdateCapacityBlockdevWorker(size_t pos G_GNUC_UNUSED, virJSONValuePtr val, void *opaque) { virHashTablePtr stats = opaque; virJSONValuePtr image; const char *nodename; qemuBlockStatsPtr entry; if (!(nodename = virJSONValueObjectGetString(val, "node-name")) || !(image = virJSONValueObjectGetObject(val, "image"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-named-block-nodes entry was not in expected format")); return -1; } if (qemuMonitorJSONBlockStatsUpdateCapacityData(image, nodename, stats, &entry) < 0) return -1; if (entry) ignore_value(virJSONValueObjectGetNumberUlong(val, "write_threshold", &entry->write_threshold)); return 1; /* we don't want to steal the value from the JSON array */ } int qemuMonitorJSONBlockStatsUpdateCapacityBlockdev(qemuMonitorPtr mon, virHashTablePtr stats) { virJSONValuePtr nodes; int ret = -1; if (!(nodes = qemuMonitorJSONQueryNamedBlockNodes(mon))) return -1; if (virJSONValueArrayForeachSteal(nodes, qemuMonitorJSONBlockStatsUpdateCapacityBlockdevWorker, stats) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(nodes); return ret; } static void qemuMonitorJSONBlockNamedNodeDataFree(qemuBlockNamedNodeDataPtr data) { g_free(data); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuBlockNamedNodeData, qemuMonitorJSONBlockNamedNodeDataFree); static int qemuMonitorJSONBlockGetNamedNodeDataWorker(size_t pos G_GNUC_UNUSED, virJSONValuePtr val, void *opaque) { virHashTablePtr nodes = opaque; virJSONValuePtr img; const char *nodename; g_autoptr(qemuBlockNamedNodeData) ent = NULL; ent = g_new0(qemuBlockNamedNodeData, 1); if (!(nodename = virJSONValueObjectGetString(val, "node-name")) || !(img = virJSONValueObjectGetObject(val, "image"))) goto broken; if (virJSONValueObjectGetNumberUlong(img, "virtual-size", &ent->capacity) < 0) goto broken; /* if actual-size is missing, image is not thin provisioned */ if (virJSONValueObjectGetNumberUlong(img, "actual-size", &ent->physical) < 0) ent->physical = ent->capacity; if (virHashAddEntry(nodes, nodename, ent) < 0) return -1; ent = NULL; return 1; /* we don't want to steal the value from the JSON array */ broken: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-named-block-nodes entry was not in expected format")); return -1; } virHashTablePtr qemuMonitorJSONBlockGetNamedNodeData(qemuMonitorPtr mon) { g_autoptr(virJSONValue) nodes = NULL; g_autoptr(virHashTable) ret = NULL; if (!(nodes = qemuMonitorJSONQueryNamedBlockNodes(mon))) return NULL; if (!(ret = virHashNew((virHashDataFree) qemuMonitorJSONBlockNamedNodeDataFree))) return NULL; if (virJSONValueArrayForeachSteal(nodes, qemuMonitorJSONBlockGetNamedNodeDataWorker, ret) < 0) return NULL; return g_steal_pointer(&ret); } int qemuMonitorJSONBlockResize(qemuMonitorPtr mon, const char *device, const char *nodename, unsigned long long size) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("block_resize", "S:device", device, "S:node-name", nodename, "U:size", size, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetPassword(qemuMonitorPtr mon, const char *protocol, const char *password, const char *action_if_connected) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_password", "s:protocol", protocol, "s:password", password, "s:connected", action_if_connected, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONExpirePassword(qemuMonitorPtr mon, const char *protocol, const char *expire_time) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("expire_password", "s:protocol", protocol, "s:time", expire_time, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, unsigned long long newmem) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon", "U:value", newmem * 1024, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; /* See if balloon soft-failed */ if (qemuMonitorJSONHasError(reply, "DeviceNotActive") || qemuMonitorJSONHasError(reply, "KVMMissingCap")) { ret = 0; goto cleanup; } /* See if any other fatal error occurred */ if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; /* Real success */ ret = 1; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetCPU(qemuMonitorPtr mon, int cpu, bool online) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; if (online) { cmd = qemuMonitorJSONMakeCommand("cpu-add", "i:id", cpu, NULL); } else { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("vCPU unplug is not supported by this QEMU")); goto cleanup; } if (!cmd) goto cleanup; if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0) goto cleanup; ret = qemuMonitorJSONCheckError(cmd, reply); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * Run QMP command to eject a media from ejectable device. * * Returns: * -1 on error * 0 on success */ int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, const char *dev_name, bool force) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject", "s:device", dev_name, "b:force", force ? 1 : 0, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, const char *dev_name, const char *newmedia, const char *format) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("change", "s:device", dev_name, "s:target", newmedia, "S:arg", format, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon, const char *cmdtype, unsigned long long offset, unsigned long long length, const char *path) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand(cmdtype, "U:val", offset, "U:size", length, "s:filename", path, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, unsigned long long offset, unsigned long long length, const char *path) { return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path); } int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, unsigned long long offset, unsigned long long length, const char *path) { return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path); } int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("migrate_set_speed", "U:value", bandwidth * 1024ULL * 1024ULL, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon, unsigned long long downtime) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("migrate_set_downtime", "d:value", downtime / 1000.0, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetMigrationCacheSize(qemuMonitorPtr mon, unsigned long long *cacheSize) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; *cacheSize = 0; cmd = qemuMonitorJSONMakeCommand("query-migrate-cache-size", NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_NUMBER) < 0) goto cleanup; if (virJSONValueObjectGetNumberUlong(reply, "return", cacheSize) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid cache size in query-migrate-cache-size reply")); goto cleanup; } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetMigrationCacheSize(qemuMonitorPtr mon, unsigned long long cacheSize) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("migrate-set-cache-size", "U:value", cacheSize, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetMigrationParams(qemuMonitorPtr mon, virJSONValuePtr *params) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; *params = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-parameters", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; *params = virJSONValueObjectStealObject(reply, "return"); ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetMigrationParams(qemuMonitorPtr mon, virJSONValuePtr params) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; if (!(cmd = virJSONValueNewObject())) goto cleanup; if (virJSONValueObjectAppendString(cmd, "execute", "migrate-set-parameters") < 0) goto cleanup; if (virJSONValueObjectAppend(cmd, "arguments", params) < 0) goto cleanup; params = NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(params); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONGetMigrationStatsReply(virJSONValuePtr reply, qemuMonitorMigrationStatsPtr stats, char **error) { virJSONValuePtr ret; virJSONValuePtr ram; virJSONValuePtr disk; virJSONValuePtr comp; const char *statusstr; int rc; double mbps; const char *tmp; ret = virJSONValueObjectGetObject(reply, "return"); if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("info migration reply was missing return status")); return -1; } stats->status = qemuMonitorMigrationStatusTypeFromString(statusstr); if (stats->status < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected migration status in %s"), statusstr); return -1; } ignore_value(virJSONValueObjectGetNumberUlong(ret, "total-time", &stats->total_time)); if (stats->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) { rc = virJSONValueObjectGetNumberUlong(ret, "downtime", &stats->downtime); } else { rc = virJSONValueObjectGetNumberUlong(ret, "expected-downtime", &stats->downtime); } if (rc == 0) stats->downtime_set = true; if (virJSONValueObjectGetNumberUlong(ret, "setup-time", &stats->setup_time) == 0) stats->setup_time_set = true; ignore_value(virJSONValueObjectGetNumberInt(ret, "cpu-throttle-percentage", &stats->cpu_throttle_percentage)); switch ((qemuMonitorMigrationStatus) stats->status) { case QEMU_MONITOR_MIGRATION_STATUS_INACTIVE: case QEMU_MONITOR_MIGRATION_STATUS_SETUP: case QEMU_MONITOR_MIGRATION_STATUS_CANCELLED: case QEMU_MONITOR_MIGRATION_STATUS_LAST: break; case QEMU_MONITOR_MIGRATION_STATUS_ERROR: if (error) { tmp = virJSONValueObjectGetString(ret, "error-desc"); if (tmp) *error = g_strdup(tmp); } break; case QEMU_MONITOR_MIGRATION_STATUS_ACTIVE: case QEMU_MONITOR_MIGRATION_STATUS_POSTCOPY: case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED: case QEMU_MONITOR_MIGRATION_STATUS_CANCELLING: case QEMU_MONITOR_MIGRATION_STATUS_PRE_SWITCHOVER: case QEMU_MONITOR_MIGRATION_STATUS_DEVICE: ram = virJSONValueObjectGetObject(ret, "ram"); if (!ram) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but no RAM info was set")); return -1; } if (virJSONValueObjectGetNumberUlong(ram, "transferred", &stats->ram_transferred) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'transferred' " "data was missing")); return -1; } if (virJSONValueObjectGetNumberUlong(ram, "remaining", &stats->ram_remaining) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'remaining' " "data was missing")); return -1; } if (virJSONValueObjectGetNumberUlong(ram, "total", &stats->ram_total) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("migration was active, but RAM 'total' " "data was missing")); return -1; } if (virJSONValueObjectGetNumberDouble(ram, "mbps", &mbps) == 0 && mbps > 0) { /* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */ stats->ram_bps = mbps * (1000 * 1000 / 8); } if (virJSONValueObjectGetNumberUlong(ram, "duplicate", &stats->ram_duplicate) == 0) stats->ram_duplicate_set = true; ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal", &stats->ram_normal)); ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal-bytes", &stats->ram_normal_bytes)); ignore_value(virJSONValueObjectGetNumberUlong(ram, "dirty-pages-rate", &stats->ram_dirty_rate)); ignore_value(virJSONValueObjectGetNumberUlong(ram, "page-size", &stats->ram_page_size)); ignore_value(virJSONValueObjectGetNumberUlong(ram, "dirty-sync-count", &stats->ram_iteration)); ignore_value(virJSONValueObjectGetNumberUlong(ram, "postcopy-requests", &stats->ram_postcopy_reqs)); disk = virJSONValueObjectGetObject(ret, "disk"); if (disk) { rc = virJSONValueObjectGetNumberUlong(disk, "transferred", &stats->disk_transferred); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("disk migration was active, but " "'transferred' data was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(disk, "remaining", &stats->disk_remaining); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("disk migration was active, but 'remaining' " "data was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(disk, "total", &stats->disk_total); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("disk migration was active, but 'total' " "data was missing")); return -1; } if (virJSONValueObjectGetNumberDouble(disk, "mbps", &mbps) == 0 && mbps > 0) { /* mpbs from QEMU reports Mbits/s (M as in 10^6 not Mi as 2^20) */ stats->disk_bps = mbps * (1000 * 1000 / 8); } } comp = virJSONValueObjectGetObject(ret, "xbzrle-cache"); if (comp) { stats->xbzrle_set = true; rc = virJSONValueObjectGetNumberUlong(comp, "cache-size", &stats->xbzrle_cache_size); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("XBZRLE is active, but 'cache-size' data " "was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(comp, "bytes", &stats->xbzrle_bytes); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("XBZRLE is active, but 'bytes' data " "was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(comp, "pages", &stats->xbzrle_pages); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("XBZRLE is active, but 'pages' data " "was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(comp, "cache-miss", &stats->xbzrle_cache_miss); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("XBZRLE is active, but 'cache-miss' data " "was missing")); return -1; } rc = virJSONValueObjectGetNumberUlong(comp, "overflow", &stats->xbzrle_overflow); if (rc < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("XBZRLE is active, but 'overflow' data " "was missing")); return -1; } } break; } return 0; } int qemuMonitorJSONGetMigrationStats(qemuMonitorPtr mon, qemuMonitorMigrationStatsPtr stats, char **error) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migrate", NULL); virJSONValuePtr reply = NULL; memset(stats, 0, sizeof(*stats)); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; if (qemuMonitorJSONGetMigrationStatsReply(reply, stats, error) < 0) goto cleanup; ret = 0; cleanup: if (ret < 0) memset(stats, 0, sizeof(*stats)); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONMigrate(qemuMonitorPtr mon, unsigned int flags, const char *uri) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate", "b:detach", flags & QEMU_MONITOR_MIGRATE_BACKGROUND ? 1 : 0, "b:blk", flags & QEMU_MONITOR_MIGRATE_NON_SHARED_DISK ? 1 : 0, "b:inc", flags & QEMU_MONITOR_MIGRATE_NON_SHARED_INC ? 1 : 0, "s:uri", uri, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate_cancel", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* qemuMonitorJSONQueryDump: * @mon: Monitor pointer * @stats: Monitor dump stats * * Attempt to make a "query-dump" call, check for errors, and get/return * the current from the reply * * Returns: 0 on success, -1 on failure */ int qemuMonitorJSONQueryDump(qemuMonitorPtr mon, qemuMonitorDumpStatsPtr stats) { virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-dump", NULL); virJSONValuePtr reply = NULL; virJSONValuePtr result = NULL; int ret = -1; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; result = virJSONValueObjectGetObject(reply, "return"); ret = qemuMonitorJSONExtractDumpStats(result, stats); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetDumpGuestMemoryCapability(qemuMonitorPtr mon, const char *capability) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr caps; virJSONValuePtr formats; size_t i; if (!(cmd = qemuMonitorJSONMakeCommand("query-dump-guest-memory-capability", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; caps = virJSONValueObjectGetObject(reply, "return"); if (!(formats = virJSONValueObjectGetArray(caps, "formats"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing supported dump formats")); goto cleanup; } for (i = 0; i < virJSONValueArraySize(formats); i++) { virJSONValuePtr dumpformat = virJSONValueArrayGet(formats, i); if (!dumpformat || virJSONValueGetType(dumpformat) != VIR_JSON_TYPE_STRING) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing entry in supported dump formats")); goto cleanup; } if (STREQ(virJSONValueGetString(dumpformat), capability)) { ret = 1; goto cleanup; } } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONDump(qemuMonitorPtr mon, const char *protocol, const char *dumpformat, bool detach) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("dump-guest-memory", "b:paging", false, "s:protocol", protocol, "S:format", dumpformat, "B:detach", detach, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGraphicsRelocate(qemuMonitorPtr mon, int type, const char *hostname, int port, int tlsPort, const char *tlsSubject) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("client_migrate_info", "s:protocol", (type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE ? "spice" : "vnc"), "s:hostname", hostname, "i:port", port, "i:tls-port", tlsPort, "S:cert-subject", tlsSubject, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("getfd", "s:fdname", fdname, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, const char *fdname) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("closefd", "s:fdname", fdname, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONAddNetdev(qemuMonitorPtr mon, const char *netdevstr) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr args = NULL; cmd = qemuMonitorJSONMakeCommand("netdev_add", NULL); if (!cmd) return -1; args = qemuMonitorJSONKeywordStringToJSON(netdevstr, "type"); if (!args) goto cleanup; if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL; /* obj owns reference to args now */ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(args); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon, const char *alias) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("netdev_del", "s:id", alias, NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg, virNetDevRxFilterPtr *filter) { int ret = -1; const char *tmp; virJSONValuePtr returnArray, entry, table, element; size_t nTable; size_t i; virNetDevRxFilterPtr fil = virNetDevRxFilterNew(); if (!fil) goto cleanup; returnArray = virJSONValueObjectGetArray(msg, "return"); if (!(entry = virJSONValueArrayGet(returnArray, 0))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query -rx-filter return data missing array element")); goto cleanup; } if (!(tmp = virJSONValueObjectGetString(entry, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid name " "in query-rx-filter response")); goto cleanup; } fil->name = g_strdup(tmp); if ((!(tmp = virJSONValueObjectGetString(entry, "main-mac"))) || virMacAddrParse(tmp, &fil->mac) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'main-mac' " "in query-rx-filter response")); goto cleanup; } if (virJSONValueObjectGetBoolean(entry, "promiscuous", &fil->promiscuous) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'promiscuous' " "in query-rx-filter response")); goto cleanup; } if (virJSONValueObjectGetBoolean(entry, "broadcast-allowed", &fil->broadcastAllowed) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'broadcast-allowed' " "in query-rx-filter response")); goto cleanup; } if ((!(tmp = virJSONValueObjectGetString(entry, "unicast"))) || ((fil->unicast.mode = virNetDevRxFilterModeTypeFromString(tmp)) < 0)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'unicast' " "in query-rx-filter response")); goto cleanup; } if (virJSONValueObjectGetBoolean(entry, "unicast-overflow", &fil->unicast.overflow) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'unicast-overflow' " "in query-rx-filter response")); goto cleanup; } if ((!(table = virJSONValueObjectGet(entry, "unicast-table"))) || (!virJSONValueIsArray(table))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'unicast-table' array " "in query-rx-filter response")); goto cleanup; } nTable = virJSONValueArraySize(table); if (VIR_ALLOC_N(fil->unicast.table, nTable)) goto cleanup; for (i = 0; i < nTable; i++) { if (!(element = virJSONValueArrayGet(table, i)) || !(tmp = virJSONValueGetString(element))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Missing or invalid element %zu of 'unicast' " "list in query-rx-filter response"), i); goto cleanup; } if (virMacAddrParse(tmp, &fil->unicast.table[i]) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid mac address '%s' in 'unicast-table' " "array in query-rx-filter response"), tmp); goto cleanup; } } fil->unicast.nTable = nTable; if ((!(tmp = virJSONValueObjectGetString(entry, "multicast"))) || ((fil->multicast.mode = virNetDevRxFilterModeTypeFromString(tmp)) < 0)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'multicast' " "in query-rx-filter response")); goto cleanup; } if (virJSONValueObjectGetBoolean(entry, "multicast-overflow", &fil->multicast.overflow) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'multicast-overflow' " "in query-rx-filter response")); goto cleanup; } if ((!(table = virJSONValueObjectGet(entry, "multicast-table"))) || (!virJSONValueIsArray(table))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'multicast-table' array " "in query-rx-filter response")); goto cleanup; } nTable = virJSONValueArraySize(table); if (VIR_ALLOC_N(fil->multicast.table, nTable)) goto cleanup; for (i = 0; i < nTable; i++) { if (!(element = virJSONValueArrayGet(table, i)) || !(tmp = virJSONValueGetString(element))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Missing or invalid element %zu of 'multicast' " "list in query-rx-filter response"), i); goto cleanup; } if (virMacAddrParse(tmp, &fil->multicast.table[i]) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid mac address '%s' in 'multicast-table' " "array in query-rx-filter response"), tmp); goto cleanup; } } fil->multicast.nTable = nTable; if ((!(tmp = virJSONValueObjectGetString(entry, "vlan"))) || ((fil->vlan.mode = virNetDevRxFilterModeTypeFromString(tmp)) < 0)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'vlan' " "in query-rx-filter response")); goto cleanup; } if ((!(table = virJSONValueObjectGet(entry, "vlan-table"))) || (!virJSONValueIsArray(table))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Missing or invalid 'vlan-table' array " "in query-rx-filter response")); goto cleanup; } nTable = virJSONValueArraySize(table); if (VIR_ALLOC_N(fil->vlan.table, nTable)) goto cleanup; for (i = 0; i < nTable; i++) { if (!(element = virJSONValueArrayGet(table, i)) || virJSONValueGetNumberUint(element, &fil->vlan.table[i]) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Missing or invalid element %zu of 'vlan-table' " "array in query-rx-filter response"), i); goto cleanup; } } fil->vlan.nTable = nTable; ret = 0; cleanup: if (ret < 0) { virNetDevRxFilterFree(fil); fil = NULL; } *filter = fil; return ret; } int qemuMonitorJSONQueryRxFilter(qemuMonitorPtr mon, const char *alias, virNetDevRxFilterPtr *filter) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-rx-filter", "s:name", alias, NULL); virJSONValuePtr reply = NULL; if (!cmd) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; if (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0) goto cleanup; ret = 0; cleanup: if (ret < 0) { virNetDevRxFilterFree(*filter); *filter = NULL; } virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* * Example return data * * {"return": [ * {"filename": "stdio", "label": "monitor"}, * {"filename": "pty:/dev/pts/6", "label": "serial0", "frontend-open": true}, * {"filename": "pty:/dev/pts/7", "label": "parallel0"} * ]} * */ static int qemuMonitorJSONExtractChardevInfo(virJSONValuePtr reply, virHashTablePtr info) { virJSONValuePtr data; int ret = -1; size_t i; qemuMonitorChardevInfoPtr entry = NULL; data = virJSONValueObjectGetArray(reply, "return"); for (i = 0; i < virJSONValueArraySize(data); i++) { virJSONValuePtr chardev = virJSONValueArrayGet(data, i); const char *type; const char *alias; bool connected; if (!chardev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing array element")); goto cleanup; } if (!(alias = virJSONValueObjectGetString(chardev, "label"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing label")); goto cleanup; } if (!(type = virJSONValueObjectGetString(chardev, "filename"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("character device information was missing filename")); goto cleanup; } if (VIR_ALLOC(entry) < 0) goto cleanup; if (STRPREFIX(type, "pty:")) entry->ptyPath = g_strdup(type + strlen("pty:")); if (virJSONValueObjectGetBoolean(chardev, "frontend-open", &connected) == 0) { if (connected) entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; else entry->state = VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED; } if (virHashAddEntry(info, alias, entry) < 0) { virReportError(VIR_ERR_OPERATION_FAILED, _("failed to add chardev '%s' info"), alias); goto cleanup; } entry = NULL; } ret = 0; cleanup: if (entry) { VIR_FREE(entry->ptyPath); VIR_FREE(entry); } return ret; } int qemuMonitorJSONGetChardevInfo(qemuMonitorPtr mon, virHashTablePtr info) { int ret = -1; virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-chardev", NULL); virJSONValuePtr reply = NULL; if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; ret = qemuMonitorJSONExtractChardevInfo(reply, info); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONDelDevice(qemuMonitorPtr mon, const char *devalias) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("device_del", "s:id", devalias, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) { ret = -2; goto cleanup; } if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONAddDeviceArgs(qemuMonitorPtr mon, virJSONValuePtr args) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("device_add", NULL))) goto cleanup; if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL; /* obj owns reference to args now */ if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(args); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONAddDevice(qemuMonitorPtr mon, const char *devicestr) { virJSONValuePtr args; if (!(args = qemuMonitorJSONKeywordStringToJSON(devicestr, "driver"))) return -1; return qemuMonitorJSONAddDeviceArgs(mon, args); } int qemuMonitorJSONAddObject(qemuMonitorPtr mon, virJSONValuePtr props) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommandInternal("object-add", props))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONDelObject(qemuMonitorPtr mon, const char *objalias) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("object-del", "s:id", objalias, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* speed is in bytes/sec */ int qemuMonitorJSONDriveMirror(qemuMonitorPtr mon, const char *device, const char *file, const char *format, unsigned long long speed, unsigned int granularity, unsigned long long buf_size, bool shallow, bool reuse) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; cmd = qemuMonitorJSONMakeCommand("drive-mirror", "s:device", device, "s:target", file, "Y:speed", speed, "z:granularity", granularity, "P:buf-size", buf_size, "s:sync", shallow ? "top" : "full", "s:mode", reuse ? "existing" : "absolute-paths", "S:format", format, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; return qemuMonitorJSONCheckError(cmd, reply); } int qemuMonitorJSONBlockdevMirror(qemuMonitorPtr mon, const char *jobname, bool persistjob, const char *device, const char *target, unsigned long long speed, unsigned int granularity, unsigned long long buf_size, bool shallow) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; if (persistjob) { autofinalize = VIR_TRISTATE_BOOL_YES; autodismiss = VIR_TRISTATE_BOOL_NO; } cmd = qemuMonitorJSONMakeCommand("blockdev-mirror", "S:job-id", jobname, "s:device", device, "s:target", target, "Y:speed", speed, "z:granularity", granularity, "P:buf-size", buf_size, "s:sync", shallow ? "top" : "full", "T:auto-finalize", autofinalize, "T:auto-dismiss", autodismiss, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; return qemuMonitorJSONCheckError(cmd, reply); } int qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr *actions) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("transaction", "a:actions", actions, NULL); if (!cmd) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* Probe if active commit is supported: pass in a bogus device and NULL top * and base. The probe return is true if active commit is detected or false * if not supported or on any error */ bool qemuMonitorJSONSupportsActiveCommit(qemuMonitorPtr mon) { bool ret = false; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("block-commit", "s:device", "bogus", NULL))) return false; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) { VIR_DEBUG("block-commit supports active commit"); ret = true; goto cleanup; } /* This is a false negative for qemu 2.0; but probably not * worth the additional complexity to worry about it */ VIR_DEBUG("block-commit requires 'top' parameter, " "assuming it lacks active commit"); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* speed is in bytes/sec. Returns 0 on success, -1 with error message * emitted on failure. */ int qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device, const char *jobname, bool persistjob, const char *top, const char *topNode, const char *base, const char *baseNode, const char *backingName, unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; if (persistjob) { autofinalize = VIR_TRISTATE_BOOL_YES; autodismiss = VIR_TRISTATE_BOOL_NO; } cmd = qemuMonitorJSONMakeCommand("block-commit", "s:device", device, "S:job-id", jobname, "Y:speed", speed, "S:top", top, "S:top-node", topNode, "S:base", base, "S:base-node", baseNode, "S:backing-file", backingName, "T:auto-finalize", autofinalize, "T:auto-dismiss", autodismiss, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static char * qemuMonitorJSONDiskNameLookupOne(virJSONValuePtr image, virStorageSourcePtr top, virStorageSourcePtr target) { virJSONValuePtr backing; char *ret; /* The caller will report a generic message if we return NULL * without an error; but in some cases we can improve by reporting * a more specific message. */ if (!top || !image) return NULL; if (top != target) { backing = virJSONValueObjectGetObject(image, "backing-image"); return qemuMonitorJSONDiskNameLookupOne(backing, top->backingStore, target); } ret = g_strdup(virJSONValueObjectGetString(image, "filename")); /* Sanity check - the name qemu gave us should resolve to the same file tracked by our target description. */ if (virStorageSourceIsLocalStorage(target) && STRNEQ(ret, target->path) && !virFileLinkPointsTo(ret, target->path)) { virReportError(VIR_ERR_INTERNAL_ERROR, _("qemu block name '%s' doesn't match expected '%s'"), ret, target->path); VIR_FREE(ret); } return ret; } char * qemuMonitorJSONDiskNameLookup(qemuMonitorPtr mon, const char *device, virStorageSourcePtr top, virStorageSourcePtr target) { char *ret = NULL; virJSONValuePtr devices; size_t i; if (!(devices = qemuMonitorJSONQueryBlock(mon))) return NULL; for (i = 0; i < virJSONValueArraySize(devices); i++) { virJSONValuePtr dev; virJSONValuePtr inserted; virJSONValuePtr image; const char *thisdev; if (!(dev = qemuMonitorJSONGetBlockDev(devices, i))) goto cleanup; if (!(thisdev = qemuMonitorJSONGetBlockDevDevice(dev))) goto cleanup; if (STREQ(thisdev, device)) { if ((inserted = virJSONValueObjectGetObject(dev, "inserted")) && (image = virJSONValueObjectGetObject(inserted, "image"))) { ret = qemuMonitorJSONDiskNameLookupOne(image, top, target); } break; } } /* Guarantee an error when returning NULL, but don't override a * more specific error if one was already generated. */ if (!ret && virGetLastErrorCode() == VIR_ERR_OK) virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to find backing name for device %s"), device); cleanup: virJSONValueFree(devices); return ret; } int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon, const char *cmd_str, char **reply_str) { virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = virJSONValueFromString(cmd_str))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (!(*reply_str = virJSONValueToString(reply, false))) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONInjectNMI(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("inject-nmi", NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSendKey(qemuMonitorPtr mon, unsigned int holdtime, unsigned int *keycodes, unsigned int nkeycodes) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr keys = NULL; virJSONValuePtr key = NULL; size_t i; /* create the key data array */ if (!(keys = virJSONValueNewArray())) goto cleanup; for (i = 0; i < nkeycodes; i++) { if (keycodes[i] > 0xffff) { virReportError(VIR_ERR_OPERATION_FAILED, _("keycode %zu is invalid: 0x%X"), i, keycodes[i]); goto cleanup; } /* create single key object */ if (!(key = virJSONValueNewObject())) goto cleanup; /* Union KeyValue has two types, use the generic one */ if (virJSONValueObjectAppendString(key, "type", "number") < 0) goto cleanup; /* with the keycode */ if (virJSONValueObjectAppendNumberInt(key, "data", keycodes[i]) < 0) goto cleanup; if (virJSONValueArrayAppend(keys, key) < 0) goto cleanup; key = NULL; } cmd = qemuMonitorJSONMakeCommand("send-key", "a:keys", &keys, "p:hold-time", holdtime, NULL); if (!cmd) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); virJSONValueFree(keys); virJSONValueFree(key); return ret; } int qemuMonitorJSONScreendump(qemuMonitorPtr mon, const char *device, unsigned int head, const char *file) { int ret = -1; virJSONValuePtr cmd, reply = NULL; cmd = qemuMonitorJSONMakeCommand("screendump", "s:filename", file, "S:device", device, "p:head", head, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONParseBlockJobInfo(virHashTablePtr blockJobs, virJSONValuePtr entry, bool rawjobname) { qemuMonitorBlockJobInfoPtr info = NULL; const char *device; const char *type; bool ready; if (!(device = virJSONValueObjectGetString(entry, "device"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("entry was missing 'device'")); return -1; } if (!rawjobname) device = qemuAliasDiskDriveSkipPrefix(device); if (VIR_ALLOC(info) < 0 || virHashAddEntry(blockJobs, device, info) < 0) { VIR_FREE(info); return -1; } /* assume we don't know the state */ info->ready = -1; if (!(type = virJSONValueObjectGetString(entry, "type"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("entry was missing 'type'")); return -1; } if (STREQ(type, "stream")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_PULL; else if (STREQ(type, "commit")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT; else if (STREQ(type, "mirror")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_COPY; else if (STREQ(type, "backup")) info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_BACKUP; else info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN; if (virJSONValueObjectGetNumberUlong(entry, "speed", &info->bandwidth) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("entry was missing 'speed'")); return -1; } if (virJSONValueObjectGetNumberUlong(entry, "offset", &info->cur) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("entry was missing 'offset'")); return -1; } if (virJSONValueObjectGetNumberUlong(entry, "len", &info->end) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("entry was missing 'len'")); return -1; } if (virJSONValueObjectGetBoolean(entry, "ready", &ready) == 0) info->ready = ready; return 0; } virHashTablePtr qemuMonitorJSONGetAllBlockJobInfo(qemuMonitorPtr mon, bool rawjobname) { virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data; size_t nr_results; size_t i; virHashTablePtr blockJobs = NULL; cmd = qemuMonitorJSONMakeCommand("query-block-jobs", NULL); if (!cmd) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if ((data = virJSONValueObjectGetArray(reply, "return")) == NULL) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("reply was missing return data")); goto cleanup; } nr_results = virJSONValueArraySize(data); if (!(blockJobs = virHashCreate(nr_results, virHashValueFree))) goto cleanup; for (i = 0; i < nr_results; i++) { virJSONValuePtr entry = virJSONValueArrayGet(data, i); if (!entry) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing array element")); goto error; } if (qemuMonitorJSONParseBlockJobInfo(blockJobs, entry, rawjobname) < 0) goto error; } cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return blockJobs; error: virHashFree(blockJobs); blockJobs = NULL; goto cleanup; } static int qemuMonitorJSONBlockJobError(virJSONValuePtr cmd, virJSONValuePtr reply, const char *jobname) { virJSONValuePtr error; if ((error = virJSONValueObjectGet(reply, "error")) && (qemuMonitorJSONErrorIsClass(error, "DeviceNotActive"))) { virReportError(VIR_ERR_OPERATION_INVALID, _("No active block job '%s'"), jobname); return -1; } return qemuMonitorJSONCheckError(cmd, reply); } /* speed is in bytes/sec */ int qemuMonitorJSONBlockStream(qemuMonitorPtr mon, const char *device, const char *jobname, bool persistjob, const char *base, const char *baseNode, const char *backingName, unsigned long long speed) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virTristateBool autofinalize = VIR_TRISTATE_BOOL_ABSENT; virTristateBool autodismiss = VIR_TRISTATE_BOOL_ABSENT; if (persistjob) { autofinalize = VIR_TRISTATE_BOOL_YES; autodismiss = VIR_TRISTATE_BOOL_NO; } if (!(cmd = qemuMonitorJSONMakeCommand("block-stream", "s:device", device, "S:job-id", jobname, "Y:speed", speed, "S:base", base, "S:base-node", baseNode, "S:backing-file", backingName, "T:auto-finalize", autofinalize, "T:auto-dismiss", autodismiss, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockJobCancel(qemuMonitorPtr mon, const char *jobname) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("block-job-cancel", "s:device", jobname, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockJobSetSpeed(qemuMonitorPtr mon, const char *jobname, unsigned long long speed) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("block-job-set-speed", "s:device", jobname, "J:speed", speed, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *jobname) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("block-job-complete", "s:device", jobname, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONJobDismiss(qemuMonitorPtr mon, const char *jobname) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("job-dismiss", "s:id", jobname, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) return -1; return 0; } int qemuMonitorJSONJobCancel(qemuMonitorPtr mon, const char *jobname, bool quiet) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("job-cancel", "s:id", jobname, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (quiet) { if (virJSONValueObjectHasKey(reply, "error") != 0) return -1; } else { if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) return -1; } return 0; } int qemuMonitorJSONJobComplete(qemuMonitorPtr mon, const char *jobname) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("job-complete", "s:id", jobname, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONBlockJobError(cmd, reply, jobname) < 0) return -1; return 0; } int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon, const char *protocol, const char *fdname, bool skipauth) { int ret = -1; virJSONValuePtr cmd, reply = NULL; cmd = qemuMonitorJSONMakeCommand("add_client", "s:protocol", protocol, "s:fdname", fdname, "b:skipauth", skipauth, NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } #define GET_THROTTLE_STATS_OPTIONAL(FIELD, STORE) \ if (virJSONValueObjectGetNumberUlong(inserted, \ FIELD, \ &reply->STORE) < 0) { \ reply->STORE = 0; \ } #define GET_THROTTLE_STATS(FIELD, STORE) \ if (virJSONValueObjectGetNumberUlong(inserted, \ FIELD, \ &reply->STORE) < 0) { \ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, \ _("block_io_throttle field '%s' missing " \ "in qemu's output"), \ #STORE); \ return -1; \ } static int qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr io_throttle, const char *drivealias, const char *qdevid, virDomainBlockIoTuneInfoPtr reply) { size_t i; bool found = false; for (i = 0; i < virJSONValueArraySize(io_throttle); i++) { virJSONValuePtr temp_dev = virJSONValueArrayGet(io_throttle, i); virJSONValuePtr inserted; const char *current_drive; const char *current_qdev; if (!temp_dev || virJSONValueGetType(temp_dev) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("block_io_throttle device entry " "was not in expected format")); return -1; } current_qdev = virJSONValueObjectGetString(temp_dev, "qdev"); current_drive = virJSONValueObjectGetString(temp_dev, "device"); if (!current_drive && !current_qdev) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("block_io_throttle device entry " "was not in expected format")); return -1; } if ((drivealias && current_drive && STRNEQ(current_drive, drivealias)) || (qdevid && current_qdev && STRNEQ(current_qdev, qdevid))) continue; found = true; if (!(inserted = virJSONValueObjectGetObject(temp_dev, "inserted"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("block_io_throttle inserted entry " "was not in expected format")); return -1; } GET_THROTTLE_STATS("bps", total_bytes_sec); GET_THROTTLE_STATS("bps_rd", read_bytes_sec); GET_THROTTLE_STATS("bps_wr", write_bytes_sec); GET_THROTTLE_STATS("iops", total_iops_sec); GET_THROTTLE_STATS("iops_rd", read_iops_sec); GET_THROTTLE_STATS("iops_wr", write_iops_sec); GET_THROTTLE_STATS_OPTIONAL("bps_max", total_bytes_sec_max); GET_THROTTLE_STATS_OPTIONAL("bps_rd_max", read_bytes_sec_max); GET_THROTTLE_STATS_OPTIONAL("bps_wr_max", write_bytes_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_max", total_iops_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_rd_max", read_iops_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_wr_max", write_iops_sec_max); GET_THROTTLE_STATS_OPTIONAL("iops_size", size_iops_sec); reply->group_name = g_strdup(virJSONValueObjectGetString(inserted, "group")); GET_THROTTLE_STATS_OPTIONAL("bps_max_length", total_bytes_sec_max_length); GET_THROTTLE_STATS_OPTIONAL("bps_rd_max_length", read_bytes_sec_max_length); GET_THROTTLE_STATS_OPTIONAL("bps_wr_max_length", write_bytes_sec_max_length); GET_THROTTLE_STATS_OPTIONAL("iops_max_length", total_iops_sec_max_length); GET_THROTTLE_STATS_OPTIONAL("iops_rd_max_length", read_iops_sec_max_length); GET_THROTTLE_STATS_OPTIONAL("iops_wr_max_length", write_iops_sec_max_length); break; } if (!found) { virReportError(VIR_ERR_INTERNAL_ERROR, _("cannot find throttling info for device '%s'"), drivealias ? drivealias : qdevid); return -1; } return 0; } #undef GET_THROTTLE_STATS #undef GET_THROTTLE_STATS_OPTIONAL int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon, const char *drivealias, const char *qomid, virDomainBlockIoTuneInfoPtr info, bool supportMaxOptions, bool supportGroupNameOption, bool supportMaxLengthOptions) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr result = NULL; virJSONValuePtr args = NULL; const char *errdev = drivealias; if (!errdev) errdev = qomid; if (!(cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle", NULL))) return -1; if (virJSONValueObjectCreate(&args, "S:device", drivealias, "S:id", qomid, "U:bps", info->total_bytes_sec, "U:bps_rd", info->read_bytes_sec, "U:bps_wr", info->write_bytes_sec, "U:iops", info->total_iops_sec, "U:iops_rd", info->read_iops_sec, "U:iops_wr", info->write_iops_sec, NULL) < 0) goto cleanup; if (supportMaxOptions && virJSONValueObjectAdd(args, "U:bps_max", info->total_bytes_sec_max, "U:bps_rd_max", info->read_bytes_sec_max, "U:bps_wr_max", info->write_bytes_sec_max, "U:iops_max", info->total_iops_sec_max, "U:iops_rd_max", info->read_iops_sec_max, "U:iops_wr_max", info->write_iops_sec_max, "U:iops_size", info->size_iops_sec, NULL) < 0) goto cleanup; if (supportGroupNameOption && virJSONValueObjectAdd(args, "S:group", info->group_name, NULL) < 0) goto cleanup; if (supportMaxLengthOptions && virJSONValueObjectAdd(args, "P:bps_max_length", info->total_bytes_sec_max_length, "P:bps_rd_max_length", info->read_bytes_sec_max_length, "P:bps_wr_max_length", info->write_bytes_sec_max_length, "P:iops_max_length", info->total_iops_sec_max_length, "P:iops_rd_max_length", info->read_iops_sec_max_length, "P:iops_wr_max_length", info->write_iops_sec_max_length, NULL) < 0) goto cleanup; if (virJSONValueObjectAppend(cmd, "arguments", args) < 0) goto cleanup; args = NULL; /* obj owns reference to args now */ if (qemuMonitorJSONCommand(mon, cmd, &result) < 0) goto cleanup; if (virJSONValueObjectHasKey(result, "error")) { if (qemuMonitorJSONHasError(result, "DeviceNotActive")) { virReportError(VIR_ERR_OPERATION_INVALID, _("No active operation on device: %s"), errdev); } else if (qemuMonitorJSONHasError(result, "NotSupported")) { virReportError(VIR_ERR_OPERATION_INVALID, _("Operation is not supported for device: %s"), errdev); } else { virJSONValuePtr error = virJSONValueObjectGet(result, "error"); virReportError(VIR_ERR_INTERNAL_ERROR, _("unable to execute '%s', unexpected error: '%s'"), qemuMonitorJSONCommandName(cmd), qemuMonitorJSONStringifyError(error)); } goto cleanup; } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(result); virJSONValueFree(args); return ret; } int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon, const char *drivealias, const char *qdevid, virDomainBlockIoTuneInfoPtr reply) { int ret = -1; virJSONValuePtr devices = NULL; if (!(devices = qemuMonitorJSONQueryBlock(mon))) return -1; ret = qemuMonitorJSONBlockIoThrottleInfo(devices, drivealias, qdevid, reply); virJSONValueFree(devices); return ret; } int qemuMonitorJSONSystemWakeup(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("system_wakeup", NULL); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetVersion(qemuMonitorPtr mon, int *major, int *minor, int *micro, char **package) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; virJSONValuePtr qemu; *major = *minor = *micro = 0; if (package) *package = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-version", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (!(qemu = virJSONValueObjectGetObject(data, "qemu"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-version reply was missing 'qemu' data")); goto cleanup; } if (virJSONValueObjectGetNumberInt(qemu, "major", major) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-version reply was missing 'major' version")); goto cleanup; } if (virJSONValueObjectGetNumberInt(qemu, "minor", minor) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-version reply was missing 'minor' version")); goto cleanup; } if (virJSONValueObjectGetNumberInt(qemu, "micro", micro) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-version reply was missing 'micro' version")); goto cleanup; } if (package) { const char *tmp; if (!(tmp = virJSONValueObjectGetString(data, "package"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-version reply was missing 'package' version")); goto cleanup; } *package = g_strdup(tmp); } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetMachines(qemuMonitorPtr mon, qemuMonitorMachineInfoPtr **machines) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; qemuMonitorMachineInfoPtr *infolist = NULL; size_t n = 0; size_t i; *machines = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-machines", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(infolist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; qemuMonitorMachineInfoPtr info; if (VIR_ALLOC(info) < 0) goto cleanup; infolist[i] = info; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-machines reply data was missing 'name'")); goto cleanup; } info->name = g_strdup(tmp); if (virJSONValueObjectHasKey(child, "is-default") && virJSONValueObjectGetBoolean(child, "is-default", &info->isDefault) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-machines reply has malformed 'is-default' data")); goto cleanup; } if (virJSONValueObjectHasKey(child, "alias")) { if (!(tmp = virJSONValueObjectGetString(child, "alias"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-machines reply has malformed 'alias' data")); goto cleanup; } info->alias = g_strdup(tmp); } if (virJSONValueObjectHasKey(child, "cpu-max") && virJSONValueObjectGetNumberUint(child, "cpu-max", &info->maxCpus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-machines reply has malformed 'cpu-max' data")); goto cleanup; } ignore_value(virJSONValueObjectGetBoolean(child, "hotpluggable-cpus", &info->hotplugCpus)); if (virJSONValueObjectHasKey(child, "default-cpu-type")) { if (!(tmp = virJSONValueObjectGetString(child, "default-cpu-type"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-machines reply has malformed " "'default-cpu-type' data")); goto cleanup; } info->defaultCPU = g_strdup(tmp); } } ret = n; *machines = infolist; infolist = NULL; cleanup: if (infolist) { for (i = 0; i < n; i++) qemuMonitorMachineInfoFree(infolist[i]); VIR_FREE(infolist); } virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon, qemuMonitorCPUDefsPtr *cpuDefs) { g_autoptr(qemuMonitorCPUDefs) defs = NULL; g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; virJSONValuePtr data; size_t ncpus; size_t i; *cpuDefs = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-definitions", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; /* Urgh, some QEMU architectures have the query-cpu-definitions * command, but return 'GenericError' with string "Not supported", * instead of simply omitting the command entirely :-( */ if (qemuMonitorJSONHasError(reply, "GenericError")) return 0; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) return -1; data = virJSONValueObjectGetArray(reply, "return"); ncpus = virJSONValueArraySize(data); if (!(defs = qemuMonitorCPUDefsNew(ncpus))) return -1; for (i = 0; i < defs->ncpus; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; qemuMonitorCPUDefInfoPtr cpu = defs->cpus + i; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-cpu-definitions reply data was missing 'name'")); return -1; } cpu->name = g_strdup(tmp); if ((tmp = virJSONValueObjectGetString(child, "typename")) && *tmp) cpu->type = g_strdup(tmp); if (virJSONValueObjectHasKey(child, "unavailable-features")) { virJSONValuePtr blockers; size_t j; size_t len; blockers = virJSONValueObjectGetArray(child, "unavailable-features"); if (!blockers) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unavailable-features in query-cpu-definitions " "reply data was not an array")); return -1; } len = virJSONValueArraySize(blockers); if (len == 0) { cpu->usable = VIR_DOMCAPS_CPU_USABLE_YES; continue; } cpu->usable = VIR_DOMCAPS_CPU_USABLE_NO; if (VIR_ALLOC_N(cpu->blockers, len + 1) < 0) return -1; for (j = 0; j < len; j++) { virJSONValuePtr blocker = virJSONValueArrayGet(blockers, j); if (virJSONValueGetType(blocker) != VIR_JSON_TYPE_STRING) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unexpected value in unavailable-features " "array")); return -1; } cpu->blockers[j] = g_strdup(virJSONValueGetString(blocker)); } } } *cpuDefs = g_steal_pointer(&defs); return 0; } VIR_ENUM_IMPL(qemuMonitorCPUProperty, QEMU_MONITOR_CPU_PROPERTY_LAST, "boolean", "string", "number", ); static int qemuMonitorJSONParseCPUModelProperty(const char *key, virJSONValue *value, void *opaque) { qemuMonitorCPUModelInfoPtr machine_model = opaque; qemuMonitorCPUPropertyPtr prop; prop = machine_model->props + machine_model->nprops; switch ((virJSONType)virJSONValueGetType(value)) { case VIR_JSON_TYPE_STRING: prop->value.string = g_strdup(virJSONValueGetString(value)); prop->type = QEMU_MONITOR_CPU_PROPERTY_STRING; break; case VIR_JSON_TYPE_NUMBER: /* Ignore numbers which cannot be parsed as unsigned long long */ if (virJSONValueGetNumberLong(value, &prop->value.number) < 0) return 0; prop->type = QEMU_MONITOR_CPU_PROPERTY_NUMBER; break; case VIR_JSON_TYPE_BOOLEAN: virJSONValueGetBoolean(value, &prop->value.boolean); prop->type = QEMU_MONITOR_CPU_PROPERTY_BOOLEAN; break; case VIR_JSON_TYPE_OBJECT: case VIR_JSON_TYPE_ARRAY: case VIR_JSON_TYPE_NULL: return 0; } machine_model->nprops++; prop->name = g_strdup(key); return 0; } static virJSONValuePtr qemuMonitorJSONMakeCPUModel(virCPUDefPtr cpu, bool migratable) { virJSONValuePtr model = NULL; virJSONValuePtr props = NULL; size_t i; if (!(model = virJSONValueNewObject())) goto error; if (virJSONValueObjectAppendString(model, "name", cpu->model) < 0) goto error; if (cpu->nfeatures || !migratable) { if (!(props = virJSONValueNewObject())) goto error; for (i = 0; i < cpu->nfeatures; i++) { char *name = cpu->features[i].name; bool enabled = false; /* policy may be reported as -1 if the CPU def is a host model */ if (cpu->features[i].policy == VIR_CPU_FEATURE_REQUIRE || cpu->features[i].policy == VIR_CPU_FEATURE_FORCE || cpu->features[i].policy == -1) enabled = true; if (virJSONValueObjectAppendBoolean(props, name, enabled) < 0) goto error; } if (!migratable && virJSONValueObjectAppendBoolean(props, "migratable", false) < 0) { goto error; } if (virJSONValueObjectAppend(model, "props", props) < 0) goto error; } return model; error: virJSONValueFree(model); virJSONValueFree(props); return NULL; } static int qemuMonitorJSONParseCPUModelData(virJSONValuePtr data, const char *cmd_name, bool fail_no_props, virJSONValuePtr *cpu_model, virJSONValuePtr *cpu_props, const char **cpu_name) { if (!(*cpu_model = virJSONValueObjectGetObject(data, "model"))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("%s reply data was missing 'model'"), cmd_name); return -1; } if (!(*cpu_name = virJSONValueObjectGetString(*cpu_model, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("%s reply data was missing 'name'"), cmd_name); return -1; } if (!(*cpu_props = virJSONValueObjectGetObject(*cpu_model, "props")) && fail_no_props) { virReportError(VIR_ERR_INTERNAL_ERROR, _("%s reply data was missing 'props'"), cmd_name); return -1; } return 0; } static int qemuMonitorJSONParseCPUModel(const char *cpu_name, virJSONValuePtr cpu_props, qemuMonitorCPUModelInfoPtr *model_info) { qemuMonitorCPUModelInfoPtr machine_model = NULL; int ret = -1; if (VIR_ALLOC(machine_model) < 0) goto cleanup; machine_model->name = g_strdup(cpu_name); if (cpu_props) { size_t nprops = virJSONValueObjectKeysNumber(cpu_props); if (VIR_ALLOC_N(machine_model->props, nprops) < 0) goto cleanup; if (virJSONValueObjectForeachKeyValue(cpu_props, qemuMonitorJSONParseCPUModelProperty, machine_model) < 0) goto cleanup; } *model_info = g_steal_pointer(&machine_model); ret = 0; cleanup: qemuMonitorCPUModelInfoFree(machine_model); return ret; } int qemuMonitorJSONGetCPUModelExpansion(qemuMonitorPtr mon, qemuMonitorCPUModelExpansionType type, virCPUDefPtr cpu, bool migratable, bool fail_no_props, qemuMonitorCPUModelInfoPtr *model_info) { g_autoptr(virJSONValue) model = NULL; g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; virJSONValuePtr data; virJSONValuePtr cpu_model; virJSONValuePtr cpu_props = NULL; const char *cpu_name = ""; const char *typeStr = ""; *model_info = NULL; if (!(model = qemuMonitorJSONMakeCPUModel(cpu, migratable))) return -1; retry: switch (type) { case QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC: case QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL: typeStr = "static"; break; case QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL: typeStr = "full"; break; } if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-expansion", "s:type", typeStr, "a:model", &model, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; /* Even though query-cpu-model-expansion is advertised by query-commands it * may just return GenericError if it is not implemented for the requested * guest architecture or it is not supported in the host environment. */ if (qemuMonitorJSONHasError(reply, "GenericError")) return 0; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) return -1; data = virJSONValueObjectGetObject(reply, "return"); if (qemuMonitorJSONParseCPUModelData(data, "query-cpu-model-expansion", fail_no_props, &cpu_model, &cpu_props, &cpu_name) < 0) return -1; /* QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL requests "full" expansion * on the result of the initial "static" expansion. */ if (type == QEMU_MONITOR_CPU_MODEL_EXPANSION_STATIC_FULL) { if (!(model = virJSONValueCopy(cpu_model))) return -1; virJSONValueFree(cmd); virJSONValueFree(reply); type = QEMU_MONITOR_CPU_MODEL_EXPANSION_FULL; goto retry; } return qemuMonitorJSONParseCPUModel(cpu_name, cpu_props, model_info); } int qemuMonitorJSONGetCPUModelBaseline(qemuMonitorPtr mon, virCPUDefPtr cpu_a, virCPUDefPtr cpu_b, qemuMonitorCPUModelInfoPtr *baseline) { g_autoptr(virJSONValue) model_a = NULL; g_autoptr(virJSONValue) model_b = NULL; g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; virJSONValuePtr data; virJSONValuePtr cpu_model; virJSONValuePtr cpu_props = NULL; const char *cpu_name = ""; if (!(model_a = qemuMonitorJSONMakeCPUModel(cpu_a, true)) || !(model_b = qemuMonitorJSONMakeCPUModel(cpu_b, true))) return -1; if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-baseline", "a:modela", &model_a, "a:modelb", &model_b, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) return -1; data = virJSONValueObjectGetObject(reply, "return"); if (qemuMonitorJSONParseCPUModelData(data, "query-cpu-model-baseline", false, &cpu_model, &cpu_props, &cpu_name) < 0) return -1; return qemuMonitorJSONParseCPUModel(cpu_name, cpu_props, baseline); } int qemuMonitorJSONGetCPUModelComparison(qemuMonitorPtr mon, virCPUDefPtr cpu_a, virCPUDefPtr cpu_b, char **result) { g_autoptr(virJSONValue) model_a = NULL; g_autoptr(virJSONValue) model_b = NULL; g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; const char *data_result; virJSONValuePtr data; if (!(model_a = qemuMonitorJSONMakeCPUModel(cpu_a, true)) || !(model_b = qemuMonitorJSONMakeCPUModel(cpu_b, true))) return -1; if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-model-comparison", "a:modela", &model_a, "a:modelb", &model_b, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONCheckError(cmd, reply) < 0) return -1; data = virJSONValueObjectGetObject(reply, "return"); if (!(data_result = virJSONValueObjectGetString(data, "result"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-cpu-model-comparison reply data was missing " "'result'")); return -1; } *result = g_strdup(data_result); return 0; } int qemuMonitorJSONGetCommands(qemuMonitorPtr mon, char ***commands) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; char **commandlist = NULL; size_t n = 0; size_t i; *commands = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-commands", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(commandlist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-commands reply data was missing 'name'")); goto cleanup; } commandlist[i] = g_strdup(tmp); } ret = n; *commands = commandlist; commandlist = NULL; cleanup: virStringListFree(commandlist); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetEvents(qemuMonitorPtr mon, char ***events) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; char **eventlist = NULL; size_t n = 0; size_t i; *events = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-events", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(eventlist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-events reply data was missing 'name'")); goto cleanup; } eventlist[i] = g_strdup(tmp); } ret = n; *events = eventlist; eventlist = NULL; cleanup: virStringListFree(eventlist); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon, const char *option, char ***params, bool *found) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data = NULL; virJSONValuePtr array = NULL; char **paramlist = NULL; size_t n = 0; size_t i; *params = NULL; if (found) *found = false; /* query-command-line-options has fixed output for a given qemu * binary; but since callers want to query parameters for one * option at a time, we cache the option list from qemu. */ if (!(array = qemuMonitorGetOptions(mon))) { if (!(cmd = qemuMonitorJSONMakeCommand("query-command-line-options", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; if (virJSONValueObjectRemoveKey(reply, "return", &array) <= 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-command-line-options reply was missing " "return data")); goto cleanup; } if (!virJSONValueIsArray(array)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed query-command-line-options array")); goto cleanup; } qemuMonitorSetOptions(mon, array); } for (i = 0; i < virJSONValueArraySize(array); i++) { virJSONValuePtr child = virJSONValueArrayGet(array, i); const char *tmp; if (!(tmp = virJSONValueObjectGetString(child, "option"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-command-line-options reply data was " "missing 'option'")); goto cleanup; } if (STREQ(tmp, option)) { data = virJSONValueObjectGet(child, "parameters"); break; } } if (!data) { /* Option not found; return 0 parameters rather than an error. */ ret = 0; goto cleanup; } if (found) *found = true; if (!virJSONValueIsArray(data)) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Malformed query-command-line-options parameters array")); goto cleanup; } n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(paramlist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-command-line-options parameter data was " "missing 'name'")); goto cleanup; } paramlist[i] = g_strdup(tmp); } ret = n; *params = paramlist; paramlist = NULL; cleanup: /* If we failed before getting the JSON array of options, we (try) * to cache an empty array to speed up the next failure. */ if (!qemuMonitorGetOptions(mon)) qemuMonitorSetOptions(mon, virJSONValueNewArray()); virStringListFree(paramlist); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon, bool *enabled, bool *present) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data = NULL; /* Safe defaults */ *enabled = *present = false; if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (virJSONValueObjectGetBoolean(data, "enabled", enabled) < 0 || virJSONValueObjectGetBoolean(data, "present", present) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-kvm replied unexpected data")); goto cleanup; } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetObjectTypes(qemuMonitorPtr mon, char ***types) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; char **typelist = NULL; size_t n = 0; size_t i; *types = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-types", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(typelist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("qom-list-types reply data was missing 'name'")); goto cleanup; } typelist[i] = g_strdup(tmp); } ret = n; *types = typelist; typelist = NULL; cleanup: virStringListFree(typelist); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetObjectListPaths(qemuMonitorPtr mon, const char *path, qemuMonitorJSONListPathPtr **paths) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; qemuMonitorJSONListPathPtr *pathlist = NULL; size_t n = 0; size_t i; *paths = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("qom-list", "s:path", path, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(pathlist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; qemuMonitorJSONListPathPtr info; if (VIR_ALLOC(info) < 0) goto cleanup; pathlist[i] = info; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("qom-list reply data was missing 'name'")); goto cleanup; } info->name = g_strdup(tmp); if (virJSONValueObjectHasKey(child, "type")) { if (!(tmp = virJSONValueObjectGetString(child, "type"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("qom-list reply has malformed 'type' data")); goto cleanup; } info->type = g_strdup(tmp); } } ret = n; *paths = pathlist; pathlist = NULL; cleanup: if (pathlist) { for (i = 0; i < n; i++) qemuMonitorJSONListPathFree(pathlist[i]); VIR_FREE(pathlist); } virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } void qemuMonitorJSONListPathFree(qemuMonitorJSONListPathPtr paths) { if (!paths) return; VIR_FREE(paths->name); VIR_FREE(paths->type); VIR_FREE(paths); } int qemuMonitorJSONGetObjectProperty(qemuMonitorPtr mon, const char *path, const char *property, qemuMonitorJSONObjectPropertyPtr prop) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; const char *tmp; if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", "s:path", path, "s:property", property, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; data = virJSONValueObjectGet(reply, "return"); switch ((qemuMonitorJSONObjectPropertyType) prop->type) { /* Simple cases of boolean, int, long, uint, ulong, double, and string * will receive return value as part of {"return": xxx} statement */ case QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN: ret = virJSONValueGetBoolean(data, &prop->val.b); break; case QEMU_MONITOR_OBJECT_PROPERTY_INT: ret = virJSONValueGetNumberInt(data, &prop->val.iv); break; case QEMU_MONITOR_OBJECT_PROPERTY_LONG: ret = virJSONValueGetNumberLong(data, &prop->val.l); break; case QEMU_MONITOR_OBJECT_PROPERTY_UINT: ret = virJSONValueGetNumberUint(data, &prop->val.ui); break; case QEMU_MONITOR_OBJECT_PROPERTY_ULONG: ret = virJSONValueGetNumberUlong(data, &prop->val.ul); break; case QEMU_MONITOR_OBJECT_PROPERTY_DOUBLE: ret = virJSONValueGetNumberDouble(data, &prop->val.d); break; case QEMU_MONITOR_OBJECT_PROPERTY_STRING: tmp = virJSONValueGetString(data); if (tmp) prop->val.str = g_strdup(tmp); if (tmp) ret = 0; break; case QEMU_MONITOR_OBJECT_PROPERTY_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("qom-get invalid object property type %d"), prop->type); goto cleanup; break; } if (ret == -1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("qom-get reply was missing return data")); goto cleanup; } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONGetStringListProperty(qemuMonitorPtr mon, const char *path, const char *property, char ***strList) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; VIR_AUTOSTRINGLIST list = NULL; virJSONValuePtr data; size_t n; size_t i; *strList = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", "s:path", path, "s:property", property, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) return -1; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); if (VIR_ALLOC_N(list, n + 1) < 0) return -1; for (i = 0; i < n; i++) { virJSONValuePtr item = virJSONValueArrayGet(data, i); if (virJSONValueGetType(item) != VIR_JSON_TYPE_STRING) { virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected value in %s array"), property); return -1; } list[i] = g_strdup(virJSONValueGetString(item)); } *strList = g_steal_pointer(&list); return n; } #define MAKE_SET_CMD(STRING, VALUE) \ cmd = qemuMonitorJSONMakeCommand("qom-set", \ "s:path", path, \ "s:property", property, \ STRING, VALUE, \ NULL) int qemuMonitorJSONSetObjectProperty(qemuMonitorPtr mon, const char *path, const char *property, qemuMonitorJSONObjectPropertyPtr prop) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; switch ((qemuMonitorJSONObjectPropertyType) prop->type) { /* Simple cases of boolean, int, long, uint, ulong, double, and string * will receive return value as part of {"return": xxx} statement */ case QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN: MAKE_SET_CMD("b:value", prop->val.b); break; case QEMU_MONITOR_OBJECT_PROPERTY_INT: MAKE_SET_CMD("i:value", prop->val.iv); break; case QEMU_MONITOR_OBJECT_PROPERTY_LONG: MAKE_SET_CMD("I:value", prop->val.l); break; case QEMU_MONITOR_OBJECT_PROPERTY_UINT: MAKE_SET_CMD("u:value", prop->val.ui); break; case QEMU_MONITOR_OBJECT_PROPERTY_ULONG: MAKE_SET_CMD("U:value", prop->val.ul); break; case QEMU_MONITOR_OBJECT_PROPERTY_DOUBLE: MAKE_SET_CMD("d:value", prop->val.d); break; case QEMU_MONITOR_OBJECT_PROPERTY_STRING: MAKE_SET_CMD("s:value", prop->val.str); break; case QEMU_MONITOR_OBJECT_PROPERTY_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, _("qom-set invalid object property type %d"), prop->type); goto cleanup; } if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } #undef MAKE_SET_CMD static int qemuMonitorJSONParsePropsList(virJSONValuePtr cmd, virJSONValuePtr reply, const char *type, char ***props) { virJSONValuePtr data; char **proplist = NULL; size_t n = 0; size_t count = 0; size_t i; int ret = -1; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(proplist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (type && STRNEQ_NULLABLE(virJSONValueObjectGetString(child, "type"), type)) continue; if (!(tmp = virJSONValueObjectGetString(child, "name"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("reply data was missing 'name'")); goto cleanup; } proplist[count++] = g_strdup(tmp); } ret = count; *props = proplist; proplist = NULL; cleanup: virStringListFree(proplist); return ret; } int qemuMonitorJSONGetDeviceProps(qemuMonitorPtr mon, const char *device, char ***props) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; *props = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("device-list-properties", "s:typename", device, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) { ret = 0; goto cleanup; } ret = qemuMonitorJSONParsePropsList(cmd, reply, NULL, props); cleanup: virJSONValueFree(reply); virJSONValueFree(cmd); return ret; } int qemuMonitorJSONGetObjectProps(qemuMonitorPtr mon, const char *object, char ***props) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; *props = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-properties", "s:typename", object, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) { ret = 0; goto cleanup; } ret = qemuMonitorJSONParsePropsList(cmd, reply, NULL, props); cleanup: virJSONValueFree(reply); virJSONValueFree(cmd); return ret; } char * qemuMonitorJSONGetTargetArch(qemuMonitorPtr mon) { char *ret = NULL; const char *arch; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; if (!(cmd = qemuMonitorJSONMakeCommand("query-target", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (!(arch = virJSONValueObjectGetString(data, "arch"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-target reply was missing arch data")); goto cleanup; } ret = g_strdup(arch); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetMigrationCapabilities(qemuMonitorPtr mon, char ***capabilities) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr caps; char **list = NULL; size_t i; size_t n; *capabilities = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; caps = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(caps); if (VIR_ALLOC_N(list, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr cap = virJSONValueArrayGet(caps, i); const char *name; if (!cap || virJSONValueGetType(cap) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing entry in migration capabilities list")); goto cleanup; } if (!(name = virJSONValueObjectGetString(cap, "capability"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing migration capability name")); goto cleanup; } list[i] = g_strdup(name); } ret = n; *capabilities = list; list = NULL; cleanup: virStringListFree(list); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetMigrationCapabilities(qemuMonitorPtr mon, virJSONValuePtr caps) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities", "a:capabilities", &caps, NULL); if (!cmd) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(caps); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * qemuMonitorJSONGetGICCapabilities: * @mon: QEMU JSON monitor * @capabilities: where to store the GIC capabilities * * Use @mon to obtain information about the GIC capabilities for the * corresponding QEMU binary, and store them in @capabilities. * * If the QEMU binary has no GIC capabilities, or if GIC capabilities could * not be determined due to the lack of 'query-gic-capabilities' QMP command, * a NULL pointer will be returned instead of an empty array. * * Returns: the number of GIC capabilities obtained from the monitor, * <0 on failure */ int qemuMonitorJSONGetGICCapabilities(qemuMonitorPtr mon, virGICCapability **capabilities) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr caps; virGICCapability *list = NULL; size_t i; size_t n; *capabilities = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-gic-capabilities", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; /* If the 'query-gic-capabilities' QMP command was not available * we simply successfully return zero capabilities. * This is the case for QEMU <2.6 and all non-ARM architectures */ if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; caps = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(caps); /* If the returned array was empty we have to return successfully */ if (n == 0) { ret = 0; goto cleanup; } if (VIR_ALLOC_N(list, n) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr cap = virJSONValueArrayGet(caps, i); int version; bool kernel; bool emulated; if (!cap || virJSONValueGetType(cap) != VIR_JSON_TYPE_OBJECT) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing entry in GIC capabilities list")); goto cleanup; } if (virJSONValueObjectGetNumberInt(cap, "version", &version) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing GIC version")); goto cleanup; } if (virJSONValueObjectGetBoolean(cap, "kernel", &kernel) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing in-kernel GIC information")); goto cleanup; } if (virJSONValueObjectGetBoolean(cap, "emulated", &emulated) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing emulated GIC information")); goto cleanup; } list[i].version = version; if (kernel) list[i].implementation |= VIR_GIC_IMPLEMENTATION_KERNEL; if (emulated) list[i].implementation |= VIR_GIC_IMPLEMENTATION_EMULATED; } ret = n; *capabilities = list; list = NULL; cleanup: VIR_FREE(list); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * qemuMonitorJSONGetSEVCapabilities: * @mon: qemu monitor object * @capabilities: pointer to pointer to a SEV capability structure to be filled * * This function queries and fills in AMD's SEV platform-specific data. * Note that from QEMU's POV both -object sev-guest and query-sev-capabilities * can be present even if SEV is not available, which basically leaves us with * checking for JSON "GenericError" in order to differentiate between * compiled-in support and actual SEV support on the platform. * * Returns -1 on error, 0 if SEV is not supported, and 1 if SEV is supported on * the platform. */ int qemuMonitorJSONGetSEVCapabilities(qemuMonitorPtr mon, virSEVCapability **capabilities) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr caps; const char *pdh = NULL; const char *cert_chain = NULL; unsigned int cbitpos; unsigned int reduced_phys_bits; g_autoptr(virSEVCapability) capability = NULL; *capabilities = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-capabilities", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; /* QEMU has only compiled-in support of SEV */ if (qemuMonitorJSONHasError(reply, "GenericError")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; caps = virJSONValueObjectGetObject(reply, "return"); if (virJSONValueObjectGetNumberUint(caps, "cbitpos", &cbitpos) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-sev-capabilities reply was missing" " 'cbitpos' field")); goto cleanup; } if (virJSONValueObjectGetNumberUint(caps, "reduced-phys-bits", &reduced_phys_bits) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-sev-capabilities reply was missing" " 'reduced-phys-bits' field")); goto cleanup; } if (!(pdh = virJSONValueObjectGetString(caps, "pdh"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-sev-capabilities reply was missing" " 'pdh' field")); goto cleanup; } if (!(cert_chain = virJSONValueObjectGetString(caps, "cert-chain"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-sev-capabilities reply was missing" " 'cert-chain' field")); goto cleanup; } if (VIR_ALLOC(capability) < 0) goto cleanup; capability->pdh = g_strdup(pdh); capability->cert_chain = g_strdup(cert_chain); capability->cbitpos = cbitpos; capability->reduced_phys_bits = reduced_phys_bits; *capabilities = g_steal_pointer(&capability); ret = 1; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static virJSONValuePtr qemuMonitorJSONBuildInetSocketAddress(const char *host, const char *port) { virJSONValuePtr addr = NULL; virJSONValuePtr data = NULL; if (virJSONValueObjectCreate(&data, "s:host", host, "s:port", port, NULL) < 0) return NULL; if (virJSONValueObjectCreate(&addr, "s:type", "inet", "a:data", &data, NULL) < 0) { virJSONValueFree(data); return NULL; } return addr; } static virJSONValuePtr qemuMonitorJSONBuildUnixSocketAddress(const char *path) { virJSONValuePtr addr = NULL; virJSONValuePtr data = NULL; if (virJSONValueObjectCreate(&data, "s:path", path, NULL) < 0) return NULL; if (virJSONValueObjectCreate(&addr, "s:type", "unix", "a:data", &data, NULL) < 0) { virJSONValueFree(data); return NULL; } return addr; } int qemuMonitorJSONNBDServerStart(qemuMonitorPtr mon, const virStorageNetHostDef *server, const char *tls_alias) { int ret = -1; virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr addr = NULL; char *port_str = NULL; switch ((virStorageNetHostTransport)server->transport) { case VIR_STORAGE_NET_HOST_TRANS_TCP: port_str = g_strdup_printf("%u", server->port); addr = qemuMonitorJSONBuildInetSocketAddress(server->name, port_str); break; case VIR_STORAGE_NET_HOST_TRANS_UNIX: addr = qemuMonitorJSONBuildUnixSocketAddress(server->socket); break; case VIR_STORAGE_NET_HOST_TRANS_RDMA: case VIR_STORAGE_NET_HOST_TRANS_LAST: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid server address")); goto cleanup; } if (!addr) goto cleanup; if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-start", "a:addr", &addr, "S:tls-creds", tls_alias, NULL))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: VIR_FREE(port_str); virJSONValueFree(reply); virJSONValueFree(cmd); virJSONValueFree(addr); return ret; } int qemuMonitorJSONNBDServerAdd(qemuMonitorPtr mon, const char *deviceID, const char *export, bool writable, const char *bitmap) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; /* Note: bitmap must be NULL if QEMU_CAPS_NBD_BITMAP is lacking */ if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-add", "s:device", deviceID, "S:name", export, "b:writable", writable, "S:bitmap", bitmap, NULL))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONNBDServerStop(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-stop", NULL))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONGetStringArray(qemuMonitorPtr mon, const char *qmpCmd, char ***array) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; char **list = NULL; size_t n = 0; size_t i; *array = NULL; if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = 0; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(list, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; if (!(tmp = virJSONValueGetString(child))) { virReportError(VIR_ERR_INTERNAL_ERROR, _("%s array element does not contain data"), qmpCmd); goto cleanup; } list[i] = g_strdup(tmp); } ret = n; *array = list; list = NULL; cleanup: virStringListFree(list); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetTPMModels(qemuMonitorPtr mon, char ***tpmmodels) { return qemuMonitorJSONGetStringArray(mon, "query-tpm-models", tpmmodels); } int qemuMonitorJSONGetTPMTypes(qemuMonitorPtr mon, char ***tpmtypes) { return qemuMonitorJSONGetStringArray(mon, "query-tpm-types", tpmtypes); } static int qemuMonitorJSONBuildChrChardevReconnect(virJSONValuePtr object, const virDomainChrSourceReconnectDef *def) { if (def->enabled != VIR_TRISTATE_BOOL_YES) return 0; return virJSONValueObjectAppendNumberUint(object, "reconnect", def->timeout); } static virJSONValuePtr qemuMonitorJSONAttachCharDevCommand(const char *chrID, const virDomainChrSourceDef *chr) { virJSONValuePtr ret = NULL; virJSONValuePtr backend = NULL; virJSONValuePtr data = NULL; virJSONValuePtr addr = NULL; const char *backend_type = NULL; const char *host; const char *port; char *tlsalias = NULL; bool telnet; if (!(backend = virJSONValueNewObject()) || !(data = virJSONValueNewObject())) { goto cleanup; } switch ((virDomainChrType)chr->type) { case VIR_DOMAIN_CHR_TYPE_NULL: case VIR_DOMAIN_CHR_TYPE_VC: backend_type = "null"; break; case VIR_DOMAIN_CHR_TYPE_PTY: backend_type = "pty"; break; case VIR_DOMAIN_CHR_TYPE_FILE: backend_type = "file"; if (virJSONValueObjectAppendString(data, "out", chr->data.file.path) < 0) goto cleanup; break; case VIR_DOMAIN_CHR_TYPE_DEV: backend_type = STRPREFIX(chrID, "parallel") ? "parallel" : "serial"; if (virJSONValueObjectAppendString(data, "device", chr->data.file.path) < 0) goto cleanup; break; case VIR_DOMAIN_CHR_TYPE_TCP: backend_type = "socket"; addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.tcp.host, chr->data.tcp.service); if (!addr || virJSONValueObjectAppend(data, "addr", addr) < 0) goto cleanup; addr = NULL; telnet = chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET; if (chr->data.tcp.listen && virJSONValueObjectAppendBoolean(data, "wait", false) < 0) goto cleanup; if (virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 || virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0) goto cleanup; if (chr->data.tcp.tlscreds) { if (!(tlsalias = qemuAliasTLSObjFromSrcAlias(chrID))) goto cleanup; if (virJSONValueObjectAppendString(data, "tls-creds", tlsalias) < 0) goto cleanup; } if (qemuMonitorJSONBuildChrChardevReconnect(data, &chr->data.tcp.reconnect) < 0) goto cleanup; break; case VIR_DOMAIN_CHR_TYPE_UDP: backend_type = "udp"; host = chr->data.udp.connectHost; if (!host) host = ""; addr = qemuMonitorJSONBuildInetSocketAddress(host, chr->data.udp.connectService); if (!addr || virJSONValueObjectAppend(data, "remote", addr) < 0) goto cleanup; host = chr->data.udp.bindHost; port = chr->data.udp.bindService; if (host || port) { if (!host) host = ""; if (!port) port = ""; addr = qemuMonitorJSONBuildInetSocketAddress(host, port); if (!addr || virJSONValueObjectAppend(data, "local", addr) < 0) goto cleanup; } addr = NULL; break; case VIR_DOMAIN_CHR_TYPE_UNIX: backend_type = "socket"; addr = qemuMonitorJSONBuildUnixSocketAddress(chr->data.nix.path); if (!addr || virJSONValueObjectAppend(data, "addr", addr) < 0) goto cleanup; addr = NULL; if (chr->data.nix.listen && virJSONValueObjectAppendBoolean(data, "wait", false) < 0) goto cleanup; if (virJSONValueObjectAppendBoolean(data, "server", chr->data.nix.listen) < 0) goto cleanup; if (qemuMonitorJSONBuildChrChardevReconnect(data, &chr->data.nix.reconnect) < 0) goto cleanup; break; case VIR_DOMAIN_CHR_TYPE_SPICEVMC: backend_type = "spicevmc"; if (virJSONValueObjectAppendString(data, "type", virDomainChrSpicevmcTypeToString(chr->data.spicevmc)) < 0) goto cleanup; break; case VIR_DOMAIN_CHR_TYPE_SPICEPORT: case VIR_DOMAIN_CHR_TYPE_PIPE: case VIR_DOMAIN_CHR_TYPE_STDIO: case VIR_DOMAIN_CHR_TYPE_NMDM: case VIR_DOMAIN_CHR_TYPE_LAST: if (virDomainChrTypeToString(chr->type)) { virReportError(VIR_ERR_OPERATION_FAILED, _("Hotplug unsupported for char device type '%s'"), virDomainChrTypeToString(chr->type)); } else { virReportError(VIR_ERR_OPERATION_FAILED, _("Hotplug unsupported for char device type '%d'"), chr->type); } goto cleanup; } if (virJSONValueObjectAppendString(backend, "type", backend_type) < 0 || virJSONValueObjectAppend(backend, "data", data) < 0) goto cleanup; data = NULL; if (!(ret = qemuMonitorJSONMakeCommand("chardev-add", "s:id", chrID, "a:backend", &backend, NULL))) goto cleanup; cleanup: VIR_FREE(tlsalias); virJSONValueFree(addr); virJSONValueFree(data); virJSONValueFree(backend); return ret; } int qemuMonitorJSONAttachCharDev(qemuMonitorPtr mon, const char *chrID, virDomainChrSourceDefPtr chr) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONAttachCharDevCommand(chrID, chr))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; } else { if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; } if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) { virJSONValuePtr data = virJSONValueObjectGetObject(reply, "return"); const char *path; if (!(path = virJSONValueObjectGetString(data, "pty"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("chardev-add reply was missing pty path")); goto cleanup; } chr->data.file.path = g_strdup(path); } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONDetachCharDev(qemuMonitorPtr mon, const char *chrID) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("chardev-remove", "s:id", chrID, NULL))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon, char ***aliases) { qemuMonitorJSONListPathPtr *paths = NULL; char **alias; int ret = -1; size_t i; int n; *aliases = NULL; n = qemuMonitorJSONGetObjectListPaths(mon, "/machine/peripheral", &paths); if (n < 0) return -1; if (VIR_ALLOC_N(*aliases, n + 1) < 0) goto cleanup; alias = *aliases; for (i = 0; i < n; i++) { if (STRPREFIX(paths[i]->type, "child<")) { *alias = paths[i]->name; paths[i]->name = NULL; alias++; } } ret = 0; cleanup: for (i = 0; i < n; i++) qemuMonitorJSONListPathFree(paths[i]); VIR_FREE(paths); return ret; } static int qemuMonitorJSONParseCPUx86FeatureWord(virJSONValuePtr data, virCPUx86CPUID *cpuid) { const char *reg; unsigned long long eax_in; unsigned long long ecx_in = 0; unsigned long long features; memset(cpuid, 0, sizeof(*cpuid)); if (!(reg = virJSONValueObjectGetString(data, "cpuid-register"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing cpuid-register in CPU data")); return -1; } if (virJSONValueObjectGetNumberUlong(data, "cpuid-input-eax", &eax_in) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing or invalid cpuid-input-eax in CPU data")); return -1; } ignore_value(virJSONValueObjectGetNumberUlong(data, "cpuid-input-ecx", &ecx_in)); if (virJSONValueObjectGetNumberUlong(data, "features", &features) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("missing or invalid features in CPU data")); return -1; } cpuid->eax_in = eax_in; cpuid->ecx_in = ecx_in; if (STREQ(reg, "EAX")) { cpuid->eax = features; } else if (STREQ(reg, "EBX")) { cpuid->ebx = features; } else if (STREQ(reg, "ECX")) { cpuid->ecx = features; } else if (STREQ(reg, "EDX")) { cpuid->edx = features; } else { virReportError(VIR_ERR_INTERNAL_ERROR, _("unknown CPU register '%s'"), reg); return -1; } return 0; } static virCPUDataPtr qemuMonitorJSONParseCPUx86Features(virJSONValuePtr data) { virCPUDataPtr cpudata = NULL; virCPUx86DataItem item = { 0 }; size_t i; if (!(cpudata = virCPUDataNew(VIR_ARCH_X86_64))) goto error; item.type = VIR_CPU_X86_DATA_CPUID; for (i = 0; i < virJSONValueArraySize(data); i++) { if (qemuMonitorJSONParseCPUx86FeatureWord(virJSONValueArrayGet(data, i), &item.data.cpuid) < 0 || virCPUx86DataAdd(cpudata, &item) < 0) goto error; } return cpudata; error: virCPUDataFree(cpudata); return NULL; } int qemuMonitorJSONGetCPUx86Data(qemuMonitorPtr mon, const char *property, virCPUDataPtr *cpudata) { virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", "s:path", QOM_CPU_PATH, "s:property", property, NULL))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); if (!(*cpudata = qemuMonitorJSONParseCPUx86Features(data))) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /* * Returns -1 on error, 0 if QEMU does not support reporting CPUID features * of a guest CPU, and 1 if the feature is supported. */ static int qemuMonitorJSONCheckCPUx86(qemuMonitorPtr mon) { virJSONValuePtr cmd = NULL; virJSONValuePtr reply = NULL; virJSONValuePtr data; size_t i; size_t n; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("qom-list", "s:path", QOM_CPU_PATH, NULL))) goto cleanup; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if ((data = virJSONValueObjectGet(reply, "error"))) { const char *klass = virJSONValueObjectGetString(data, "class"); if (STREQ_NULLABLE(klass, "DeviceNotFound") || STREQ_NULLABLE(klass, "CommandNotFound")) { ret = 0; goto cleanup; } } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); for (i = 0; i < n; i++) { virJSONValuePtr element = virJSONValueArrayGet(data, i); if (STREQ_NULLABLE(virJSONValueObjectGetString(element, "name"), "feature-words")) break; } if (i == n) ret = 0; else ret = 1; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * qemuMonitorJSONGetGuestCPUx86: * @mon: Pointer to the monitor * @data: returns the cpu data of the guest * @disabled: returns the CPU data for features which were disabled by QEMU * * Retrieve the definition of the guest CPU from a running qemu instance. * * Returns 0 on success, -2 if guest doesn't support this feature, * -1 on other errors. */ int qemuMonitorJSONGetGuestCPUx86(qemuMonitorPtr mon, virCPUDataPtr *data, virCPUDataPtr *disabled) { virCPUDataPtr cpuEnabled = NULL; virCPUDataPtr cpuDisabled = NULL; int rc; if ((rc = qemuMonitorJSONCheckCPUx86(mon)) < 0) return -1; else if (!rc) return -2; if (qemuMonitorJSONGetCPUx86Data(mon, "feature-words", &cpuEnabled) < 0) goto error; if (disabled && qemuMonitorJSONGetCPUx86Data(mon, "filtered-features", &cpuDisabled) < 0) goto error; *data = cpuEnabled; if (disabled) *disabled = cpuDisabled; return 0; error: virCPUDataFree(cpuEnabled); virCPUDataFree(cpuDisabled); return -1; } static int qemuMonitorJSONGetCPUProperties(qemuMonitorPtr mon, char ***props) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; *props = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("qom-list", "s:path", QOM_CPU_PATH, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) return 0; return qemuMonitorJSONParsePropsList(cmd, reply, "bool", props); } static int qemuMonitorJSONGetCPUData(qemuMonitorPtr mon, qemuMonitorCPUFeatureTranslationCallback translate, void *opaque, virCPUDataPtr data) { qemuMonitorJSONObjectProperty prop = { .type = QEMU_MONITOR_OBJECT_PROPERTY_BOOLEAN }; VIR_AUTOSTRINGLIST props = NULL; char **p; if (qemuMonitorJSONGetCPUProperties(mon, &props) < 0) return -1; for (p = props; p && *p; p++) { const char *name = *p; if (qemuMonitorJSONGetObjectProperty(mon, QOM_CPU_PATH, name, &prop) < 0) return -1; if (!prop.val.b) continue; if (translate) name = translate(name, opaque); if (virCPUDataAddFeature(data, name) < 0) return -1; } return 0; } static int qemuMonitorJSONGetCPUDataDisabled(qemuMonitorPtr mon, qemuMonitorCPUFeatureTranslationCallback translate, void *opaque, virCPUDataPtr data) { VIR_AUTOSTRINGLIST props = NULL; char **p; if (qemuMonitorJSONGetStringListProperty(mon, QOM_CPU_PATH, "unavailable-features", &props) < 0) return -1; for (p = props; p && *p; p++) { const char *name = *p; if (translate) name = translate(name, opaque); if (virCPUDataAddFeature(data, name) < 0) return -1; } return 0; } /** * qemuMonitorJSONGetGuestCPU: * @mon: Pointer to the monitor * @arch: CPU architecture * @translate: callback for translating CPU feature names from QEMU to libvirt * @opaque: data for @translate callback * @enabled: returns the CPU data for all enabled features * @disabled: returns the CPU data for features which we asked for * (either explicitly or via a named CPU model) but QEMU disabled them * * Retrieve the definition of the guest CPU from a running QEMU instance. * * Returns 0 on success, -1 on error. */ int qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, virArch arch, qemuMonitorCPUFeatureTranslationCallback translate, void *opaque, virCPUDataPtr *enabled, virCPUDataPtr *disabled) { virCPUDataPtr cpuEnabled = NULL; virCPUDataPtr cpuDisabled = NULL; int ret = -1; if (!(cpuEnabled = virCPUDataNew(arch)) || !(cpuDisabled = virCPUDataNew(arch))) goto cleanup; if (qemuMonitorJSONGetCPUData(mon, translate, opaque, cpuEnabled) < 0) goto cleanup; if (disabled && qemuMonitorJSONGetCPUDataDisabled(mon, translate, opaque, cpuDisabled) < 0) goto cleanup; *enabled = g_steal_pointer(&cpuEnabled); if (disabled) *disabled = g_steal_pointer(&cpuDisabled); ret = 0; cleanup: virCPUDataFree(cpuEnabled); virCPUDataFree(cpuDisabled); return ret; } int qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection", NULL))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * Query and parse returned array of data such as: * * {u'return': [{u'id': u'iothread1', u'thread-id': 30992}, \ * {u'id': u'iothread2', u'thread-id': 30993}]} */ int qemuMonitorJSONGetIOThreads(qemuMonitorPtr mon, qemuMonitorIOThreadInfoPtr **iothreads) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; qemuMonitorIOThreadInfoPtr *infolist = NULL; size_t n = 0; size_t i; *iothreads = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-iothreads", NULL))) return ret; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); n = virJSONValueArraySize(data); /* null-terminated list */ if (VIR_ALLOC_N(infolist, n + 1) < 0) goto cleanup; for (i = 0; i < n; i++) { virJSONValuePtr child = virJSONValueArrayGet(data, i); const char *tmp; qemuMonitorIOThreadInfoPtr info; if (!(tmp = virJSONValueObjectGetString(child, "id"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-iothreads reply data was missing 'id'")); goto cleanup; } if (!STRPREFIX(tmp, "iothread")) continue; if (VIR_ALLOC(info) < 0) goto cleanup; infolist[i] = info; if (virStrToLong_ui(tmp + strlen("iothread"), NULL, 10, &info->iothread_id) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, _("failed to find iothread id for '%s'"), tmp); goto cleanup; } if (virJSONValueObjectGetNumberInt(child, "thread-id", &info->thread_id) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-iothreads reply has malformed " "'thread-id' data")); goto cleanup; } /* Fetch poll values (since QEMU 2.9 ) if available. QEMU * stores these values as int64_t's; however, the qapi type * is an int. The qapi/misc.json also mis-describes the grow * and shrink values as pure add/remove values. The source * util/aio-posix.c function aio_poll uses them as a factor * or divisor in it's calculation. We will fetch and store * them as defined in our structures. */ if (virJSONValueObjectGetNumberUlong(child, "poll-max-ns", &info->poll_max_ns) == 0 && virJSONValueObjectGetNumberUint(child, "poll-grow", &info->poll_grow) == 0 && virJSONValueObjectGetNumberUint(child, "poll-shrink", &info->poll_shrink) == 0) info->poll_valid = true; } ret = n; *iothreads = infolist; infolist = NULL; cleanup: if (infolist) { for (i = 0; i < n; i++) VIR_FREE(infolist[i]); VIR_FREE(infolist); } virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetIOThread(qemuMonitorPtr mon, qemuMonitorIOThreadInfoPtr iothreadInfo) { int ret = -1; char *path = NULL; qemuMonitorJSONObjectProperty prop; path = g_strdup_printf("/objects/iothread%u", iothreadInfo->iothread_id); #define VIR_IOTHREAD_SET_PROP(propName, propVal) \ if (iothreadInfo->set_##propVal) { \ memset(&prop, 0, sizeof(qemuMonitorJSONObjectProperty)); \ prop.type = QEMU_MONITOR_OBJECT_PROPERTY_INT; \ prop.val.iv = iothreadInfo->propVal; \ if (qemuMonitorJSONSetObjectProperty(mon, path, propName, &prop) < 0) \ goto cleanup; \ } VIR_IOTHREAD_SET_PROP("poll-max-ns", poll_max_ns); VIR_IOTHREAD_SET_PROP("poll-grow", poll_grow); VIR_IOTHREAD_SET_PROP("poll-shrink", poll_shrink); #undef VIR_IOTHREAD_SET_PROP ret = 0; cleanup: VIR_FREE(path); return ret; } int qemuMonitorJSONGetMemoryDeviceInfo(qemuMonitorPtr mon, virHashTablePtr info) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data = NULL; qemuMonitorMemoryDeviceInfoPtr meminfo = NULL; size_t i; if (!(cmd = qemuMonitorJSONMakeCommand("query-memory-devices", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONHasError(reply, "CommandNotFound")) { ret = -2; goto cleanup; } if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGetArray(reply, "return"); for (i = 0; i < virJSONValueArraySize(data); i++) { virJSONValuePtr elem = virJSONValueArrayGet(data, i); const char *type; if (!(type = virJSONValueObjectGetString(elem, "type"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-memory-devices reply data doesn't contain " "enum type discriminator")); goto cleanup; } /* dimm memory devices */ if (STREQ(type, "dimm")) { virJSONValuePtr dimminfo; const char *devalias; if (!(dimminfo = virJSONValueObjectGetObject(elem, "data"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-memory-devices reply data doesn't " "contain enum data")); goto cleanup; } if (!(devalias = virJSONValueObjectGetString(dimminfo, "id"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("dimm memory info data is missing 'id'")); goto cleanup; } if (VIR_ALLOC(meminfo) < 0) goto cleanup; if (virJSONValueObjectGetNumberUlong(dimminfo, "addr", &meminfo->address) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed/missing addr in dimm memory info")); goto cleanup; } if (virJSONValueObjectGetNumberUint(dimminfo, "slot", &meminfo->slot) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed/missing slot in dimm memory info")); goto cleanup; } if (virJSONValueObjectGetBoolean(dimminfo, "hotplugged", &meminfo->hotplugged) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed/missing hotplugged in dimm memory info")); goto cleanup; } if (virJSONValueObjectGetBoolean(dimminfo, "hotpluggable", &meminfo->hotpluggable) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed/missing hotpluggable in dimm memory info")); goto cleanup; } if (virHashAddEntry(info, devalias, meminfo) < 0) goto cleanup; meminfo = NULL; } } ret = 0; cleanup: VIR_FREE(meminfo); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * Search for a QOM object link by alias and name. * * For @alias and @name, this function tries to find QOM object named @name * with id @alias in /machine/peripheral. * * Returns: * 0 - Found * -1 - Error - bail out * -2 - Not found */ static int qemuMonitorJSONFindObjectPathByAlias(qemuMonitorPtr mon, const char *name, const char *alias, char **path) { qemuMonitorJSONListPathPtr *paths = NULL; char *child = NULL; int npaths; int ret = -1; size_t i; npaths = qemuMonitorJSONGetObjectListPaths(mon, "/machine/peripheral", &paths); if (npaths < 0) return -1; child = g_strdup_printf("child<%s>", name); for (i = 0; i < npaths; i++) { if (STREQ(paths[i]->name, alias) && STREQ(paths[i]->type, child)) { *path = g_strdup_printf("/machine/peripheral/%s", alias); ret = 0; goto cleanup; } } ret = -2; cleanup: for (i = 0; i < npaths; i++) qemuMonitorJSONListPathFree(paths[i]); VIR_FREE(paths); VIR_FREE(child); return ret; } /** * Recursively search for a QOM object link only by name. * * For @name, this function finds the first QOM object * named @name, recursively going through all the "child<>" * entries, starting from @curpath. * * Returns: * 0 - Found * -1 - Error - bail out * -2 - Not found */ static int qemuMonitorJSONFindObjectPathByName(qemuMonitorPtr mon, const char *curpath, const char *name, char **path) { ssize_t i, npaths = 0; int ret = -2; char *nextpath = NULL; qemuMonitorJSONListPathPtr *paths = NULL; VIR_DEBUG("Searching for '%s' Object Path starting at '%s'", name, curpath); npaths = qemuMonitorJSONGetObjectListPaths(mon, curpath, &paths); if (npaths < 0) goto cleanup; for (i = 0; i < npaths && ret == -2; i++) { if (STREQ_NULLABLE(paths[i]->type, name)) { VIR_DEBUG("Path to '%s' is '%s/%s'", name, curpath, paths[i]->name); ret = 0; *path = g_strdup_printf("%s/%s", curpath, paths[i]->name); goto cleanup; } /* Type entries that begin with "child<" are a branch that can be * traversed looking for more entries */ if (paths[i]->type && STRPREFIX(paths[i]->type, "child<")) { nextpath = g_strdup_printf("%s/%s", curpath, paths[i]->name); ret = qemuMonitorJSONFindObjectPathByName(mon, nextpath, name, path); VIR_FREE(nextpath); } } cleanup: for (i = 0; i < npaths; i++) qemuMonitorJSONListPathFree(paths[i]); VIR_FREE(paths); VIR_FREE(nextpath); return ret; } /** * Recursively search for a QOM object link. * * For @name and @alias, this function finds the first QOM object. * The search is done at first by @alias and @name and if nothing was found * it continues recursively only with @name. * * Returns: * 0 - Found * -1 - Error * -2 - Not found */ int qemuMonitorJSONFindLinkPath(qemuMonitorPtr mon, const char *name, const char *alias, char **path) { char *linkname = NULL; int ret = -1; if (alias) { ret = qemuMonitorJSONFindObjectPathByAlias(mon, name, alias, path); if (ret == -1 || ret == 0) return ret; } linkname = g_strdup_printf("link<%s>", name); ret = qemuMonitorJSONFindObjectPathByName(mon, "/", linkname, path); VIR_FREE(linkname); return ret; } int qemuMonitorJSONMigrateIncoming(qemuMonitorPtr mon, const char *uri) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("migrate-incoming", "s:uri", uri, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; ret = qemuMonitorJSONCheckError(cmd, reply); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONMigrateStartPostCopy(qemuMonitorPtr mon) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("migrate-start-postcopy", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; ret = qemuMonitorJSONCheckError(cmd, reply); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONMigrateContinue(qemuMonitorPtr mon, qemuMonitorMigrationStatus status) { const char *statusStr = qemuMonitorMigrationStatusTypeToString(status); int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("migrate-continue", "s:state", statusStr, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; ret = qemuMonitorJSONCheckError(cmd, reply); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONGetRTCTime(qemuMonitorPtr mon, struct tm *tm) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; if (!(cmd = qemuMonitorJSONMakeCommand("qom-get", "s:path", "/machine", "s:property", "rtc-time", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGet(reply, "return"); if (virJSONValueObjectGetNumberInt(data, "tm_year", &tm->tm_year) < 0 || virJSONValueObjectGetNumberInt(data, "tm_mon", &tm->tm_mon) < 0 || virJSONValueObjectGetNumberInt(data, "tm_mday", &tm->tm_mday) < 0 || virJSONValueObjectGetNumberInt(data, "tm_hour", &tm->tm_hour) < 0 || virJSONValueObjectGetNumberInt(data, "tm_min", &tm->tm_min) < 0 || virJSONValueObjectGetNumberInt(data, "tm_sec", &tm->tm_sec) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("qemu returned malformed time")); goto cleanup; } ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } void qemuMonitorQueryHotpluggableCpusFree(struct qemuMonitorQueryHotpluggableCpusEntry *entries, size_t nentries) { struct qemuMonitorQueryHotpluggableCpusEntry *entry; size_t i; if (!entries) return; for (i = 0; i < nentries; i++) { entry = entries + i; VIR_FREE(entry->type); VIR_FREE(entry->qom_path); VIR_FREE(entry->alias); virJSONValueFree(entry->props); } VIR_FREE(entries); } /** * [{ * "props": { * "core-id": 0, * "thread-id": 0, * "socket-id": 0 * }, * "vcpus-count": 1, * "qom-path": "/machine/unattached/device[0]", * "type": "qemu64-x86_64-cpu" * }, * {...} * ] */ static int qemuMonitorJSONProcessHotpluggableCpusReply(virJSONValuePtr vcpu, struct qemuMonitorQueryHotpluggableCpusEntry *entry) { virJSONValuePtr props; const char *tmp; if (!(tmp = virJSONValueObjectGetString(vcpu, "type"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-hotpluggable-cpus didn't return device type")); return -1; } entry->type = g_strdup(tmp); if (virJSONValueObjectGetNumberUint(vcpu, "vcpus-count", &entry->vcpus) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-hotpluggable-cpus didn't return vcpus-count")); return -1; } if (!(props = virJSONValueObjectGetObject(vcpu, "props"))) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-hotpluggable-cpus didn't return device props")); return -1; } if (!(entry->props = virJSONValueCopy(props))) return -1; entry->node_id = -1; entry->socket_id = -1; entry->core_id = -1; entry->thread_id = -1; ignore_value(virJSONValueObjectGetNumberInt(props, "node-id", &entry->node_id)); ignore_value(virJSONValueObjectGetNumberInt(props, "socket-id", &entry->socket_id)); ignore_value(virJSONValueObjectGetNumberInt(props, "core-id", &entry->core_id)); ignore_value(virJSONValueObjectGetNumberInt(props, "thread-id", &entry->thread_id)); if (entry->node_id == -1 && entry->socket_id == -1 && entry->core_id == -1 && entry->thread_id == -1) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("query-hotpluggable-cpus entry doesn't report " "topology information")); return -1; } /* qom path is not present unless the vCPU is online */ if ((tmp = virJSONValueObjectGetString(vcpu, "qom-path"))) { entry->qom_path = g_strdup(tmp); /* alias is the part after last slash having a "vcpu" prefix */ if ((tmp = strrchr(tmp, '/')) && STRPREFIX(tmp + 1, "vcpu")) entry->alias = g_strdup(tmp + 1); } return 0; } static int qemuMonitorQueryHotpluggableCpusEntrySort(const void *p1, const void *p2) { const struct qemuMonitorQueryHotpluggableCpusEntry *a = p1; const struct qemuMonitorQueryHotpluggableCpusEntry *b = p2; if (a->socket_id != b->socket_id) return a->socket_id - b->socket_id; if (a->core_id != b->core_id) return a->core_id - b->core_id; return a->thread_id - b->thread_id; } int qemuMonitorJSONGetHotpluggableCPUs(qemuMonitorPtr mon, struct qemuMonitorQueryHotpluggableCpusEntry **entries, size_t *nentries) { struct qemuMonitorQueryHotpluggableCpusEntry *info = NULL; size_t ninfo = 0; int ret = -1; size_t i; virJSONValuePtr data; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr vcpu; if (!(cmd = qemuMonitorJSONMakeCommand("query-hotpluggable-cpus", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; data = virJSONValueObjectGet(reply, "return"); ninfo = virJSONValueArraySize(data); if (VIR_ALLOC_N(info, ninfo) < 0) goto cleanup; for (i = 0; i < ninfo; i++) { vcpu = virJSONValueArrayGet(data, i); if (qemuMonitorJSONProcessHotpluggableCpusReply(vcpu, info + i) < 0) goto cleanup; } qsort(info, ninfo, sizeof(*info), qemuMonitorQueryHotpluggableCpusEntrySort); *entries = g_steal_pointer(&info); *nentries = ninfo; ret = 0; cleanup: qemuMonitorQueryHotpluggableCpusFree(info, ninfo); virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } virJSONValuePtr qemuMonitorJSONQueryQMPSchema(qemuMonitorPtr mon) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr ret = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-qmp-schema", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; ret = virJSONValueObjectStealArray(reply, "return"); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetBlockThreshold(qemuMonitorPtr mon, const char *nodename, unsigned long long threshold) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("block-set-write-threshold", "s:node-name", nodename, "U:write-threshold", threshold, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } virJSONValuePtr qemuMonitorJSONQueryNamedBlockNodes(qemuMonitorPtr mon) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr ret = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-named-block-nodes", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; ret = virJSONValueObjectStealArray(reply, "return"); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONSetWatchdogAction(qemuMonitorPtr mon, const char *action) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("watchdog-set-action", "s:action", action, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevCreate(qemuMonitorPtr mon, const char *jobname, virJSONValuePtr props) { g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; cmd = qemuMonitorJSONMakeCommand("blockdev-create", "s:job-id", jobname, "a:options", &props, NULL); virJSONValueFree(props); if (!cmd) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONCheckError(cmd, reply) < 0) return -1; return 0; } int qemuMonitorJSONBlockdevAdd(qemuMonitorPtr mon, virJSONValuePtr props) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommandInternal("blockdev-add", props))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevDel(qemuMonitorPtr mon, const char *nodename) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-del", "s:node-name", nodename, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevTrayOpen(qemuMonitorPtr mon, const char *id, bool force) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-open-tray", "s:id", id, "b:force", force, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevTrayClose(qemuMonitorPtr mon, const char *id) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-close-tray", "s:id", id, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevMediumRemove(qemuMonitorPtr mon, const char *id) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-remove-medium", "s:id", id, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONBlockdevMediumInsert(qemuMonitorPtr mon, const char *id, const char *nodename) { virJSONValuePtr cmd; virJSONValuePtr reply = NULL; int ret = -1; if (!(cmd = qemuMonitorJSONMakeCommand("blockdev-insert-medium", "s:id", id, "s:node-name", nodename, NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckError(cmd, reply) < 0) goto cleanup; ret = 0; cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } /** * The function is used to retrieve the measurement of a SEV guest. * The measurement is signature of the memory contents that was encrypted * through the SEV launch flow. * * A example JSON output: * * { "execute" : "query-sev-launch-measure" } * { "return" : { "data" : "4l8LXeNlSPUDlXPJG5966/8%YZ" } } */ char * qemuMonitorJSONGetSEVMeasurement(qemuMonitorPtr mon) { const char *tmp; char *measurement = NULL; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; virJSONValuePtr data; if (!(cmd = qemuMonitorJSONMakeCommand("query-sev-launch-measure", NULL))) return NULL; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; data = virJSONValueObjectGetObject(reply, "return"); if (!(tmp = virJSONValueObjectGetString(data, "data"))) goto cleanup; measurement = g_strdup(tmp); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return measurement; } /* * Example return data * * "return": [ * { "connected": true, "id": "pr-helper0" } * ] */ static int qemuMonitorJSONExtractPRManagerInfo(virJSONValuePtr reply, virHashTablePtr info) { qemuMonitorPRManagerInfoPtr entry = NULL; virJSONValuePtr data; int ret = -1; size_t i; data = virJSONValueObjectGetArray(reply, "return"); for (i = 0; i < virJSONValueArraySize(data); i++) { virJSONValuePtr prManager = virJSONValueArrayGet(data, i); const char *alias; if (!(alias = virJSONValueObjectGetString(prManager, "id"))) goto malformed; if (VIR_ALLOC(entry) < 0) goto cleanup; if (virJSONValueObjectGetBoolean(prManager, "connected", &entry->connected) < 0) { goto malformed; } if (virHashAddEntry(info, alias, entry) < 0) goto cleanup; entry = NULL; } ret = 0; cleanup: VIR_FREE(entry); return ret; malformed: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed prManager reply")); goto cleanup; } int qemuMonitorJSONGetPRManagerInfo(qemuMonitorPtr mon, virHashTablePtr info) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-pr-managers", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) goto cleanup; ret = qemuMonitorJSONExtractPRManagerInfo(reply, info); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } static int qemuMonitorJSONExtractCurrentMachineInfo(virJSONValuePtr reply, qemuMonitorCurrentMachineInfoPtr info) { virJSONValuePtr data; data = virJSONValueObjectGetObject(reply, "return"); if (!data) goto malformed; if (virJSONValueObjectGetBoolean(data, "wakeup-suspend-support", &info->wakeupSuspendSupport) < 0) goto malformed; return 0; malformed: virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("malformed qemu-current-machine reply")); return -1; } int qemuMonitorJSONGetCurrentMachineInfo(qemuMonitorPtr mon, qemuMonitorCurrentMachineInfoPtr info) { int ret = -1; virJSONValuePtr cmd; virJSONValuePtr reply = NULL; if (!(cmd = qemuMonitorJSONMakeCommand("query-current-machine", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) goto cleanup; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_OBJECT) < 0) goto cleanup; ret = qemuMonitorJSONExtractCurrentMachineInfo(reply, info); cleanup: virJSONValueFree(cmd); virJSONValueFree(reply); return ret; } int qemuMonitorJSONTransactionBitmapAdd(virJSONValuePtr actions, const char *node, const char *name, bool persistent, bool disabled) { return qemuMonitorJSONTransactionAdd(actions, "block-dirty-bitmap-add", "s:node", node, "s:name", name, "b:persistent", persistent, "b:disabled", disabled, NULL); } int qemuMonitorJSONTransactionBitmapRemove(virJSONValuePtr actions, const char *node, const char *name) { return qemuMonitorJSONTransactionAdd(actions, "block-dirty-bitmap-remove", "s:node", node, "s:name", name, NULL); } int qemuMonitorJSONTransactionBitmapEnable(virJSONValuePtr actions, const char *node, const char *name) { return qemuMonitorJSONTransactionAdd(actions, "block-dirty-bitmap-enable", "s:node", node, "s:name", name, NULL); } int qemuMonitorJSONTransactionBitmapDisable(virJSONValuePtr actions, const char *node, const char *name) { return qemuMonitorJSONTransactionAdd(actions, "block-dirty-bitmap-disable", "s:node", node, "s:name", name, NULL); } int qemuMonitorJSONTransactionBitmapMerge(virJSONValuePtr actions, const char *node, const char *target, virJSONValuePtr *sources) { return qemuMonitorJSONTransactionAdd(actions, "block-dirty-bitmap-merge", "s:node", node, "s:target", target, "a:bitmaps", sources, NULL); } int qemuMonitorJSONTransactionBitmapMergeSourceAddBitmap(virJSONValuePtr sources, const char *sourcenode, const char *sourcebitmap) { g_autoptr(virJSONValue) sourceobj = NULL; if (virJSONValueObjectCreate(&sourceobj, "s:node", sourcenode, "s:name", sourcebitmap, NULL) < 0) return -1; if (virJSONValueArrayAppend(sources, sourceobj) < 0) return -1; sourceobj = NULL; return 0; } int qemuMonitorJSONTransactionSnapshotLegacy(virJSONValuePtr actions, const char *device, const char *path, const char *format, bool existing) { const char *mode = NULL; if (existing) mode = "existing"; return qemuMonitorJSONTransactionAdd(actions, "blockdev-snapshot-sync", "s:device", device, "s:snapshot-file", path, "s:format", format, "S:mode", mode, NULL); } int qemuMonitorJSONTransactionSnapshotBlockdev(virJSONValuePtr actions, const char *node, const char *overlay) { return qemuMonitorJSONTransactionAdd(actions, "blockdev-snapshot", "s:node", node, "s:overlay", overlay, NULL); } VIR_ENUM_DECL(qemuMonitorTransactionBackupSyncMode); VIR_ENUM_IMPL(qemuMonitorTransactionBackupSyncMode, QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_LAST, "none", "incremental", "full"); int qemuMonitorJSONTransactionBackup(virJSONValuePtr actions, const char *device, const char *jobname, const char *target, const char *bitmap, qemuMonitorTransactionBackupSyncMode syncmode) { const char *syncmodestr = qemuMonitorTransactionBackupSyncModeTypeToString(syncmode); return qemuMonitorJSONTransactionAdd(actions, "blockdev-backup", "s:device", device, "s:job-id", jobname, "s:target", target, "s:sync", syncmodestr, "S:bitmap", bitmap, "T:auto-finalize", VIR_TRISTATE_BOOL_YES, "T:auto-dismiss", VIR_TRISTATE_BOOL_NO, NULL); } static qemuMonitorJobInfoPtr qemuMonitorJSONGetJobInfoOne(virJSONValuePtr data) { const char *id = virJSONValueObjectGetString(data, "id"); const char *type = virJSONValueObjectGetString(data, "type"); const char *status = virJSONValueObjectGetString(data, "status"); const char *errmsg = virJSONValueObjectGetString(data, "error"); int tmp; g_autoptr(qemuMonitorJobInfo) job = NULL; if (VIR_ALLOC(job) < 0) return NULL; if ((tmp = qemuMonitorJobTypeFromString(type)) < 0) tmp = QEMU_MONITOR_JOB_TYPE_UNKNOWN; job->type = tmp; if ((tmp = qemuMonitorJobStatusTypeFromString(status)) < 0) tmp = QEMU_MONITOR_JOB_STATUS_UNKNOWN; job->status = tmp; job->id = g_strdup(id); job->error = g_strdup(errmsg); /* failure to fetch progress stats is not fatal */ ignore_value(virJSONValueObjectGetNumberUlong(data, "current-progress", &job->progressCurrent)); ignore_value(virJSONValueObjectGetNumberUlong(data, "total-progress", &job->progressTotal)); return g_steal_pointer(&job); } int qemuMonitorJSONGetJobInfo(qemuMonitorPtr mon, qemuMonitorJobInfoPtr **jobs, size_t *njobs) { virJSONValuePtr data; g_autoptr(virJSONValue) cmd = NULL; g_autoptr(virJSONValue) reply = NULL; size_t i; if (!(cmd = qemuMonitorJSONMakeCommand("query-jobs", NULL))) return -1; if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) return -1; if (qemuMonitorJSONCheckReply(cmd, reply, VIR_JSON_TYPE_ARRAY) < 0) return -1; data = virJSONValueObjectGetArray(reply, "return"); for (i = 0; i < virJSONValueArraySize(data); i++) { qemuMonitorJobInfoPtr job = NULL; if (!(job = qemuMonitorJSONGetJobInfoOne(virJSONValueArrayGet(data, i)))) return -1; if (VIR_APPEND_ELEMENT(*jobs, *njobs, job) < 0) return -1; } return 0; }