/*
* qemu_monitor_json.c: interaction with QEMU monitor console
*
* Copyright (C) 2006-2014 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
* .
*
* Author: Daniel P. Berrange
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include "qemu_monitor_text.h"
#include "qemu_monitor_json.h"
#include "qemu_command.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"
#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"
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 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);
typedef struct {
const char *type;
void (*handler)(qemuMonitorPtr mon, virJSONValuePtr data);
} qemuEventHandler;
static qemuEventHandler eventHandlers[] = {
{ "BALLOON_CHANGE", qemuMonitorJSONHandleBalloonChange, },
{ "BLOCK_IO_ERROR", qemuMonitorJSONHandleIOError, },
{ "BLOCK_JOB_CANCELLED", qemuMonitorJSONHandleBlockJobCanceled, },
{ "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, },
{ "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, },
{ "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, },
{ "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, },
{ "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, },
{ "NIC_RX_FILTER_CHANGED", qemuMonitorJSONHandleNicRxFilterChanged, },
{ "POWERDOWN", qemuMonitorJSONHandlePowerdown, },
{ "RESET", qemuMonitorJSONHandleReset, },
{ "RESUME", qemuMonitorJSONHandleResume, },
{ "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, },
{ "SHUTDOWN", qemuMonitorJSONHandleShutdown, },
{ "SPICE_CONNECTED", qemuMonitorJSONHandleSPICEConnect, },
{ "SPICE_DISCONNECTED", qemuMonitorJSONHandleSPICEDisconnect, },
{ "SPICE_INITIALIZED", qemuMonitorJSONHandleSPICEInitialize, },
{ "STOP", qemuMonitorJSONHandleStop, },
{ "SUSPEND", qemuMonitorJSONHandlePMSuspend, },
{ "SUSPEND_DISK", qemuMonitorJSONHandlePMSuspendDisk, },
{ "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, },
{ "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, },
{ "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, },
{ "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"))) {
virJSONValuePtr elt;
if ((elt = virJSONValueObjectGet(timestamp, "seconds")))
ignore_value(virJSONValueGetNumberLong(elt, &seconds));
if ((elt = virJSONValueObjectGet(timestamp, "microseconds")))
ignore_value(virJSONValueGetNumberUint(elt, µs));
}
qemuMonitorEmitEvent(mon, type, seconds, micros, details);
VIR_FREE(details);
handler = bsearch(type, eventHandlers, ARRAY_CARDINALITY(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;
}
static 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 (obj->type != 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;
}
}
VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len);
return used;
}
static int
qemuMonitorJSONCommandWithFd(qemuMonitorPtr mon,
virJSONValuePtr cmd,
int scm_fd,
virJSONValuePtr *reply)
{
int ret = -1;
qemuMonitorMessage msg;
char *cmdstr = NULL;
char *id = NULL;
virJSONValuePtr exe;
*reply = NULL;
memset(&msg, 0, sizeof(msg));
exe = virJSONValueObjectGet(cmd, "execute");
if (exe) {
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 (!(cmdstr = virJSONValueToString(cmd, false)))
goto cleanup;
if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0)
goto cleanup;
msg.txLength = strlen(msg.txBuffer);
msg.txFD = scm_fd;
VIR_DEBUG("Send command '%s' for write with FD %d", cmdstr, scm_fd);
ret = qemuMonitorSend(mon, &msg);
VIR_DEBUG("Receive command reply ret=%d rxObject=%p",
ret, msg.rxObject);
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(cmdstr);
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
qemuMonitorJSONHasError(virJSONValuePtr reply,
const char *klass)
{
virJSONValuePtr error;
const char *thisklass;
if (!virJSONValueObjectHasKey(reply, "error"))
return 0;
error = virJSONValueObjectGet(reply, "error");
if (!error)
return 0;
if (!virJSONValueObjectHasKey(error, "class"))
return 0;
thisklass = virJSONValueObjectGetString(error, "class");
if (!thisklass)
return 0;
return STREQ(klass, thisklass);
}
/* Top-level commands and nested transaction list elements share a
* common structure for everything except the dictionary names. */
static virJSONValuePtr ATTRIBUTE_SENTINEL
qemuMonitorJSONMakeCommandRaw(bool wrap, const char *cmdname, ...)
{
virJSONValuePtr obj;
virJSONValuePtr jargs = NULL;
va_list args;
char *key;
va_start(args, cmdname);
if (!(obj = virJSONValueNewObject()))
goto error;
if (virJSONValueObjectAppendString(obj, wrap ? "type" : "execute",
cmdname) < 0)
goto error;
while ((key = va_arg(args, char *)) != NULL) {
int ret;
char type;
if (strlen(key) < 3) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' is too short, missing type prefix"),
key);
goto error;
}
/* Keys look like s:name the first letter is a type code:
* Explanation of type codes:
* s: string value, must be non-null
* S: string value, omitted if null
*
* i: signed integer value
* j: signed integer value, error if negative
* z: signed integer value, omitted if zero
* y: signed integer value, omitted if zero, error if negative
*
* I: signed long integer value
* J: signed long integer value, error if negative
* Z: signed long integer value, omitted if zero
* Y: signed long integer value, omitted if zero, error if negative
*
* u: unsigned integer value
* p: unsigned integer value, omitted if zero
*
* U: unsigned long integer value (see below for quirks)
* P: unsigned long integer value, omitted if zero
*
* b: boolean value
* B: boolean value, omitted if false
*
* d: double precision floating point number
* n: json null value
* a: json array
*/
type = key[0];
key += 2;
if (!jargs &&
!(jargs = virJSONValueNewObject()))
goto error;
/* This doesn't support maps, but no command uses those. */
switch (type) {
case 'S':
case 's': {
char *val = va_arg(args, char *);
if (!val) {
if (type == 'S')
continue;
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not have null value"),
key);
goto error;
}
ret = virJSONValueObjectAppendString(jargs, key, val);
} break;
case 'z':
case 'y':
case 'j':
case 'i': {
int val = va_arg(args, int);
if (val < 0 && (type == 'j' || type == 'y')) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not be negative"),
key);
goto error;
}
if (!val && (type == 'z' || type == 'y'))
continue;
ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
} break;
case 'p':
case 'u': {
unsigned int val = va_arg(args, unsigned int);
if (!val && type == 'p')
continue;
ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
} break;
case 'Z':
case 'Y':
case 'J':
case 'I': {
long long val = va_arg(args, long long);
if (val < 0 && (type == 'J' || type == 'Y')) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("argument key '%s' must not be negative"),
key);
goto error;
}
if (!val && (type == 'Z' || type == 'Y'))
continue;
ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
} break;
case 'P':
case 'U': {
/* qemu silently truncates numbers larger than LLONG_MAX,
* so passing the full range of unsigned 64 bit integers
* is not safe here. Pass them as signed 64 bit integers
* instead.
*/
long long val = va_arg(args, long long);
if (!val && type == 'P')
continue;
ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
} break;
case 'd': {
double val = va_arg(args, double);
ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
} break;
case 'B':
case 'b': {
int val = va_arg(args, int);
if (!val && type == 'B')
continue;
ret = virJSONValueObjectAppendBoolean(jargs, key, val);
} break;
case 'n': {
ret = virJSONValueObjectAppendNull(jargs, key);
} break;
case 'a': {
virJSONValuePtr val = va_arg(args, virJSONValuePtr);
ret = virJSONValueObjectAppend(jargs, key, val);
} break;
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
goto error;
}
if (ret < 0)
goto error;
}
if (jargs &&
virJSONValueObjectAppend(obj, wrap ? "data" : "arguments", jargs) < 0)
goto error;
va_end(args);
return obj;
error:
virJSONValueFree(obj);
virJSONValueFree(jargs);
va_end(args);
return NULL;
}
#define qemuMonitorJSONMakeCommand(cmdname, ...) \
qemuMonitorJSONMakeCommandRaw(false, cmdname, __VA_ARGS__)
static void
qemuFreeKeywords(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);
}
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 (qemuParseKeywords(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;
}
}
qemuFreeKeywords(nkeywords, keywords, values);
return ret;
error:
qemuFreeKeywords(nkeywords, keywords, values);
virJSONValueFree(ret);
return NULL;
}
static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitShutdown(mon);
}
static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitReset(mon);
}
static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitPowerdown(mon);
}
static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitStop(mon);
}
static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitResume(mon);
}
static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitGuestPanic(mon);
}
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");
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 *action;
const char *reason = "";
bool nospc = false;
int actionID;
/* Throughout here we try our best to carry on upon errors,
since it's imporatant 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");
}
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, actionID, reason);
}
VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily)
VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily,
VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_LAST,
"ipv4", "ipv6", "unix");
static void qemuMonitorJSONHandleGraphics(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 = virJSONValueObjectGet(data, "client"))) {
VIR_WARN("missing client info in VNC event");
return;
}
if (!(server = virJSONValueObjectGet(data, "server"))) {
VIR_WARN("missing server info in VNC event");
return;
}
authScheme = virJSONValueObjectGetString(server, "auth");
if (!authScheme) {
/* not all events are required to contain auth scheme */
VIR_DEBUG("missing auth scheme in graphics event");
authScheme = "";
}
localFamily = virJSONValueObjectGetString(server, "family");
if (!localFamily) {
VIR_WARN("missing local address family in graphics event");
return;
}
localNode = virJSONValueObjectGetString(server, "host");
if (!localNode) {
VIR_WARN("missing local hostname in graphics event");
return;
}
localService = virJSONValueObjectGetString(server, "service");
if (!localService)
localService = ""; /* Spice has multiple ports, so this isn't provided */
remoteFamily = virJSONValueObjectGetString(client, "family");
if (!remoteFamily) {
VIR_WARN("missing remote address family in graphics event");
return;
}
remoteNode = virJSONValueObjectGetString(client, "host");
if (!remoteNode) {
VIR_WARN("missing remote hostname in graphics event");
return;
}
remoteService = virJSONValueObjectGetString(client, "service");
if (!remoteService)
remoteService = ""; /* Spice has multiple ports, so this isn't provided */
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)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
}
static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
}
static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
}
static void qemuMonitorJSONHandleSPICEConnect(qemuMonitorPtr mon, virJSONValuePtr data)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT);
}
static void qemuMonitorJSONHandleSPICEInitialize(qemuMonitorPtr mon, virJSONValuePtr data)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE);
}
static void qemuMonitorJSONHandleSPICEDisconnect(qemuMonitorPtr mon, virJSONValuePtr data)
{
qemuMonitorJSONHandleGraphics(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT);
}
static void
qemuMonitorJSONHandleBlockJobImpl(qemuMonitorPtr mon,
virJSONValuePtr data,
int event)
{
const char *device;
const char *type_str;
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;
switch ((virConnectDomainEventBlockJobStatus) event) {
case VIR_DOMAIN_BLOCK_JOB_COMPLETED:
/* Make sure the whole device has been processed */
if (offset != len)
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);
}
static void
qemuMonitorJSONHandleTrayChange(qemuMonitorPtr mon,
virJSONValuePtr data)
{
const char *devAlias = NULL;
bool trayOpened;
int reason;
if ((devAlias = virJSONValueObjectGetString(data, "device")) == NULL) {
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, reason);
}
static void
qemuMonitorJSONHandlePMWakeup(qemuMonitorPtr mon,
virJSONValuePtr data ATTRIBUTE_UNUSED)
{
qemuMonitorEmitPMWakeup(mon);
}
static void
qemuMonitorJSONHandlePMSuspend(qemuMonitorPtr mon,
virJSONValuePtr data ATTRIBUTE_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 ATTRIBUTE_UNUSED)
{
qemuMonitorEmitPMSuspendDisk(mon);
}
static void
qemuMonitorJSONHandleDeviceDeleted(qemuMonitorPtr mon, virJSONValuePtr data)
{
const char *device;
if (!(device = virJSONValueObjectGetString(data, "device"))) {
VIR_WARN("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);
}
int
qemuMonitorJSONHumanCommandWithFd(qemuMonitorPtr mon,
const char *cmd_str,
int scm_fd,
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 || qemuMonitorJSONCommandWithFd(mon, cmd, scm_fd, &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;
if (!(obj = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("human monitor command was missing return data"));
goto cleanup;
}
if (reply_str) {
const char *data;
data = virJSONValueGetString(obj);
if (VIR_STRDUP(*reply_str, data ? data : "") < 0)
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("qmp_capabilities", NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
virConnectPtr conn ATTRIBUTE_UNUSED)
{
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;
usleep(250000);
} while (++i <= timeout);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONStopCPUs(qemuMonitorPtr mon)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("stop", NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetStatus(qemuMonitorPtr mon,
bool *running,
virDomainPausedReason *reason)
{
int ret;
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-status reply was missing return data"));
goto cleanup;
}
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;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_powerdown", NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetLink(qemuMonitorPtr mon,
const char *name,
virDomainNetInterfaceLinkState state)
{
int ret;
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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSystemReset(qemuMonitorPtr mon)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_reset", NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* [ { "CPU": 0, "current": true, "halted": false, "pc": 3227107138 },
* { "CPU": 1, "current": false, "halted": true, "pc": 7108165 } ]
*/
static int
qemuMonitorJSONExtractCPUInfo(virJSONValuePtr reply,
int **pids)
{
virJSONValuePtr data;
int ret = -1;
size_t i;
int *threads = NULL;
int ncpus;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cpu reply was missing return data"));
goto cleanup;
}
if (data->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cpu information was not an array"));
goto cleanup;
}
if ((ncpus = virJSONValueArraySize(data)) <= 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cpu information was empty"));
goto cleanup;
}
if (VIR_ALLOC_N(threads, ncpus) < 0)
goto cleanup;
for (i = 0; i < ncpus; i++) {
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
int thread;
if (!entry) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cpu information was missing an array element"));
goto cleanup;
}
if (virJSONValueObjectGetNumberInt(entry, "thread_id", &thread) < 0) {
/* Some older qemu versions don't report the thread_id,
* so treat this as non-fatal, simply returning no data */
ret = 0;
goto cleanup;
}
threads[i] = thread;
}
*pids = threads;
threads = NULL;
ret = ncpus;
cleanup:
VIR_FREE(threads);
return ret;
}
int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
int **pids)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-cpus",
NULL);
virJSONValuePtr reply = NULL;
*pids = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret == 0)
ret = qemuMonitorJSONExtractCPUInfo(reply, pids);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetVirtType(qemuMonitorPtr mon,
int *virtType)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-kvm",
NULL);
virJSONValuePtr reply = NULL;
*virtType = VIR_DOMAIN_VIRT_QEMU;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret == 0) {
virJSONValuePtr data;
bool val = false;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info kvm reply was missing return data"));
ret = -1;
goto cleanup;
}
if (virJSONValueObjectGetBoolean(data, "enabled", &val) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info kvm reply missing 'enabled' field"));
ret = -1;
goto cleanup;
}
if (val)
*virtType = VIR_DOMAIN_VIRT_KVM;
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* Returns: 0 if balloon not supported, +1 if balloon query worked
* or -1 on failure
*/
int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon,
unsigned long long *currmem)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-balloon",
NULL);
virJSONValuePtr reply = NULL;
*currmem = 0;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
/* See if balloon soft-failed */
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
qemuMonitorJSONHasError(reply, "KVMMissingCap"))
goto cleanup;
/* See if any other fatal error occurred */
ret = qemuMonitorJSONCheckError(cmd, reply);
/* Success */
if (ret == 0) {
virJSONValuePtr data;
unsigned long long mem;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info balloon reply was missing return data"));
ret = -1;
goto cleanup;
}
if (virJSONValueObjectGetNumberUlong(data, "actual", &mem) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info balloon reply was missing balloon data"));
ret = -1;
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(FIELD, TAG, DIVISOR) \
if (virJSONValueObjectHasKey(statsdata, FIELD) && \
(got < nr_stats)) { \
if (virJSONValueObjectGetNumberUlong(statsdata, 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;
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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if ((data = virJSONValueObjectGet(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 ((ret = qemuMonitorJSONCheckError(cmd, reply)) < 0)
goto cleanup;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-get reply was missing return data"));
goto cleanup;
}
if (!(statsdata = virJSONValueObjectGet(data, "stats"))) {
VIR_DEBUG("data does not include 'stats'");
goto cleanup;
}
GET_BALLOON_STATS("stat-swap-in",
VIR_DOMAIN_MEMORY_STAT_SWAP_IN, 1024);
GET_BALLOON_STATS("stat-swap-out",
VIR_DOMAIN_MEMORY_STAT_SWAP_OUT, 1024);
GET_BALLOON_STATS("stat-major-faults",
VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT, 1);
GET_BALLOON_STATS("stat-minor-faults",
VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT, 1);
GET_BALLOON_STATS("stat-free-memory",
VIR_DOMAIN_MEMORY_STAT_UNUSED, 1024);
GET_BALLOON_STATS("stat-total-memory",
VIR_DOMAIN_MEMORY_STAT_AVAILABLE, 1024);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
if (got > 0)
ret = got;
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;
}
int qemuMonitorJSONGetBlockInfo(qemuMonitorPtr mon,
virHashTablePtr table)
{
int ret;
size_t i;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-block",
NULL);
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block info reply was missing device list"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
struct qemuDomainDiskInfo *info;
const char *thisdev;
const char *status;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block info device entry was not in expected format"));
goto cleanup;
}
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block info device entry was not in expected format"));
goto cleanup;
}
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
if (VIR_ALLOC(info) < 0)
goto cleanup;
if (virHashAddEntry(table, thisdev, info) < 0) {
VIR_FREE(info);
goto cleanup;
}
if (virJSONValueObjectGetBoolean(dev, "removable", &info->removable) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s value"),
"removable");
goto cleanup;
}
if (virJSONValueObjectGetBoolean(dev, "locked", &info->locked) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s value"),
"locked");
goto cleanup;
}
/* Don't check for success here, because 'tray_open' is presented iff
* medium is ejected.
*/
ignore_value(virJSONValueObjectGetBoolean(dev, "tray_open",
&info->tray_open));
/* Missing io-status indicates no error */
if ((status = virJSONValueObjectGetString(dev, "io-status"))) {
info->io_status = qemuMonitorBlockIOStatusToError(status);
if (info->io_status < 0)
goto cleanup;
}
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
const char *dev_name,
long long *rd_req,
long long *rd_bytes,
long long *rd_total_times,
long long *wr_req,
long long *wr_bytes,
long long *wr_total_times,
long long *flush_req,
long long *flush_total_times,
long long *errs)
{
qemuBlockStats *stats;
virHashTablePtr blockstats = NULL;
int ret = -1;
*rd_req = *rd_bytes = -1;
*wr_req = *wr_bytes = *errs = -1;
if (rd_total_times)
*rd_total_times = -1;
if (wr_total_times)
*wr_total_times = -1;
if (flush_req)
*flush_req = -1;
if (flush_total_times)
*flush_total_times = -1;
if (qemuMonitorJSONGetAllBlockStatsInfo(mon, &blockstats) < 0)
goto cleanup;
if (!(stats = virHashLookup(blockstats, dev_name))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find statistics for device '%s'"), dev_name);
goto cleanup;
}
*rd_req = stats->rd_req;
*rd_bytes = stats->rd_bytes;
*wr_req = stats->wr_req;
*wr_bytes = stats->wr_bytes;
*errs = -1; /* QEMU does not have this */
if (rd_total_times)
*rd_total_times = stats->rd_total_times;
if (wr_total_times)
*wr_total_times = stats->wr_total_times;
if (flush_req)
*flush_req = stats->flush_req;
if (flush_total_times)
*flush_total_times = stats->flush_total_times;
ret = 0;
cleanup:
virHashFree(blockstats);
return ret;
}
typedef enum {
QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK,
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT,
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS,
QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET,
} qemuMonitorBlockExtentError;
static int
qemuMonitorJSONDevGetBlockExtent(virJSONValuePtr dev,
unsigned long long *extent)
{
virJSONValuePtr stats;
virJSONValuePtr parent;
if ((parent = virJSONValueObjectGet(dev, "parent")) == NULL ||
parent->type != VIR_JSON_TYPE_OBJECT) {
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT;
}
if ((stats = virJSONValueObjectGet(parent, "stats")) == NULL ||
stats->type != VIR_JSON_TYPE_OBJECT) {
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS;
}
if (virJSONValueObjectGetNumberUlong(stats, "wr_highest_offset",
extent) < 0) {
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET;
}
return QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK;
}
int qemuMonitorJSONGetAllBlockStatsInfo(qemuMonitorPtr mon,
virHashTablePtr *ret_stats)
{
int ret = -1;
int rc;
size_t i;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
qemuBlockStatsPtr bstats = NULL;
virHashTablePtr hash = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-blockstats", NULL)))
return -1;
if (!(hash = virHashCreate(10, virHashValueFree)))
goto cleanup;
if ((rc = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats reply was missing device list"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
virJSONValuePtr stats;
const char *dev_name;
if (VIR_ALLOC(bstats) < 0)
goto cleanup;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not "
"in expected format"));
goto cleanup;
}
if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not "
"in expected format"));
goto cleanup;
}
if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
stats->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats stats entry was not "
"in expected format"));
goto cleanup;
}
if (virJSONValueObjectGetNumberLong(stats, "rd_bytes",
&bstats->rd_bytes) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"rd_bytes");
goto cleanup;
}
if (virJSONValueObjectGetNumberLong(stats, "rd_operations",
&bstats->rd_req) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"rd_operations");
goto cleanup;
}
if (virJSONValueObjectHasKey(stats, "rd_total_time_ns") &&
(virJSONValueObjectGetNumberLong(stats, "rd_total_time_ns",
&bstats->rd_total_times) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"rd_total_time_ns");
goto cleanup;
}
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes",
&bstats->wr_bytes) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"wr_bytes");
goto cleanup;
}
if (virJSONValueObjectGetNumberLong(stats, "wr_operations",
&bstats->wr_req) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"wr_operations");
goto cleanup;
}
if (virJSONValueObjectHasKey(stats, "wr_total_time_ns") &&
(virJSONValueObjectGetNumberLong(stats, "wr_total_time_ns",
&bstats->wr_total_times) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"wr_total_time_ns");
goto cleanup;
}
if (virJSONValueObjectHasKey(stats, "flush_operations") &&
(virJSONValueObjectGetNumberLong(stats, "flush_operations",
&bstats->flush_req) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"flush_operations");
goto cleanup;
}
if (virJSONValueObjectHasKey(stats, "flush_total_time_ns") &&
(virJSONValueObjectGetNumberLong(stats, "flush_total_time_ns",
&bstats->flush_total_times) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"flush_total_time_ns");
goto cleanup;
}
/* it's ok to not have this information here. Just skip silently. */
qemuMonitorJSONDevGetBlockExtent(dev, &bstats->wr_highest_offset);
if (virHashAddEntry(hash, dev_name, bstats) < 0)
goto cleanup;
bstats = NULL;
}
*ret_stats = hash;
hash = NULL;
ret = 0;
cleanup:
VIR_FREE(bstats);
virHashFree(hash);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONBlockStatsUpdateCapacity(qemuMonitorPtr mon,
virHashTablePtr stats)
{
int ret = -1;
int rc;
size_t i;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
if (!(cmd = qemuMonitorJSONMakeCommand("query-block", NULL)))
return -1;
if ((rc = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block reply was missing device list"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
virJSONValuePtr inserted;
virJSONValuePtr image;
qemuBlockStatsPtr bstats;
const char *dev_name;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block device entry was not "
"in expected format"));
goto cleanup;
}
if (!(dev_name = virJSONValueObjectGetString(dev, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-block device entry was not "
"in expected format"));
goto cleanup;
}
if (STRPREFIX(dev_name, QEMU_DRIVE_HOST_PREFIX))
dev_name += strlen(QEMU_DRIVE_HOST_PREFIX);
/* ignore missing info */
if (!(bstats = virHashLookup(stats, dev_name)))
continue;
/* drive may be empty */
if (!(inserted = virJSONValueObjectGet(dev, "inserted")) ||
!(image = virJSONValueObjectGet(inserted, "image")))
continue;
if (virJSONValueObjectGetNumberUlong(image, "virtual-size",
&bstats->capacity) < 0)
continue;
/* if actual-size is missing, image is not thin provisioned */
if (virJSONValueObjectGetNumberUlong(image, "actual-size",
&bstats->physical) < 0)
bstats->physical = bstats->capacity;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetBlockStatsParamsNumber(qemuMonitorPtr mon,
int *nparams)
{
int ret, num = 0;
size_t i;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
NULL);
virJSONValuePtr reply = NULL;
virJSONValuePtr devices = NULL;
virJSONValuePtr dev = NULL;
virJSONValuePtr stats = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats reply was missing device list"));
goto cleanup;
}
dev = virJSONValueArrayGet(devices, 0);
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
goto cleanup;
}
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
stats->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats stats entry was not in expected format"));
goto cleanup;
}
for (i = 0; i < stats->data.object.npairs; i++) {
const char *key = stats->data.object.pairs[i].key;
if (STREQ(key, "rd_bytes") ||
STREQ(key, "rd_operations") ||
STREQ(key, "rd_total_time_ns") ||
STREQ(key, "wr_bytes") ||
STREQ(key, "wr_operations") ||
STREQ(key, "wr_total_time_ns") ||
STREQ(key, "flush_operations") ||
STREQ(key, "flush_total_time_ns")) {
num++;
} else {
/* wr_highest_offset is parsed by qemuMonitorJSONGetBlockExtent. */
if (STRNEQ(key, "wr_highest_offset"))
VIR_DEBUG("Missed block stat: %s", key);
}
}
*nparams = num;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONReportBlockExtentError(qemuMonitorBlockExtentError error)
{
switch (error) {
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOPARENT:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats parent entry was not in "
"expected format"));
break;
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOSTATS:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats stats entry was not in "
"expected format"));
break;
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_NOOFFSET:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot read %s statistic"),
"wr_highest_offset");
break;
case QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK:
return 0;
}
return -1;
}
int qemuMonitorJSONGetBlockExtent(qemuMonitorPtr mon,
const char *dev_name,
unsigned long long *extent)
{
int ret = -1;
size_t i;
bool found = false;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
NULL);
virJSONValuePtr reply = NULL;
virJSONValuePtr devices;
*extent = 0;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
devices = virJSONValueObjectGet(reply, "return");
if (!devices || devices->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats reply was missing device list"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(devices); i++) {
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
const char *thisdev;
int err;
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
goto cleanup;
}
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("blockstats device entry was not in expected format"));
goto cleanup;
}
/* New QEMU has separate names for host & guest side of the disk
* and libvirt gives the host side a 'drive-' prefix. The passed
* in dev_name is the guest side though
*/
if (STRPREFIX(thisdev, QEMU_DRIVE_HOST_PREFIX))
thisdev += strlen(QEMU_DRIVE_HOST_PREFIX);
if (STRNEQ(thisdev, dev_name))
continue;
found = true;
if ((err = qemuMonitorJSONDevGetBlockExtent(dev, extent)) !=
QEMU_MONITOR_BLOCK_EXTENT_ERROR_OK) {
qemuMonitorJSONReportBlockExtentError(err);
goto cleanup;
}
}
if (!found) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find statistics for device '%s'"), dev_name);
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* Return 0 on success, -1 on failure, or -2 if not supported. Size
* is in bytes. */
int qemuMonitorJSONBlockResize(qemuMonitorPtr mon,
const char *device,
unsigned long long size)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("block_resize",
"s:device", device,
"U:size", size,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
ret = -2;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon,
const char *password)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("change",
"s:device", "vnc",
"s:target", "password",
"s:arg", password,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* Returns -1 on error, -2 if not supported */
int qemuMonitorJSONSetPassword(qemuMonitorPtr mon,
const char *protocol,
const char *password,
const char *action_if_connected)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("set_password",
"s:protocol", protocol,
"s:password", password,
"s:connected", action_if_connected,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
ret = -2;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* Returns -1 on error, -2 if not supported */
int qemuMonitorJSONExpirePassword(qemuMonitorPtr mon,
const char *protocol,
const char *expire_time)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("expire_password",
"s:protocol", protocol,
"s:time", expire_time,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
ret = -2;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* Returns: 0 if balloon not supported, +1 if balloon adjust worked
* or -1 on failure
*/
int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon,
unsigned long newmem)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("balloon",
"U:value", ((unsigned long long)newmem)*1024,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
/* See if balloon soft-failed */
if (qemuMonitorJSONHasError(reply, "DeviceNotActive") ||
qemuMonitorJSONHasError(reply, "KVMMissingCap"))
goto cleanup;
/* See if any other fatal error occurred */
ret = qemuMonitorJSONCheckError(cmd, reply);
/* Real success */
if (ret == 0)
ret = 1;
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/*
* Returns: 0 if CPU hotplug not supported, +1 if CPU hotplug worked
* or -1 on failure
*/
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 {
/* offlining is not yet implemented in qmp */
goto fallback;
}
if (!cmd)
goto cleanup;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto fallback;
else
ret = qemuMonitorJSONCheckError(cmd, reply);
/* this function has non-standard return values, so adapt it */
if (ret == 0)
ret = 1;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
fallback:
VIR_DEBUG("no QMP support for cpu_set, trying HMP");
ret = qemuMonitorTextSetCPU(mon, cpu, online);
goto cleanup;
}
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
const char *dev_name,
bool force)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject",
"s:device", dev_name,
"b:force", force ? 1 : 0,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon,
const char *dev_name,
const char *newmedia,
const char *format)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("change",
"s:device", dev_name,
"s:target", newmedia,
"S:arg", format,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int qemuMonitorJSONSaveMemory(qemuMonitorPtr mon,
const char *cmdtype,
unsigned long long offset,
size_t length,
const char *path)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand(cmdtype,
"U:val", offset,
"u:size", length,
"s:filename", path,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon,
unsigned long long offset,
size_t length,
const char *path)
{
return qemuMonitorJSONSaveMemory(mon, "memsave", offset, length, path);
}
int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon,
unsigned long long offset,
size_t length,
const char *path)
{
return qemuMonitorJSONSaveMemory(mon, "pmemsave", offset, length, path);
}
int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon,
unsigned long bandwidth)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate_set_speed",
"U:value", bandwidth * 1024ULL * 1024ULL,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONSetMigrationDowntime(qemuMonitorPtr mon,
unsigned long long downtime)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate_set_downtime",
"d:value", downtime / 1000.0,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationCacheSize(qemuMonitorPtr mon,
unsigned long long *cacheSize)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
*cacheSize = 0;
cmd = qemuMonitorJSONMakeCommand("query-migrate-cache-size", NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = virJSONValueObjectGetNumberUlong(reply, "return", cacheSize);
if (ret < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-migrate-cache-size reply was missing "
"'return' data"));
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONSetMigrationCacheSize(qemuMonitorPtr mon,
unsigned long long cacheSize)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate-set-cache-size",
"U:value", cacheSize,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONGetMigrationStatusReply(virJSONValuePtr reply,
qemuMonitorMigrationStatusPtr status)
{
virJSONValuePtr ret;
const char *statusstr;
int rc;
double mbps;
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info migration reply was missing return data"));
return -1;
}
if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("info migration reply was missing return status"));
return -1;
}
status->status = qemuMonitorMigrationStatusTypeFromString(statusstr);
if (status->status < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected migration status in %s"), statusstr);
return -1;
}
ignore_value(virJSONValueObjectGetNumberUlong(ret, "total-time",
&status->total_time));
if (status->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
rc = virJSONValueObjectGetNumberUlong(ret, "downtime",
&status->downtime);
} else {
rc = virJSONValueObjectGetNumberUlong(ret, "expected-downtime",
&status->downtime);
}
if (rc == 0)
status->downtime_set = true;
if (virJSONValueObjectGetNumberUlong(ret, "setup-time",
&status->setup_time) == 0)
status->setup_time_set = true;
if (status->status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE ||
status->status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED) {
virJSONValuePtr ram = virJSONValueObjectGet(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",
&status->ram_transferred) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but RAM 'transferred' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(ram, "remaining",
&status->ram_remaining) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("migration was active, but RAM 'remaining' "
"data was missing"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(ram, "total",
&status->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) */
status->ram_bps = mbps * (1000 * 1000 / 8);
}
if (virJSONValueObjectGetNumberUlong(ram, "duplicate",
&status->ram_duplicate) == 0)
status->ram_duplicate_set = true;
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal",
&status->ram_normal));
ignore_value(virJSONValueObjectGetNumberUlong(ram, "normal-bytes",
&status->ram_normal_bytes));
virJSONValuePtr disk = virJSONValueObjectGet(ret, "disk");
if (disk) {
rc = virJSONValueObjectGetNumberUlong(disk, "transferred",
&status->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",
&status->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",
&status->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) */
status->disk_bps = mbps * (1000 * 1000 / 8);
}
}
virJSONValuePtr comp = virJSONValueObjectGet(ret, "xbzrle-cache");
if (comp) {
status->xbzrle_set = true;
rc = virJSONValueObjectGetNumberUlong(comp, "cache-size",
&status->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",
&status->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",
&status->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",
&status->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",
&status->xbzrle_overflow);
if (rc < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("XBZRLE is active, but 'overflow' data "
"was missing"));
return -1;
}
}
}
return 0;
}
int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
qemuMonitorMigrationStatusPtr status)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migrate",
NULL);
virJSONValuePtr reply = NULL;
memset(status, 0, sizeof(*status));
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret == 0 &&
qemuMonitorJSONGetMigrationStatusReply(reply, status) < 0)
ret = -1;
if (ret < 0)
memset(status, 0, sizeof(*status));
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONSpiceGetMigrationStatusReply(virJSONValuePtr reply,
bool *spice_migrated)
{
virJSONValuePtr ret;
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-spice reply was missing return data"));
return -1;
}
if (virJSONValueObjectGetBoolean(ret, "migrated", spice_migrated) < 0) {
/* Deliberately don't report error here as we are
* probably dealing with older qemu which doesn't
* report this yet. Pretend spice is migrated. */
*spice_migrated = true;
}
return 0;
}
int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon,
bool *spice_migrated)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-spice",
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret == 0)
ret = qemuMonitorJSONSpiceGetMigrationStatusReply(reply,
spice_migrated);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
unsigned int flags,
const char *uri)
{
int ret;
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate_cancel", NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetDumpGuestMemoryCapability(qemuMonitorPtr mon,
const char *capability)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr caps;
virJSONValuePtr formats;
size_t i;
if (!(cmd = qemuMonitorJSONMakeCommand("query-dump-guest-memory-capability",
NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 0)
goto cleanup;
ret = -1;
caps = virJSONValueObjectGet(reply, "return");
if (!caps || caps->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing dump guest memory capabilities"));
goto cleanup;
}
formats = virJSONValueObjectGet(caps, "formats");
if (!formats || formats->type != VIR_JSON_TYPE_ARRAY) {
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 || dumpformat->type != 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)
{
int ret;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
if (dumpformat) {
cmd = qemuMonitorJSONMakeCommand("dump-guest-memory",
"b:paging", false,
"s:protocol", protocol,
"s:format", dumpformat,
NULL);
} else {
cmd = qemuMonitorJSONMakeCommand("dump-guest-memory",
"b:paging", false,
"s:protocol", protocol,
NULL);
}
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *path ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("usb_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
int bus ATTRIBUTE_UNUSED,
int dev ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("usb_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
int vendor ATTRIBUTE_UNUSED,
int product ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("usb_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDevicePCIAddress *hostAddr ATTRIBUTE_UNUSED,
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("pci_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *path ATTRIBUTE_UNUSED,
const char *bus ATTRIBUTE_UNUSED,
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("pci_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *nicstr ATTRIBUTE_UNUSED,
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("pci_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("pci_del not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon,
const char *fdname,
int fd)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("getfd",
"s:fdname", fdname,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon,
const char *fdname)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("closefd",
"s:fdname", fdname,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONAddFd(qemuMonitorPtr mon, int fdset, int fd, const char *name)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("add-fd",
"i:fdset-id", fdset,
"S:opaque", name,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply);
if (ret == 0) {
/* qemu 1.2 lacks the functionality we need; but we have to
* probe to find that out. Don't log errors in that case. */
if (STREQ_NULLABLE(name, "/dev/null") &&
qemuMonitorJSONHasError(reply, "GenericError")) {
ret = -2;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret == 0) {
virJSONValuePtr data = virJSONValueObjectGet(reply, "return");
if (!data || data->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing return information"));
goto error;
}
data = virJSONValueObjectGet(data, "fd");
if (!data || data->type != VIR_JSON_TYPE_NUMBER ||
virJSONValueGetNumberInt(data, &ret) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("incomplete return information"));
goto error;
}
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
error:
/* Best effort cleanup - kill the entire fdset (even if it has
* earlier successful fd registrations), since we don't know which
* fd qemu got, and don't want to leave the fd leaked in qemu. */
qemuMonitorJSONRemoveFd(mon, fdset, -1);
ret = -1;
goto cleanup;
}
int
qemuMonitorJSONRemoveFd(qemuMonitorPtr mon, int fdset, int fd)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("remove-fd",
"i:fdset-id", fdset,
fd < 0 ? NULL : "i:fd",
fd, NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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 */
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(args);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONRemoveNetdev(qemuMonitorPtr mon,
const char *alias)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("netdev_del",
"s:id", alias,
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONQueryRxFilterParse(virJSONValuePtr msg,
virNetDevRxFilterPtr *filter)
{
int ret = -1;
const char *tmp;
virJSONValuePtr returnArray, entry, table, element;
int nTable;
size_t i;
virNetDevRxFilterPtr fil = virNetDevRxFilterNew();
if (!fil)
goto cleanup;
if (!(returnArray = virJSONValueObjectGet(msg, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-rx-filter reply was missing return data"));
goto cleanup;
}
if (returnArray->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-rx-filter return data was not an array"));
goto cleanup;
}
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;
}
if (VIR_STRDUP(fil->name, tmp) < 0)
goto cleanup;
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"))) ||
((nTable = virJSONValueArraySize(table)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'unicast-table' array "
"in query-rx-filter response"));
goto cleanup;
}
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"))) ||
((nTable = virJSONValueArraySize(table)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'multicast-table' array "
"in query-rx-filter response"));
goto cleanup;
}
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"))) ||
((nTable = virJSONValueArraySize(table)) < 0)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Missing or invalid 'vlan-table' array "
"in query-rx-filter response"));
goto cleanup;
}
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 (qemuMonitorJSONQueryRxFilterParse(reply, filter) < 0)
goto cleanup;
ret = 0;
cleanup:
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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"},
* {"filename": "pty:/dev/pts/7", "label": "parallel0"}
* ]}
*
*/
static int qemuMonitorJSONExtractPtyPaths(virJSONValuePtr reply,
virHashTablePtr paths)
{
virJSONValuePtr data;
int ret = -1;
size_t i;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device reply was missing return data"));
goto cleanup;
}
if (data->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was not an array"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(data); i++) {
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
const char *type;
const char *id;
if (!entry) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing array element"));
goto cleanup;
}
if (!(type = virJSONValueObjectGetString(entry, "filename"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing filename"));
goto cleanup;
}
if (!(id = virJSONValueObjectGetString(entry, "label"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("character device information was missing filename"));
goto cleanup;
}
if (STRPREFIX(type, "pty:")) {
char *path;
if (VIR_STRDUP(path, type + strlen("pty:")) < 0)
goto cleanup;
if (virHashAddEntry(paths, id, path) < 0) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("failed to save chardev path '%s'"), path);
VIR_FREE(path);
goto cleanup;
}
}
}
ret = 0;
cleanup:
return ret;
}
int qemuMonitorJSONGetPtyPaths(qemuMonitorPtr mon,
virHashTablePtr paths)
{
int ret;
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-chardev",
NULL);
virJSONValuePtr reply = NULL;
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret == 0)
ret = qemuMonitorJSONExtractPtyPaths(reply, paths);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONAttachPCIDiskController(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
const char *bus ATTRIBUTE_UNUSED,
virDevicePCIAddress *guestAddr ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("pci_add not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONGetAllPCIAddresses(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
qemuMonitorPCIAddress **addrs ATTRIBUTE_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-pci not supported in JSON mode"));
return -1;
}
int qemuMonitorJSONDelDevice(qemuMonitorPtr mon,
const char *devalias)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("device_del",
"s:id", devalias,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONAddDevice(qemuMonitorPtr mon,
const char *devicestr)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr args;
cmd = qemuMonitorJSONMakeCommand("device_add", NULL);
if (!cmd)
return -1;
args = qemuMonitorJSONKeywordStringToJSON(devicestr, "driver");
if (!args)
goto cleanup;
if (virJSONValueObjectAppend(cmd, "arguments", args) < 0)
goto cleanup;
args = NULL; /* obj owns reference to args now */
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(args);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONAddDrive(qemuMonitorPtr mon,
const char *drivestr)
{
/* XXX Update to use QMP, if QMP ever adds support for drive_add */
VIR_DEBUG("drive_add command not found, trying HMP");
return qemuMonitorTextAddDrive(mon, drivestr);
}
int qemuMonitorJSONDriveDel(qemuMonitorPtr mon,
const char *drivestr)
{
int ret;
/* XXX Update to use QMP, if QMP ever adds support for drive_del */
VIR_DEBUG("drive_del command not found, trying HMP");
if ((ret = qemuMonitorTextDriveDel(mon, drivestr)) < 0) {
virErrorPtr err = virGetLastError();
if (err && err->code == VIR_ERR_OPERATION_UNSUPPORTED) {
VIR_ERROR("%s",
_("deleting disk is not supported. "
"This may leak data if disk is reassigned"));
ret = 1;
virResetLastError();
}
}
return ret;
}
int qemuMonitorJSONSetDrivePassphrase(qemuMonitorPtr mon,
const char *alias,
const char *passphrase)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
char *drive;
if (virAsprintf(&drive, "%s%s", QEMU_DRIVE_HOST_PREFIX, alias) < 0)
return -1;
cmd = qemuMonitorJSONMakeCommand("block_passwd",
"s:device", drive,
"s:password", passphrase,
NULL);
VIR_FREE(drive);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONCreateSnapshot(qemuMonitorPtr mon, const char *name)
{
/* XXX Update to use QMP, if QMP ever adds support for savevm */
VIR_DEBUG("savevm command not found, trying HMP");
return qemuMonitorTextCreateSnapshot(mon, name);
}
int qemuMonitorJSONLoadSnapshot(qemuMonitorPtr mon, const char *name)
{
/* XXX Update to use QMP, if QMP ever adds support for loadvm */
VIR_DEBUG("loadvm command not found, trying HMP");
return qemuMonitorTextLoadSnapshot(mon, name);
}
int qemuMonitorJSONDeleteSnapshot(qemuMonitorPtr mon, const char *name)
{
/* XXX Update to use QMP, if QMP ever adds support for delvm */
VIR_DEBUG("delvm command not found, trying HMP");
return qemuMonitorTextDeleteSnapshot(mon, name);
}
int
qemuMonitorJSONDiskSnapshot(qemuMonitorPtr mon, virJSONValuePtr actions,
const char *device, const char *file,
const char *format, bool reuse)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommandRaw(actions != NULL,
"blockdev-snapshot-sync",
"s:device", device,
"s:snapshot-file", file,
"s:format", format,
"S:mode", reuse ? "existing" : NULL,
NULL);
if (!cmd)
return -1;
if (actions) {
if (virJSONValueArrayAppend(actions, cmd) == 0) {
ret = 0;
cmd = NULL;
}
} else {
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
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,
unsigned int flags)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
bool shallow = (flags & VIR_DOMAIN_BLOCK_REBASE_SHALLOW) != 0;
bool reuse = (flags & VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT) != 0;
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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONTransaction(qemuMonitorPtr mon, virJSONValuePtr actions)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
bool protect = actions->protect;
/* We do NOT want to free actions when recursively freeing cmd. */
actions->protect = true;
cmd = qemuMonitorJSONMakeCommand("transaction",
"a:actions", actions,
NULL);
if (!cmd)
goto cleanup;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
actions->protect = protect;
return ret;
}
/* speed is in bytes/sec. Returns 0 on success, -1 with error message
* emitted on failure.
*
* Additionally, can be used to probe if active commit is supported:
* pass in a bogus device and NULL top and base. The probe return is
* -2 if active commit is detected, -3 if inconclusive; with no error
* message issued.
*/
int
qemuMonitorJSONBlockCommit(qemuMonitorPtr mon, const char *device,
const char *top, const char *base,
const char *backingName,
unsigned long long speed)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("block-commit",
"s:device", device,
"Y:speed", speed,
"S:top", top,
"S:base", base,
"S:backing-file", backingName,
NULL);
if (!cmd)
return -1;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (!top && !base) {
/* Normally we always specify top and base; but omitting them
* allows for probing whether qemu is new enough to support
* live commit. */
if (qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
VIR_DEBUG("block-commit supports active commit");
ret = -2;
} else {
/* 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");
ret = -3;
}
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONDrivePivot(qemuMonitorPtr mon, const char *device,
const char *file ATTRIBUTE_UNUSED,
const char *format ATTRIBUTE_UNUSED)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("block-job-complete",
"s:device", device,
NULL);
if (!cmd)
return -1;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONArbitraryCommand(qemuMonitorPtr mon,
const char *cmd_str,
char **reply_str,
bool hmp)
{
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
int ret = -1;
if (hmp) {
return qemuMonitorJSONHumanCommandWithFd(mon, cmd_str, -1, reply_str);
} else {
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;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("inject-nmi", NULL);
if (!cmd)
return -1;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
VIR_DEBUG("inject-nmi command not found, trying HMP");
ret = qemuMonitorTextInjectNMI(mon);
} else {
ret = qemuMonitorJSONCheckError(cmd, reply);
}
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;
/* @keys is part of @cmd now. Avoid double free */
keys = NULL;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
VIR_DEBUG("send-key command not found, trying HMP");
ret = qemuMonitorTextSendKey(mon, holdtime, keycodes, nkeycodes);
} else {
ret = qemuMonitorJSONCheckError(cmd, reply);
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
virJSONValueFree(keys);
virJSONValueFree(key);
return ret;
}
int qemuMonitorJSONScreendump(qemuMonitorPtr mon,
const char *file)
{
int ret;
virJSONValuePtr cmd, reply = NULL;
cmd = qemuMonitorJSONMakeCommand("screendump",
"s:filename", file,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* Returns -1 on error, 0 if not the right device, 1 if info was
* populated. However, rather than populate info->bandwidth (which
* might overflow on 32-bit machines), bandwidth is tracked optionally
* on the side. */
static int
qemuMonitorJSONGetBlockJobInfoOne(virJSONValuePtr entry,
const char *device,
virDomainBlockJobInfoPtr info,
unsigned long long *bandwidth)
{
const char *this_dev;
const char *type;
if ((this_dev = virJSONValueObjectGetString(entry, "device")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("entry was missing 'device'"));
return -1;
}
if (!STREQ(this_dev, device))
return 0;
type = virJSONValueObjectGetString(entry, "type");
if (!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
info->type = VIR_DOMAIN_BLOCK_JOB_TYPE_UNKNOWN;
if (bandwidth &&
virJSONValueObjectGetNumberUlong(entry, "speed", 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;
}
return 1;
}
/**
* qemuMonitorJSONBlockJobInfo:
* Parse Block Job information, and populate info for the named device.
* Return 1 if info available, 0 if device has no block job, and -1 on error.
*/
int
qemuMonitorJSONBlockJobInfo(qemuMonitorPtr mon,
const char *device,
virDomainBlockJobInfoPtr info,
unsigned long long *bandwidth)
{
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
int nr_results;
size_t i;
int ret = -1;
cmd = qemuMonitorJSONMakeCommand("query-block-jobs", NULL);
if (!cmd)
return -1;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
if ((data = virJSONValueObjectGet(reply, "return")) == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("reply was missing return data"));
goto cleanup;
}
if (data->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unrecognized format of block job information"));
goto cleanup;
}
if ((nr_results = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("unable to determine array size"));
goto cleanup;
}
for (i = ret = 0; i < nr_results && ret == 0; i++) {
virJSONValuePtr entry = virJSONValueArrayGet(data, i);
if (!entry) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing array element"));
ret = -1;
goto cleanup;
}
ret = qemuMonitorJSONGetBlockJobInfoOne(entry, device, info,
bandwidth);
}
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
/* speed is in bytes/sec */
int
qemuMonitorJSONBlockJob(qemuMonitorPtr mon,
const char *device,
const char *base,
const char *backingName,
unsigned long long speed,
qemuMonitorBlockJobCmd mode,
bool modern)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
const char *cmd_name = NULL;
if (base && (mode != BLOCK_JOB_PULL || !modern)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("only modern block pull supports base: %s"), base);
return -1;
}
if (backingName && mode != BLOCK_JOB_PULL) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("backing name is supported only for block pull"));
return -1;
}
if (backingName && !base) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("backing name requires a base image"));
return -1;
}
if (speed && mode == BLOCK_JOB_PULL && !modern) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("only modern block pull supports speed: %llu"),
speed);
return -1;
}
switch (mode) {
case BLOCK_JOB_ABORT:
cmd_name = modern ? "block-job-cancel" : "block_job_cancel";
cmd = qemuMonitorJSONMakeCommand(cmd_name,
"s:device", device,
NULL);
break;
case BLOCK_JOB_SPEED:
cmd_name = modern ? "block-job-set-speed" : "block_job_set_speed";
cmd = qemuMonitorJSONMakeCommand(cmd_name,
"s:device", device,
modern ? "J:speed" : "J:value", speed,
NULL);
break;
case BLOCK_JOB_PULL:
cmd_name = modern ? "block-stream" : "block_stream";
cmd = qemuMonitorJSONMakeCommand(cmd_name,
"s:device", device,
"Y:speed", speed,
"S:base", base,
"S:backing-file", backingName,
NULL);
break;
}
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0 && virJSONValueObjectHasKey(reply, "error")) {
ret = -1;
if (qemuMonitorJSONHasError(reply, "DeviceNotActive")) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("No active operation on device: %s"),
device);
} else if (qemuMonitorJSONHasError(reply, "DeviceInUse")) {
virReportError(VIR_ERR_OPERATION_FAILED,
_("Device %s in use"), device);
} else if (qemuMonitorJSONHasError(reply, "NotSupported")) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Operation is not supported for device: %s"),
device);
} else if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
virReportError(VIR_ERR_OPERATION_INVALID,
_("Command '%s' is not found"), cmd_name);
} else {
virJSONValuePtr error = virJSONValueObjectGet(reply, "error");
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unexpected error: (%s) '%s'"),
NULLSTR(virJSONValueObjectGetString(error, "class")),
NULLSTR(virJSONValueObjectGetString(error, "desc")));
}
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONOpenGraphics(qemuMonitorPtr mon,
const char *protocol,
const char *fdname,
bool skipauth)
{
int ret;
virJSONValuePtr cmd, reply = NULL;
cmd = qemuMonitorJSONMakeCommand("add_client",
"s:protocol", protocol,
"s:fdname", fdname,
"b:skipauth", skipauth,
NULL);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
#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); \
goto cleanup; \
}
static int
qemuMonitorJSONBlockIoThrottleInfo(virJSONValuePtr result,
const char *device,
virDomainBlockIoTuneInfoPtr reply)
{
virJSONValuePtr io_throttle;
int ret = -1;
size_t i;
bool found = false;
io_throttle = virJSONValueObjectGet(result, "return");
if (!io_throttle || io_throttle->type != VIR_JSON_TYPE_ARRAY) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_(" block_io_throttle reply was missing device list"));
goto cleanup;
}
for (i = 0; i < virJSONValueArraySize(io_throttle); i++) {
virJSONValuePtr temp_dev = virJSONValueArrayGet(io_throttle, i);
virJSONValuePtr inserted;
const char *current_dev;
if (!temp_dev || temp_dev->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle device entry "
"was not in expected format"));
goto cleanup;
}
if (!(current_dev = virJSONValueObjectGetString(temp_dev, "device"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle device entry "
"was not in expected format"));
goto cleanup;
}
if (STRNEQ(current_dev, device))
continue;
found = true;
if ((inserted = virJSONValueObjectGet(temp_dev, "inserted")) == NULL ||
inserted->type != VIR_JSON_TYPE_OBJECT) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("block_io_throttle inserted entry "
"was not in expected format"));
goto cleanup;
}
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);
break;
}
if (!found) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("cannot find throttling info for device '%s'"),
device);
goto cleanup;
}
ret = 0;
cleanup:
return ret;
}
#undef GET_THROTTLE_STATS
int qemuMonitorJSONSetBlockIoThrottle(qemuMonitorPtr mon,
const char *device,
virDomainBlockIoTuneInfoPtr info)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr result = NULL;
cmd = qemuMonitorJSONMakeCommand("block_set_io_throttle",
"s:device", device,
"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);
if (!cmd)
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &result);
if (ret == 0 && virJSONValueObjectHasKey(result, "error")) {
if (qemuMonitorJSONHasError(result, "DeviceNotActive"))
virReportError(VIR_ERR_OPERATION_INVALID,
_("No active operation on device: %s"), device);
else if (qemuMonitorJSONHasError(result, "NotSupported"))
virReportError(VIR_ERR_OPERATION_INVALID,
_("Operation is not supported for device: %s"), device);
else
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unexpected error"));
ret = -1;
}
virJSONValueFree(cmd);
virJSONValueFree(result);
return ret;
}
int qemuMonitorJSONGetBlockIoThrottle(qemuMonitorPtr mon,
const char *device,
virDomainBlockIoTuneInfoPtr reply)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr result = NULL;
cmd = qemuMonitorJSONMakeCommand("query-block", NULL);
if (!cmd) {
return -1;
}
ret = qemuMonitorJSONCommand(mon, cmd, &result);
if (ret == 0 && virJSONValueObjectHasKey(result, "error")) {
if (qemuMonitorJSONHasError(result, "DeviceNotActive"))
virReportError(VIR_ERR_OPERATION_INVALID,
_("No active operation on device: %s"), device);
else if (qemuMonitorJSONHasError(result, "NotSupported"))
virReportError(VIR_ERR_OPERATION_INVALID,
_("Operation is not supported for device: %s"), device);
else
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("Unexpected error"));
ret = -1;
}
if (ret == 0)
ret = qemuMonitorJSONBlockIoThrottleInfo(result, device, reply);
virJSONValueFree(cmd);
virJSONValueFree(result);
return ret;
}
int qemuMonitorJSONSystemWakeup(qemuMonitorPtr mon)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
cmd = qemuMonitorJSONMakeCommand("system_wakeup", NULL);
if (!cmd) {
return -1;
}
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetVersion(qemuMonitorPtr mon,
int *major,
int *minor,
int *micro,
char **package)
{
int ret;
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-version reply was missing 'return' data"));
goto cleanup;
}
if (!(qemu = virJSONValueObjectGet(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;
}
if (VIR_STRDUP(*package, tmp) < 0)
goto cleanup;
}
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetMachines(qemuMonitorPtr mon,
qemuMonitorMachineInfoPtr **machines)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
qemuMonitorMachineInfoPtr *infolist = NULL;
int n = 0;
size_t i;
*machines = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-machines", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-machines reply data was not an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(info->name, tmp) < 0)
goto cleanup;
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;
}
if (VIR_STRDUP(info->alias, tmp) < 0)
goto cleanup;
}
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;
}
}
ret = n;
*machines = infolist;
cleanup:
if (ret < 0 && infolist) {
for (i = 0; i < n; i++)
qemuMonitorMachineInfoFree(infolist[i]);
VIR_FREE(infolist);
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetCPUDefinitions(qemuMonitorPtr mon,
char ***cpus)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **cpulist = NULL;
int n = 0;
size_t i;
*cpus = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-cpu-definitions", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
/* 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")) {
ret = 0;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-cpu-definitions reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-cpu-definitions reply data was not an array"));
goto cleanup;
}
/* null-terminated list */
if (VIR_ALLOC_N(cpulist, 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-cpu-definitions reply data was missing 'name'"));
goto cleanup;
}
if (VIR_STRDUP(cpulist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*cpus = cpulist;
cleanup:
if (ret < 0)
virStringFreeList(cpulist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetCommands(qemuMonitorPtr mon,
char ***commands)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **commandlist = NULL;
int n = 0;
size_t i;
*commands = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-commands", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-commands reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-commands reply data was not an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(commandlist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*commands = commandlist;
cleanup:
if (ret < 0)
virStringFreeList(commandlist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetEvents(qemuMonitorPtr mon,
char ***events)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **eventlist = NULL;
int n = 0;
size_t i;
*events = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-events", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound")) {
ret = 0;
goto cleanup;
}
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-events reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-events reply data was not an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(eventlist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*events = eventlist;
cleanup:
if (ret < 0)
virStringFreeList(eventlist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetCommandLineOptionParameters(qemuMonitorPtr mon,
const char *option,
char ***params,
bool *found)
{
int ret;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr data = NULL;
virJSONValuePtr array = NULL;
char **paramlist = NULL;
int 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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 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;
}
qemuMonitorSetOptions(mon, array);
}
ret = -1;
if ((n = virJSONValueArraySize(array)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-command-line-options reply data was not "
"an array"));
goto cleanup;
}
for (i = 0; i < n; 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 ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-command-line-options parameter data was not "
"an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(paramlist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*params = paramlist;
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());
if (ret < 0)
virStringFreeList(paramlist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetKVMState(qemuMonitorPtr mon,
bool *enabled,
bool *present)
{
int ret;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr data = NULL;
/* Safe defaults */
*enabled = *present = false;
if (!(cmd = qemuMonitorJSONMakeCommand("query-kvm", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-kvm reply was missing return data"));
goto cleanup;
}
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;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **typelist = NULL;
int n = 0;
size_t i;
*types = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list-types", NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list-types reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list-types reply data was not an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(typelist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*types = typelist;
cleanup:
if (ret < 0)
virStringFreeList(typelist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int qemuMonitorJSONGetObjectListPaths(qemuMonitorPtr mon,
const char *path,
qemuMonitorJSONListPathPtr **paths)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
qemuMonitorJSONListPathPtr *pathlist = NULL;
int n = 0;
size_t i;
*paths = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
"s:path", path,
NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list reply data was not an array"));
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(info->name, tmp) < 0)
goto cleanup;
if (virJSONValueObjectHasKey(child, "type")) {
if (!(tmp = virJSONValueObjectGetString(child, "type"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-list reply has malformed 'type' data"));
goto cleanup;
}
if (VIR_STRDUP(info->type, tmp) < 0)
goto cleanup;
}
}
ret = n;
*paths = pathlist;
cleanup:
if (ret < 0 && 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;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
const char *tmp;
if (!(cmd = qemuMonitorJSONMakeCommand("qom-get",
"s:path", path,
"s:property", property,
NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-get reply was missing return data"));
goto cleanup;
}
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 && VIR_STRDUP(prop->val.str, tmp) < 0)
goto cleanup;
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;
}
#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 ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
#undef MAKE_SET_CMD
int qemuMonitorJSONGetObjectProps(qemuMonitorPtr mon,
const char *type,
char ***props)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **proplist = NULL;
int n = 0;
size_t i;
*props = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("device-list-properties",
"s:typename", type,
NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0 &&
qemuMonitorJSONHasError(reply, "DeviceNotFound")) {
goto cleanup;
}
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("device-list-properties reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("device-list-properties reply data was not an array"));
goto cleanup;
}
/* 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 (!(tmp = virJSONValueObjectGetString(child, "name"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("device-list-properties reply data was missing 'name'"));
goto cleanup;
}
if (VIR_STRDUP(proplist[i], tmp) < 0)
goto cleanup;
}
ret = n;
*props = proplist;
cleanup:
if (ret < 0)
virStringFreeList(proplist);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
char *
qemuMonitorJSONGetTargetArch(qemuMonitorPtr mon)
{
char *ret = NULL;
int rv;
const char *arch;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
if (!(cmd = qemuMonitorJSONMakeCommand("query-target", NULL)))
return NULL;
rv = qemuMonitorJSONCommand(mon, cmd, &reply);
if (rv == 0)
rv = qemuMonitorJSONCheckError(cmd, reply);
if (rv < 0)
goto cleanup;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-target reply was missing return data"));
goto cleanup;
}
if (!(arch = virJSONValueObjectGetString(data, "arch"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-target reply was missing arch data"));
goto cleanup;
}
ignore_value(VIR_STRDUP(ret, arch));
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationCapabilities(qemuMonitorPtr mon,
char ***capabilities)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr caps;
char **list = NULL;
size_t i;
int n;
*capabilities = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-migrate-capabilities",
NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 0)
goto cleanup;
ret = -1;
if (!(caps = virJSONValueObjectGet(reply, "return")) ||
(n = virJSONValueArraySize(caps)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing migration capabilities"));
goto cleanup;
}
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 || cap->type != 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;
}
if (VIR_STRDUP(list[i], name) < 1)
goto cleanup;
}
ret = n;
*capabilities = list;
cleanup:
if (ret < 0)
virStringFreeList(list);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
int
qemuMonitorJSONGetMigrationCapability(qemuMonitorPtr mon,
qemuMonitorMigrationCaps capability)
{
int ret;
char **capsList = NULL;
const char *cap = qemuMonitorMigrationCapsTypeToString(capability);
if (qemuMonitorJSONGetMigrationCapabilities(mon, &capsList) < 0)
return -1;
ret = virStringArrayHasString(capsList, cap);
virStringFreeList(capsList);
return ret;
}
int
qemuMonitorJSONSetMigrationCapability(qemuMonitorPtr mon,
qemuMonitorMigrationCaps capability)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr cap = NULL;
virJSONValuePtr caps;
if (!(caps = virJSONValueNewArray()))
goto cleanup;
if (!(cap = virJSONValueNewObject()))
goto cleanup;
if (virJSONValueObjectAppendString(
cap, "capability",
qemuMonitorMigrationCapsTypeToString(capability)) < 0)
goto cleanup;
if (virJSONValueObjectAppendBoolean(cap, "state", 1) < 0)
goto cleanup;
if (virJSONValueArrayAppend(caps, cap) < 0)
goto cleanup;
cap = NULL;
cmd = qemuMonitorJSONMakeCommand("migrate-set-capabilities",
"a:capabilities", caps,
NULL);
if (!cmd)
goto cleanup;
caps = NULL;
if ((ret = qemuMonitorJSONCommand(mon, cmd, &reply)) < 0)
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
cleanup:
virJSONValueFree(caps);
virJSONValueFree(cap);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static virJSONValuePtr
qemuMonitorJSONBuildInetSocketAddress(const char *host,
const char *port)
{
virJSONValuePtr addr = NULL;
virJSONValuePtr data = NULL;
if (!(data = virJSONValueNewObject()) ||
!(addr = virJSONValueNewObject()))
goto error;
/* port is really expected as a string here by qemu */
if (virJSONValueObjectAppendString(data, "host", host) < 0 ||
virJSONValueObjectAppendString(data, "port", port) < 0 ||
virJSONValueObjectAppendString(addr, "type", "inet") < 0 ||
virJSONValueObjectAppend(addr, "data", data) < 0)
goto error;
return addr;
error:
virJSONValueFree(data);
virJSONValueFree(addr);
return NULL;
}
static virJSONValuePtr
qemuMonitorJSONBuildUnixSocketAddress(const char *path)
{
virJSONValuePtr addr = NULL;
virJSONValuePtr data = NULL;
if (!(data = virJSONValueNewObject()) ||
!(addr = virJSONValueNewObject()))
goto error;
if (virJSONValueObjectAppendString(data, "path", path) < 0 ||
virJSONValueObjectAppendString(addr, "type", "unix") < 0 ||
virJSONValueObjectAppend(addr, "data", data) < 0)
goto error;
return addr;
error:
virJSONValueFree(data);
virJSONValueFree(addr);
return NULL;
}
int
qemuMonitorJSONNBDServerStart(qemuMonitorPtr mon,
const char *host,
unsigned int port)
{
int ret = -1;
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr addr = NULL;
char *port_str = NULL;
if (virAsprintf(&port_str, "%u", port) < 0)
return ret;
if (!(addr = qemuMonitorJSONBuildInetSocketAddress(host, port_str)))
return ret;
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-start",
"a:addr", addr,
NULL)))
goto cleanup;
/* From now on, @addr is part of @cmd */
addr = NULL;
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,
bool writable)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("nbd-server-add",
"s:device", deviceID,
"b:writable", writable,
NULL)))
return ret;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}
static int
qemuMonitorJSONGetStringArray(qemuMonitorPtr mon, const char *qmpCmd,
char ***array)
{
int ret;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
char **list = NULL;
int n = 0;
size_t i;
*array = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand(qmpCmd, NULL)))
return -1;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0) {
if (qemuMonitorJSONHasError(reply, "CommandNotFound"))
goto cleanup;
ret = qemuMonitorJSONCheckError(cmd, reply);
}
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s reply was missing return data"),
qmpCmd);
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s reply data was not an array"),
qmpCmd);
goto cleanup;
}
/* 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;
}
if (VIR_STRDUP(list[i], tmp) < 0)
goto cleanup;
}
ret = n;
*array = list;
cleanup:
if (ret < 0)
virStringFreeList(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 virJSONValuePtr
qemuMonitorJSONAttachCharDevCommand(const char *chrID,
const virDomainChrSourceDef *chr)
{
virJSONValuePtr ret;
virJSONValuePtr backend;
virJSONValuePtr data = NULL;
virJSONValuePtr addr = NULL;
const char *backend_type = NULL;
bool telnet;
if (!(backend = virJSONValueNewObject()) ||
!(data = virJSONValueNewObject())) {
goto error;
}
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 error;
break;
case VIR_DOMAIN_CHR_TYPE_DEV:
backend_type = STRPREFIX(chrID, "parallel") ? "parallel" : "serial";
if (virJSONValueObjectAppendString(data, "device",
chr->data.file.path) < 0)
goto error;
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 error;
addr = NULL;
telnet = chr->data.tcp.protocol == VIR_DOMAIN_CHR_TCP_PROTOCOL_TELNET;
if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 ||
virJSONValueObjectAppendBoolean(data, "telnet", telnet) < 0 ||
virJSONValueObjectAppendBoolean(data, "server", chr->data.tcp.listen) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_TYPE_UDP:
backend_type = "socket";
addr = qemuMonitorJSONBuildInetSocketAddress(chr->data.udp.connectHost,
chr->data.udp.connectService);
if (!addr ||
virJSONValueObjectAppend(data, "addr", addr) < 0)
goto error;
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 error;
addr = NULL;
if (virJSONValueObjectAppendBoolean(data, "wait", false) < 0 ||
virJSONValueObjectAppendBoolean(data, "server", chr->data.nix.listen) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
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:
virReportError(VIR_ERR_OPERATION_FAILED,
_("Unsupported char device type '%d'"),
chr->type);
goto error;
}
if (virJSONValueObjectAppendString(backend, "type", backend_type) < 0 ||
virJSONValueObjectAppend(backend, "data", data) < 0)
goto error;
data = NULL;
if (!(ret = qemuMonitorJSONMakeCommand("chardev-add",
"s:id", chrID,
"a:backend", backend,
NULL)))
goto error;
return ret;
error:
virJSONValueFree(addr);
virJSONValueFree(data);
virJSONValueFree(backend);
return NULL;
}
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 (qemuMonitorJSONCheckError(cmd, reply) < 0)
goto cleanup;
if (chr->type == VIR_DOMAIN_CHR_TYPE_PTY) {
virJSONValuePtr data;
const char *path;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("chardev-add reply was missing return data"));
goto cleanup;
}
if (!(path = virJSONValueObjectGetString(data, "pty"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("chardev-add reply was missing pty path"));
goto cleanup;
}
if (VIR_STRDUP(chr->data.file.path, path) < 0)
goto cleanup;
}
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;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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 fun;
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", &fun) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing or invalid cpuid-input-eax in CPU data"));
return -1;
}
if (virJSONValueObjectGetNumberUlong(data, "features", &features) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing or invalid features in CPU data"));
return -1;
}
cpuid->function = fun;
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 int
qemuMonitorJSONGetCPUx86Data(qemuMonitorPtr mon,
const char *property,
virCPUDataPtr *cpudata)
{
virJSONValuePtr cmd = NULL;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
virJSONValuePtr element;
virCPUx86Data *x86Data = NULL;
virCPUx86CPUID cpuid;
size_t i;
int n;
int ret = -1;
/* look up if the property exists before asking */
if (!(cmd = qemuMonitorJSONMakeCommand("qom-list",
"s:path", QOM_CPU_PATH,
NULL)))
goto cleanup;
if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0)
goto cleanup;
/* check if device exists */
if ((data = virJSONValueObjectGet(reply, "error"))) {
const char *klass = virJSONValueObjectGetString(data, "class");
if (STREQ_NULLABLE(klass, "DeviceNotFound") ||
STREQ_NULLABLE(klass, "CommandNotFound")) {
ret = -2;
goto cleanup;
}
}
if (qemuMonitorJSONCheckError(cmd, reply))
goto cleanup;
data = virJSONValueObjectGet(reply, "return");
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s CPU property did not return an array"),
property);
goto cleanup;
}
for (i = 0; i < n; i++) {
element = virJSONValueArrayGet(data, i);
if (STREQ_NULLABLE(virJSONValueObjectGetString(element, "name"),
property))
break;
}
/* "property" was not found */
if (i == n) {
ret = -2;
goto cleanup;
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
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 (qemuMonitorJSONCheckError(cmd, reply))
goto cleanup;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qom-get reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("%s CPU property did not return an array"),
property);
goto cleanup;
}
if (VIR_ALLOC(x86Data) < 0)
goto cleanup;
for (i = 0; i < n; i++) {
if (qemuMonitorJSONParseCPUx86FeatureWord(virJSONValueArrayGet(data, i),
&cpuid) < 0 ||
virCPUx86DataAddCPUID(x86Data, &cpuid) < 0)
goto cleanup;
}
if (!(*cpudata = virCPUx86MakeData(VIR_ARCH_X86_64, &x86Data)))
goto cleanup;
ret = 0;
cleanup:
virJSONValueFree(cmd);
virJSONValueFree(reply);
virCPUx86DataFree(x86Data);
return ret;
}
/**
* qemuMonitorJSONGetGuestCPU:
* @mon: Pointer to the monitor
* @arch: arch of the guest
* @data: returns the cpu data of the guest
*
* 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
qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon,
virArch arch,
virCPUDataPtr *data)
{
switch (arch) {
case VIR_ARCH_X86_64:
case VIR_ARCH_I686:
return qemuMonitorJSONGetCPUx86Data(mon, "feature-words", data);
default:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("CPU definition retrieval isn't supported for '%s'"),
virArchToString(arch));
return -1;
}
}
int
qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection",
NULL)))
return ret;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
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,
qemuMonitorIOThreadsInfoPtr **iothreads)
{
int ret = -1;
virJSONValuePtr cmd;
virJSONValuePtr reply = NULL;
virJSONValuePtr data;
qemuMonitorIOThreadsInfoPtr *infolist = NULL;
int n = 0;
size_t i;
*iothreads = NULL;
if (!(cmd = qemuMonitorJSONMakeCommand("query-iothreads", NULL)))
return ret;
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
if (ret == 0)
ret = qemuMonitorJSONCheckError(cmd, reply);
if (ret < 0)
goto cleanup;
ret = -1;
if (!(data = virJSONValueObjectGet(reply, "return"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-iothreads reply was missing return data"));
goto cleanup;
}
if ((n = virJSONValueArraySize(data)) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-iothreads reply data was not an array"));
goto cleanup;
}
/* 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;
qemuMonitorIOThreadsInfoPtr info;
if (VIR_ALLOC(info) < 0)
goto cleanup;
infolist[i] = info;
if (!(tmp = virJSONValueObjectGetString(child, "id"))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("query-iothreads reply data was missing 'id'"));
goto cleanup;
}
if (VIR_STRDUP(info->name, tmp) < 0)
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;
}
}
ret = n;
*iothreads = infolist;
cleanup:
if (ret < 0 && infolist) {
for (i = 0; i < n; i++)
qemuMonitorIOThreadsInfoFree(infolist[i]);
VIR_FREE(infolist);
}
virJSONValueFree(cmd);
virJSONValueFree(reply);
return ret;
}