mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-27 16:15:23 +00:00
3a4f172fdd
Initial support for the new QEMU monitor protocol using JSON as the data encoding format instead of plain text * po/POTFILES.in: Add src/qemu/qemu_monitor_json.c * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Hack to turn on QMP mode. Replace with a version number check on >= 0.12 later * src/qemu/qemu_monitor.c: Delegate to json monitor if enabled * src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h: Add impl of QMP protocol * src/Makefile.am: Add src/qemu/qemu_monitor_json.{c,h}
1427 lines
40 KiB
C
1427 lines
40 KiB
C
/*
|
|
* qemu_monitor_json.c: interaction with QEMU monitor console
|
|
*
|
|
* Copyright (C) 2006-2009 Red Hat, Inc.
|
|
* Copyright (C) 2006 Daniel P. Berrange
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* Author: Daniel P. Berrange <berrange@redhat.com>
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <poll.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
|
|
#include "qemu_monitor_json.h"
|
|
#include "qemu_conf.h"
|
|
#include "memory.h"
|
|
#include "logging.h"
|
|
#include "driver.h"
|
|
#include "datatypes.h"
|
|
#include "virterror_internal.h"
|
|
#include "json.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
|
|
|
|
#define LINE_ENDING "\r\n"
|
|
|
|
static int
|
|
qemuMonitorJSONIOProcessLine(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
|
|
const char *line,
|
|
qemuMonitorMessagePtr msg)
|
|
{
|
|
virJSONValuePtr obj = NULL;
|
|
int ret = -1;
|
|
|
|
VIR_DEBUG("Line [%s]", line);
|
|
|
|
if (!(obj = virJSONValueFromString(line))) {
|
|
VIR_DEBUG0("Parsing JSON string failed");
|
|
errno = EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (obj->type != VIR_JSON_TYPE_OBJECT) {
|
|
VIR_DEBUG0("Parsed JSON string isn't an object");
|
|
errno = EINVAL;
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(obj, "QMP") == 1) {
|
|
VIR_DEBUG0("Got QMP capabilities data");
|
|
ret = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virJSONValueObjectHasKey(obj, "event") == 1) {
|
|
VIR_DEBUG0("Got an event");
|
|
ret = 0;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (msg) {
|
|
if (!(msg->rxBuffer = strdup(line))) {
|
|
errno = ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
msg->rxLength = strlen(line);
|
|
msg->finished = 1;
|
|
} else {
|
|
VIR_DEBUG("Ignoring unexpected JSON message [%s]", line);
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
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 = strndup(data + used, got);
|
|
if (!line) {
|
|
errno = ENOMEM;
|
|
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;
|
|
|
|
*reply = NULL;
|
|
|
|
memset(&msg, 0, sizeof msg);
|
|
|
|
if (!(cmdstr = virJSONValueToString(cmd))) {
|
|
virReportOOMError(NULL);
|
|
goto cleanup;
|
|
}
|
|
if (virAsprintf(&msg.txBuffer, "%s\r\n", cmdstr) < 0) {
|
|
virReportOOMError(NULL);
|
|
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 errno=%d %d bytes '%s'",
|
|
ret, msg.lastErrno, msg.rxLength, msg.rxBuffer);
|
|
|
|
|
|
/* If we got ret==0, but not reply data something rather bad
|
|
* went wrong, so lets fake an EIO error */
|
|
if (!msg.rxBuffer && ret == 0) {
|
|
msg.lastErrno = EIO;
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
if (!((*reply) = virJSONValueFromString(msg.rxBuffer))) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot parse JSON doc '%s'"), msg.rxBuffer);
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (ret < 0)
|
|
virReportSystemError(NULL, msg.lastErrno,
|
|
_("cannot send monitor command '%s'"), cmdstr);
|
|
|
|
cleanup:
|
|
VIR_FREE(cmdstr);
|
|
VIR_FREE(msg.txBuffer);
|
|
VIR_FREE(msg.rxBuffer);
|
|
|
|
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 char *qemuMonitorJSONStringifyError(virJSONValuePtr error)
|
|
{
|
|
char *klass = virJSONValueObjectGetString(error, "class");
|
|
|
|
if (klass) {
|
|
return strdup(klass);
|
|
} else {
|
|
return strdup(_("Missing QEMU error klass"));
|
|
}
|
|
}
|
|
|
|
static int
|
|
qemuMonitorJSONCheckError(virJSONValuePtr cmd,
|
|
virJSONValuePtr reply)
|
|
{
|
|
if (virJSONValueObjectHasKey(reply, "error")) {
|
|
virJSONValuePtr error = virJSONValueObjectGet(reply, "error");
|
|
char *cmdstr = virJSONValueToString(cmd);
|
|
char *replystr = virJSONValueToString(reply);
|
|
|
|
if (!error) {
|
|
VIR_DEBUG("Saw a JSON error, but value is null for %s: %s",
|
|
cmdstr, replystr);
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("error running QEMU command '%s': '%s'"),
|
|
cmdstr, replystr);
|
|
} else {
|
|
VIR_DEBUG("Got a JSON error set for %s", cmdstr);
|
|
char *detail = qemuMonitorJSONStringifyError(error);
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("error running QEMU command '%s': %s ('%s')"),
|
|
cmdstr, detail, replystr);
|
|
VIR_FREE(detail);
|
|
}
|
|
VIR_FREE(cmdstr);
|
|
VIR_FREE(replystr);
|
|
return -1;
|
|
} else if (!virJSONValueObjectHasKey(reply, "return")) {
|
|
char *cmdstr = virJSONValueToString(cmd);
|
|
char *replystr = virJSONValueToString(reply);
|
|
|
|
VIR_DEBUG("Neither 'return' nor 'error' is set in the JSON reply %s: %s",
|
|
cmdstr, replystr);
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("error running QEMU command '%s': '%s'"), cmdstr, replystr);
|
|
VIR_FREE(cmdstr);
|
|
VIR_FREE(replystr);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuMonitorJSONHasError(virJSONValuePtr reply,
|
|
const char *klass)
|
|
{
|
|
virJSONValuePtr error;
|
|
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);
|
|
}
|
|
|
|
static int
|
|
qemuMonitorJSONCommandAddTimestamp(virJSONValuePtr obj)
|
|
{
|
|
struct timeval tv;
|
|
virJSONValuePtr timestamp = NULL;
|
|
|
|
if (gettimeofday(&tv, NULL) < 0) {
|
|
virReportSystemError(NULL, errno, "%s",
|
|
_("cannot query time of day"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(timestamp = virJSONValueNewObject()))
|
|
goto no_memory;
|
|
|
|
if (virJSONValueObjectAppendNumberLong(timestamp, "seconds", tv.tv_sec) < 0)
|
|
goto no_memory;
|
|
if (virJSONValueObjectAppendNumberLong(timestamp, "microseconds", tv.tv_usec) < 0)
|
|
goto no_memory;
|
|
|
|
if (virJSONValueObjectAppend(obj, "timestamp", timestamp) < 0)
|
|
goto no_memory;
|
|
|
|
return 0;
|
|
|
|
no_memory:
|
|
virReportOOMError(NULL);
|
|
virJSONValueFree(timestamp);
|
|
return -1;
|
|
}
|
|
|
|
static virJSONValuePtr ATTRIBUTE_SENTINEL
|
|
qemuMonitorJSONMakeCommand(const char *cmdname,
|
|
...)
|
|
{
|
|
virJSONValuePtr obj;
|
|
virJSONValuePtr jargs = NULL;
|
|
va_list args;
|
|
char *key;
|
|
|
|
va_start(args, cmdname);
|
|
|
|
if (!(obj = virJSONValueNewObject()))
|
|
goto no_memory;
|
|
|
|
if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
|
|
goto no_memory;
|
|
|
|
if (qemuMonitorJSONCommandAddTimestamp(obj) < 0)
|
|
goto error;
|
|
|
|
while ((key = va_arg(args, char *)) != NULL) {
|
|
int ret;
|
|
char type;
|
|
|
|
if (strlen(key) < 3) {
|
|
qemudReportError(NULL, NULL, NULL, 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 */
|
|
type = key[0];
|
|
key += 2;
|
|
|
|
if (!jargs &&
|
|
!(jargs = virJSONValueNewObject()))
|
|
goto no_memory;
|
|
|
|
/* This doesn't supports maps/arrays. This hasn't
|
|
* proved to be a problem..... yet :-) */
|
|
switch (type) {
|
|
case 's': {
|
|
char *val = va_arg(args, char *);
|
|
ret = virJSONValueObjectAppendString(jargs, key, val);
|
|
} break;
|
|
case 'i': {
|
|
int val = va_arg(args, int);
|
|
ret = virJSONValueObjectAppendNumberInt(jargs, key, val);
|
|
} break;
|
|
case 'u': {
|
|
unsigned int val = va_arg(args, unsigned int);
|
|
ret = virJSONValueObjectAppendNumberUint(jargs, key, val);
|
|
} break;
|
|
case 'I': {
|
|
long long val = va_arg(args, long long);
|
|
ret = virJSONValueObjectAppendNumberLong(jargs, key, val);
|
|
} break;
|
|
case 'U': {
|
|
unsigned long long val = va_arg(args, unsigned long long);
|
|
ret = virJSONValueObjectAppendNumberUlong(jargs, key, val);
|
|
} break;
|
|
case 'd': {
|
|
double val = va_arg(args, double);
|
|
ret = virJSONValueObjectAppendNumberDouble(jargs, key, val);
|
|
} break;
|
|
case 'b': {
|
|
int val = va_arg(args, int);
|
|
ret = virJSONValueObjectAppendBoolean(jargs, key, val);
|
|
} break;
|
|
case 'n': {
|
|
ret = virJSONValueObjectAppendNull(jargs, key);
|
|
} break;
|
|
default:
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("unsupported data type '%c' for arg '%s'"), type, key - 2);
|
|
goto error;
|
|
}
|
|
if (ret < 0)
|
|
goto no_memory;
|
|
}
|
|
|
|
if (jargs &&
|
|
virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
|
|
goto no_memory;
|
|
|
|
va_end(args);
|
|
|
|
return obj;
|
|
|
|
no_memory:
|
|
virReportOOMError(NULL);
|
|
error:
|
|
virJSONValueFree(obj);
|
|
virJSONValueFree(jargs);
|
|
va_end(args);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
qemuMonitorJSONStartCPUs(qemuMonitorPtr mon,
|
|
virConnectPtr conn ATTRIBUTE_UNUSED)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("cont", 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
|
|
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 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 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);
|
|
|
|
/* XXX extract PIDs if present - QEMU hasn't implement this yet :-( */
|
|
|
|
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 *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) {
|
|
unsigned long long mem;
|
|
|
|
if (virJSONValueObjectGetNumberUlong(reply, "return", &mem) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("info balloon reply was missing mem return data"));
|
|
ret = -1;
|
|
goto cleanup;
|
|
}
|
|
|
|
*currmem = mem;
|
|
ret = 1;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONGetBlockStatsInfo(qemuMonitorPtr mon,
|
|
const char *devname,
|
|
long long *rd_req,
|
|
long long *rd_bytes,
|
|
long long *wr_req,
|
|
long long *wr_bytes,
|
|
long long *errs)
|
|
{
|
|
int ret;
|
|
int i;
|
|
int found = 0;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-blockstats",
|
|
NULL);
|
|
virJSONValuePtr reply = NULL;
|
|
virJSONValuePtr devices;
|
|
|
|
*rd_req = *rd_bytes = *wr_req = *wr_bytes = *errs = 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) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("blockstats reply was missing device list"));
|
|
goto cleanup;
|
|
}
|
|
|
|
for (i = 0 ; i < virJSONValueArraySize(devices) ; i++) {
|
|
virJSONValuePtr dev = virJSONValueArrayGet(devices, i);
|
|
virJSONValuePtr stats;
|
|
const char *thisdev;
|
|
if (!dev || dev->type != VIR_JSON_TYPE_OBJECT) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("blockstats device entry was not in expected format"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((thisdev = virJSONValueObjectGetString(dev, "device")) == NULL) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("blockstats device entry was not in expected format"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (STRNEQ(thisdev, devname))
|
|
continue;
|
|
|
|
found = 1;
|
|
if ((stats = virJSONValueObjectGet(dev, "stats")) == NULL ||
|
|
stats->type != VIR_JSON_TYPE_OBJECT) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("blockstats stats entry was not in expected format"));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_bytes", rd_bytes) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read %s statistic"),
|
|
"rd_bytes");
|
|
goto cleanup;
|
|
}
|
|
if (virJSONValueObjectGetNumberLong(stats, "rd_operations", rd_req) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read %s statistic"),
|
|
"rd_operations");
|
|
goto cleanup;
|
|
}
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_bytes", wr_bytes) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read %s statistic"),
|
|
"wr_bytes");
|
|
goto cleanup;
|
|
}
|
|
if (virJSONValueObjectGetNumberLong(stats, "wr_operations", wr_req) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot read %s statistic"),
|
|
"wr_operations");
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("cannot find statistics for device '%s'"), devname);
|
|
goto cleanup;
|
|
}
|
|
ret = 0;
|
|
|
|
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: 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,
|
|
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;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon,
|
|
const char *devname)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("eject",
|
|
"s:device", devname,
|
|
"i:force", 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 *devname,
|
|
const char *newmedia,
|
|
const char *format)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd;
|
|
if (format)
|
|
cmd = qemuMonitorJSONMakeCommand("change",
|
|
"s:device", devname,
|
|
"s:target", newmedia,
|
|
"s:arg", format,
|
|
NULL);
|
|
else
|
|
cmd = qemuMonitorJSONMakeCommand("change",
|
|
"s:device", devname,
|
|
"s:target", newmedia,
|
|
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 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;
|
|
char *bandwidthstr;
|
|
virJSONValuePtr cmd;
|
|
virJSONValuePtr reply = NULL;
|
|
if (virAsprintf(&bandwidthstr, "%lum", bandwidth) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
cmd = qemuMonitorJSONMakeCommand("migrate_set_speed",
|
|
"s:value", bandwidthstr,
|
|
NULL);
|
|
VIR_FREE(bandwidthstr);
|
|
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,
|
|
int *status,
|
|
unsigned long long *transferred,
|
|
unsigned long long *remaining,
|
|
unsigned long long *total)
|
|
{
|
|
virJSONValuePtr ret;
|
|
char *statusstr;
|
|
|
|
if (!(ret = virJSONValueObjectGet(reply, "return"))) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("info migration reply was missing return data"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(statusstr = virJSONValueObjectGetString(ret, "status"))) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("info migration reply was missing return status"));
|
|
return -1;
|
|
}
|
|
|
|
if ((*status = qemuMonitorMigrationStatusTypeFromString(statusstr)) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("unexpected migration status in %s"), statusstr);
|
|
return -1;
|
|
}
|
|
|
|
if (*status == QEMU_MONITOR_MIGRATION_STATUS_ACTIVE) {
|
|
virJSONValuePtr ram = virJSONValueObjectGet(ret, "ram");
|
|
if (!ram) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("migration was active, but no RAM info was set"));
|
|
return -1;
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUlong(ram, "transferred", transferred) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("migration was active, but RAM 'transferred' data was missing"));
|
|
return -1;
|
|
}
|
|
if (virJSONValueObjectGetNumberUlong(ram, "remaining", remaining) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("migration was active, but RAM 'remaining' data was missing"));
|
|
return -1;
|
|
}
|
|
if (virJSONValueObjectGetNumberUlong(ram, "total", total) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("migration was active, but RAM 'total' data was missing"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon,
|
|
int *status,
|
|
unsigned long long *transferred,
|
|
unsigned long long *remaining,
|
|
unsigned long long *total)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-migration",
|
|
NULL);
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
*status = 0;
|
|
*transferred = *remaining = *total = 0;
|
|
|
|
if (!cmd)
|
|
return -1;
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret == 0 &&
|
|
qemuMonitorJSONGetMigrationStatusReply(reply,
|
|
status,
|
|
transferred,
|
|
remaining,
|
|
total) < 0)
|
|
ret = -1;
|
|
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int qemuMonitorJSONMigrate(qemuMonitorPtr mon,
|
|
int background,
|
|
const char *uri)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("migrate",
|
|
"i:detach", background ? 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 qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon,
|
|
int background,
|
|
const char *hostname,
|
|
int port)
|
|
{
|
|
char *uri = NULL;
|
|
int ret;
|
|
|
|
if (virAsprintf(&uri, "tcp:%s:%d", hostname, port) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, uri);
|
|
|
|
VIR_FREE(uri);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon,
|
|
int background,
|
|
const char * const *argv,
|
|
const char *target)
|
|
{
|
|
char *argstr;
|
|
char *dest = NULL;
|
|
int ret = -1;
|
|
char *safe_target = NULL;
|
|
|
|
argstr = virArgvToString(argv);
|
|
if (!argstr) {
|
|
virReportOOMError(NULL);
|
|
goto cleanup;
|
|
}
|
|
|
|
/* Migrate to file */
|
|
safe_target = qemuMonitorEscapeShell(target);
|
|
if (!safe_target) {
|
|
virReportOOMError(NULL);
|
|
goto cleanup;
|
|
}
|
|
|
|
if (virAsprintf(&dest, "exec:%s >>%s 2>/dev/null", argstr, safe_target) < 0) {
|
|
virReportOOMError(NULL);
|
|
goto cleanup;
|
|
}
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, dest);
|
|
|
|
cleanup:
|
|
VIR_FREE(safe_target);
|
|
VIR_FREE(argstr);
|
|
VIR_FREE(dest);
|
|
return ret;
|
|
}
|
|
|
|
int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon,
|
|
int background,
|
|
const char *unixfile)
|
|
{
|
|
char *dest = NULL;
|
|
int ret = -1;
|
|
|
|
if (virAsprintf(&dest, "unix:%s", unixfile) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
ret = qemuMonitorJSONMigrate(mon, background, dest);
|
|
|
|
VIR_FREE(dest);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
static int qemuMonitorJSONAddUSB(qemuMonitorPtr mon,
|
|
const char *dev)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("usb_add",
|
|
"s:devname", dev,
|
|
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,
|
|
const char *path)
|
|
{
|
|
int ret;
|
|
char *disk;
|
|
|
|
if (virAsprintf(&disk, "disk:%s", path) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, disk);
|
|
|
|
VIR_FREE(disk);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon,
|
|
int bus,
|
|
int dev)
|
|
{
|
|
int ret;
|
|
char *addr;
|
|
|
|
if (virAsprintf(&addr, "host:%.3d.%.3d", bus, dev) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, addr);
|
|
|
|
VIR_FREE(addr);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon,
|
|
int vendor,
|
|
int product)
|
|
{
|
|
int ret;
|
|
char *addr;
|
|
|
|
if (virAsprintf(&addr, "host:%.4x:%.4x", vendor, product) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
ret = qemuMonitorJSONAddUSB(mon, addr);
|
|
|
|
VIR_FREE(addr);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* XXX qemu also returns a 'function' number now */
|
|
static int
|
|
qemuMonitorJSONGetGuestAddress(virJSONValuePtr reply,
|
|
unsigned *guestDomain,
|
|
unsigned *guestBus,
|
|
unsigned *guestSlot)
|
|
{
|
|
virJSONValuePtr addr;
|
|
|
|
addr = virJSONValueObjectGet(reply, "return");
|
|
if (!addr || addr->type != VIR_JSON_TYPE_OBJECT) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("pci_add reply was missing device address"));
|
|
return -1;
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "domain", guestDomain) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("pci_add reply was missing device domain number"));
|
|
return -1;
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "bus", guestBus) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("pci_add reply was missing device bus number"));
|
|
return -1;
|
|
}
|
|
|
|
if (virJSONValueObjectGetNumberUint(addr, "slot", guestSlot) < 0) {
|
|
qemudReportError(NULL, NULL, NULL, VIR_ERR_INTERNAL_ERROR,
|
|
_("pci_add reply was missing device slot number"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon,
|
|
unsigned hostDomain ATTRIBUTE_UNUSED,
|
|
unsigned hostBus,
|
|
unsigned hostSlot,
|
|
unsigned hostFunction,
|
|
unsigned *guestDomain,
|
|
unsigned *guestBus,
|
|
unsigned *guestSlot)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd;
|
|
virJSONValuePtr reply = NULL;
|
|
char *dev;
|
|
|
|
*guestDomain = *guestBus = *guestSlot = 0;
|
|
|
|
/* XXX hostDomain */
|
|
if (virAsprintf(&dev, "host=%.2x:%.2x.%.1x",
|
|
hostBus, hostSlot, hostFunction) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
"s:pci_addr", "auto"
|
|
"s:type", "host",
|
|
"s:opts", dev,
|
|
NULL);
|
|
VIR_FREE(dev);
|
|
if (!cmd)
|
|
return -1;
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret == 0 &&
|
|
qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0)
|
|
ret = -1;
|
|
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon,
|
|
const char *path,
|
|
const char *bus,
|
|
unsigned *guestDomain,
|
|
unsigned *guestBus,
|
|
unsigned *guestSlot) {
|
|
int ret;
|
|
virJSONValuePtr cmd;
|
|
virJSONValuePtr reply = NULL;
|
|
char *dev;
|
|
|
|
*guestDomain = *guestBus = *guestSlot = 0;
|
|
|
|
if (virAsprintf(&dev, "file=%s,if=%s", path, bus) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
"s:pci_addr", "auto",
|
|
"s:type", "storage",
|
|
"s:opts", dev,
|
|
NULL);
|
|
VIR_FREE(dev);
|
|
if (!cmd)
|
|
return -1;
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret == 0 &&
|
|
qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0)
|
|
ret = -1;
|
|
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon,
|
|
const char *nicstr,
|
|
unsigned *guestDomain,
|
|
unsigned *guestBus,
|
|
unsigned *guestSlot)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("pci_add",
|
|
"s:pci_addr", "auto",
|
|
"s:type", "nic",
|
|
"s:opts", nicstr,
|
|
NULL);
|
|
virJSONValuePtr reply = NULL;
|
|
|
|
*guestDomain = *guestBus = *guestSlot = 0;
|
|
|
|
if (!cmd)
|
|
return -1;
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
if (ret == 0 &&
|
|
qemuMonitorJSONGetGuestAddress(reply, guestDomain, guestBus, guestSlot) < 0)
|
|
ret = -1;
|
|
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon,
|
|
unsigned guestDomain,
|
|
unsigned guestBus,
|
|
unsigned guestSlot)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd;
|
|
virJSONValuePtr reply = NULL;
|
|
char *addr;
|
|
|
|
if (virAsprintf(&addr, "%.4x:%.2x:%.2x",
|
|
guestDomain, guestBus, guestSlot) < 0) {
|
|
virReportOOMError(NULL);
|
|
return -1;
|
|
}
|
|
|
|
cmd = qemuMonitorJSONMakeCommand("pci_del",
|
|
"s:pci_addr", addr,
|
|
NULL);
|
|
VIR_FREE(addr);
|
|
if (!cmd)
|
|
return -1;
|
|
|
|
ret = qemuMonitorJSONCommand(mon, cmd, &reply);
|
|
|
|
if (ret == 0)
|
|
ret = qemuMonitorJSONCheckError(cmd, reply);
|
|
|
|
virJSONValueFree(cmd);
|
|
virJSONValueFree(reply);
|
|
return ret;
|
|
}
|
|
|
|
|
|
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 qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon,
|
|
const char *netstr)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_add",
|
|
"s:device", netstr,
|
|
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 qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon,
|
|
int vlan,
|
|
const char *netname)
|
|
{
|
|
int ret;
|
|
virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("host_net_remove",
|
|
"i:vlan", vlan,
|
|
"s:device", netname,
|
|
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;
|
|
}
|