diff --git a/po/POTFILES.in b/po/POTFILES.in index 9864259675..f8e913022f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -29,6 +29,7 @@ src/qemu/qemu_bridge_filter.c src/qemu/qemu_conf.c src/qemu/qemu_driver.c src/qemu/qemu_monitor.c +src/qemu/qemu_monitor_json.c src/qemu/qemu_monitor_text.c src/remote/remote_driver.c src/secret/secret_driver.c diff --git a/src/Makefile.am b/src/Makefile.am index adffa53b42..e5d89334c4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -189,7 +189,9 @@ QEMU_DRIVER_SOURCES = \ qemu/qemu_monitor.c qemu/qemu_monitor.h \ qemu/qemu_monitor_text.c \ qemu/qemu_monitor_text.h \ - qemu/qemu_driver.c qemu/qemu_driver.h \ + qemu/qemu_monitor_json.c \ + qemu/qemu_monitor_json.h \ + qemu/qemu_driver.c qemu/qemu_driver.h \ qemu/qemu_bridge_filter.c \ qemu/qemu_bridge_filter.h diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 8f8d49028c..7d41b5d0d9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -942,6 +942,9 @@ static unsigned int qemudComputeCmdFlags(const char *help, if (version >= 10000) flags |= QEMUD_CMD_FLAG_0_10; + if (version >= 12000) + flags |= QEMUD_CMD_FLAG_0_12; + return flags; } @@ -1584,6 +1587,7 @@ int qemudBuildCommandLine(virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, virDomainChrDefPtr monitor_chr, + int monitor_json, unsigned int qemuCmdFlags, const char ***retargv, const char ***retenv, @@ -1858,6 +1862,9 @@ int qemudBuildCommandLine(virConnectPtr conn, if (monitor_chr) { virBuffer buf = VIR_BUFFER_INITIALIZER; + if (monitor_json) + virBufferAddLit(&buf, "control,"); + qemudBuildCommandLineChrDevStr(monitor_chr, &buf); if (virBufferError(&buf)) goto error; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 1bf3e16728..248677a8cd 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -74,6 +74,8 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_MIGRATE_QEMU_UNIX = (1 << 21), /* Does qemu support unix domain sockets for migration? */ QEMUD_CMD_FLAG_CHARDEV = (1 << 22), /* Is the new -chardev arg available */ QEMUD_CMD_FLAG_ENABLE_KVM = (1 << 23), /* Is the -enable-kvm flag available to "enable KVM full virtualization support" */ + QEMUD_CMD_FLAG_0_12 = (1 << 24), + QEMUD_CMD_FLAG_MONITOR_JSON = QEMUD_CMD_FLAG_0_12, /* JSON mode for monitor */ }; /* Main driver state */ @@ -168,6 +170,7 @@ int qemudBuildCommandLine (virConnectPtr conn, struct qemud_driver *driver, virDomainDefPtr def, virDomainChrDefPtr monitor_chr, + int monitor_json, unsigned int qemuCmdFlags, const char ***retargv, const char ***retenv, diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8a9c051e3b..005647e871 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -87,6 +87,7 @@ struct _qemuDomainObjPrivate { qemuMonitorPtr mon; virDomainChrDefPtr monConfig; + int monJSON; int nvcpupids; int *vcpupids; @@ -173,6 +174,8 @@ static int qemuDomainObjPrivateXMLFormat(virBufferPtr buf, void *data) } virBufferEscapeString(buf, " monJSON) + virBufferAddLit(buf, " json='1'"); virBufferVSprintf(buf, " type='%s'/>\n", virDomainChrTypeToString(priv->monConfig->type)); } @@ -217,6 +220,9 @@ static int qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, void *data) priv->monConfig->type = VIR_DOMAIN_CHR_TYPE_PTY; VIR_FREE(tmp); + if (virXPathBoolean(NULL, "int(./monitor[1]/@json)", ctxt)) + priv->monJSON = 1; + switch (priv->monConfig->type) { case VIR_DOMAIN_CHR_TYPE_PTY: priv->monConfig->data.file.path = monitorpath; @@ -799,6 +805,7 @@ qemuConnectMonitor(virDomainObjPtr vm) if ((priv->mon = qemuMonitorOpen(vm, priv->monConfig, + priv->monJSON, qemuHandleMonitorEOF)) == NULL) { VIR_ERROR(_("Failed to connect monitor for %s\n"), vm->def->name); return -1; @@ -2347,6 +2354,11 @@ static int qemudStartVMDaemon(virConnectPtr conn, if (qemuPrepareMonitorChr(conn, driver, priv->monConfig, vm->def->name) < 0) goto cleanup; +#if HAVE_YAJL + if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON) + priv->monJSON = 1; +#endif + if ((ret = virFileDeletePid(driver->stateDir, vm->def->name)) != 0) { virReportSystemError(conn, ret, _("Cannot remove stale PID file for %s"), @@ -2362,7 +2374,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, vm->def->id = driver->nextvmid++; if (qemudBuildCommandLine(conn, driver, vm->def, priv->monConfig, - qemuCmdFlags, &argv, &progenv, + priv->monJSON, qemuCmdFlags, &argv, &progenv, &tapfds, &ntapfds, migrateFrom) < 0) goto cleanup; @@ -4437,7 +4449,7 @@ static char *qemuDomainXMLToNative(virConnectPtr conn, goto cleanup; if (qemudBuildCommandLine(conn, driver, def, - &monConfig, qemuCmdFlags, + &monConfig, 0, qemuCmdFlags, &retargv, &retenv, NULL, NULL, /* Don't want it to create TAP devices */ NULL) < 0) { diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 26a8c9c3a4..1d771feacd 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -30,6 +30,7 @@ #include "qemu_monitor.h" #include "qemu_monitor_text.h" +#include "qemu_monitor_json.h" #include "qemu_conf.h" #include "event.h" #include "virterror_internal.h" @@ -72,6 +73,7 @@ struct _qemuMonitor { /* If the monitor is in process of shutting down */ unsigned closed: 1; + unsigned json: 1; }; @@ -282,9 +284,14 @@ qemuMonitorIOProcess(qemuMonitorPtr mon) msg = mon->msg; VIR_DEBUG("Process %d", (int)mon->bufferOffset); - len = qemuMonitorTextIOProcess(mon, - mon->buffer, mon->bufferOffset, - msg); + if (mon->json) + len = qemuMonitorJSONIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); + else + len = qemuMonitorTextIOProcess(mon, + mon->buffer, mon->bufferOffset, + msg); if (len < 0) { mon->lastErrno = errno; @@ -528,6 +535,7 @@ qemuMonitorIO(int watch, int fd, int events, void *opaque) { qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, + int json, qemuMonitorEOFNotify eofCB) { qemuMonitorPtr mon; @@ -554,6 +562,7 @@ qemuMonitorOpen(virDomainObjPtr vm, mon->refs = 1; mon->vm = vm; mon->eofCB = eofCB; + mon->json = json; qemuMonitorLock(mon); switch (config->type) { @@ -692,43 +701,68 @@ int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStartCPUs(mon, conn); + if (mon->json) + ret = qemuMonitorJSONStartCPUs(mon, conn); + else + ret = qemuMonitorTextStartCPUs(mon, conn); + return ret; } int qemuMonitorStopCPUs(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextStopCPUs(mon); + if (mon->json) + ret = qemuMonitorJSONStopCPUs(mon); + else + ret = qemuMonitorTextStopCPUs(mon); + return ret; } int qemuMonitorSystemPowerdown(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSystemPowerdown(mon); + if (mon->json) + ret = qemuMonitorJSONSystemPowerdown(mon); + else + ret = qemuMonitorTextSystemPowerdown(mon); + return ret; } int qemuMonitorGetCPUInfo(qemuMonitorPtr mon, int **pids) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetCPUInfo(mon, pids); + if (mon->json) + ret = qemuMonitorJSONGetCPUInfo(mon, pids); + else + ret = qemuMonitorTextGetCPUInfo(mon, pids); + return ret; } int qemuMonitorGetBalloonInfo(qemuMonitorPtr mon, unsigned long *currmem) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetBalloonInfo(mon, currmem); + if (mon->json) + ret = qemuMonitorJSONGetBalloonInfo(mon, currmem); + else + ret = qemuMonitorTextGetBalloonInfo(mon, currmem); + return ret; } @@ -740,38 +774,61 @@ int qemuMonitorGetBlockStatsInfo(qemuMonitorPtr mon, long long *wr_bytes, long long *errs) { + int ret; DEBUG("mon=%p, fd=%d dev=%s", mon, mon->fd, devname); - return qemuMonitorTextGetBlockStatsInfo(mon, devname, - rd_req, rd_bytes, - wr_req, wr_bytes, - errs); + if (mon->json) + ret = qemuMonitorJSONGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + else + ret = qemuMonitorTextGetBlockStatsInfo(mon, devname, + rd_req, rd_bytes, + wr_req, wr_bytes, + errs); + return ret; } int qemuMonitorSetVNCPassword(qemuMonitorPtr mon, const char *password) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextSetVNCPassword(mon, password); + if (mon->json) + ret = qemuMonitorJSONSetVNCPassword(mon, password); + else + ret = qemuMonitorTextSetVNCPassword(mon, password); + return ret; } int qemuMonitorSetBalloon(qemuMonitorPtr mon, unsigned long newmem) { + int ret; DEBUG("mon=%p, fd=%d newmem=%lu", mon, mon->fd, newmem); - return qemuMonitorTextSetBalloon(mon, newmem); + if (mon->json) + ret = qemuMonitorJSONSetBalloon(mon, newmem); + else + ret = qemuMonitorTextSetBalloon(mon, newmem); + return ret; } int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname) { + int ret; DEBUG("mon=%p, fd=%d devname=%s", mon, mon->fd, devname); - return qemuMonitorTextEjectMedia(mon, devname); + if (mon->json) + ret = qemuMonitorJSONEjectMedia(mon, devname); + else + ret = qemuMonitorTextEjectMedia(mon, devname); + return ret; } @@ -780,10 +837,15 @@ int qemuMonitorChangeMedia(qemuMonitorPtr mon, const char *newmedia, const char *format) { + int ret; DEBUG("mon=%p, fd=%d devname=%s newmedia=%s format=%s", mon, mon->fd, devname, newmedia, format); - return qemuMonitorTextChangeMedia(mon, devname, newmedia, format); + if (mon->json) + ret = qemuMonitorJSONChangeMedia(mon, devname, newmedia, format); + else + ret = qemuMonitorTextChangeMedia(mon, devname, newmedia, format); + return ret; } @@ -792,10 +854,15 @@ int qemuMonitorSaveVirtualMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSaveVirtualMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSaveVirtualMemory(mon, offset, length, path); + return ret; } int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, @@ -803,19 +870,29 @@ int qemuMonitorSavePhysicalMemory(qemuMonitorPtr mon, size_t length, const char *path) { + int ret; DEBUG("mon=%p, fd=%d offset=%llu length=%zu path=%s", mon, mon->fd, offset, length, path); - return qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + if (mon->json) + ret = qemuMonitorJSONSavePhysicalMemory(mon, offset, length, path); + else + ret = qemuMonitorTextSavePhysicalMemory(mon, offset, length, path); + return ret; } int qemuMonitorSetMigrationSpeed(qemuMonitorPtr mon, unsigned long bandwidth) { + int ret; DEBUG("mon=%p, fd=%d bandwidth=%lu", mon, mon->fd, bandwidth); - return qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + if (mon->json) + ret = qemuMonitorJSONSetMigrationSpeed(mon, bandwidth); + else + ret = qemuMonitorTextSetMigrationSpeed(mon, bandwidth); + return ret; } int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, @@ -824,12 +901,20 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total) { + int ret; DEBUG("mon=%p, fd=%d", mon, mon->fd); - return qemuMonitorTextGetMigrationStatus(mon, status, - transferred, - remaining, - total); + if (mon->json) + ret = qemuMonitorJSONGetMigrationStatus(mon, status, + transferred, + remaining, + total); + else + ret = qemuMonitorTextGetMigrationStatus(mon, status, + transferred, + remaining, + total); + return ret; } @@ -838,10 +923,15 @@ int qemuMonitorMigrateToHost(qemuMonitorPtr mon, const char *hostname, int port) { + int ret; DEBUG("mon=%p, fd=%d hostname=%s port=%d", mon, mon->fd, hostname, port); - return qemuMonitorTextMigrateToHost(mon, background, hostname, port); + if (mon->json) + ret = qemuMonitorJSONMigrateToHost(mon, background, hostname, port); + else + ret = qemuMonitorTextMigrateToHost(mon, background, hostname, port); + return ret; } @@ -850,35 +940,55 @@ int qemuMonitorMigrateToCommand(qemuMonitorPtr mon, const char * const *argv, const char *target) { + int ret; DEBUG("mon=%p, fd=%d argv=%p target=%s", mon, mon->fd, argv, target); - return qemuMonitorTextMigrateToCommand(mon, background, argv, target); + if (mon->json) + ret = qemuMonitorJSONMigrateToCommand(mon, background, argv, target); + else + ret = qemuMonitorTextMigrateToCommand(mon, background, argv, target); + return ret; } int qemuMonitorMigrateToUnix(qemuMonitorPtr mon, int background, const char *unixfile) { + int ret; DEBUG("mon=%p fd=%d unixfile=%s", mon, mon->fd, unixfile); - return qemuMonitorTextMigrateToUnix(mon, background, unixfile); + if (mon->json) + ret = qemuMonitorJSONMigrateToUnix(mon, background, unixfile); + else + ret = qemuMonitorTextMigrateToUnix(mon, background, unixfile); + return ret; } int qemuMonitorMigrateCancel(qemuMonitorPtr mon) { + int ret; DEBUG("mon=%p fd=%d", mon, mon->fd); - return qemuMonitorTextMigrateCancel(mon); + if (mon->json) + ret = qemuMonitorJSONMigrateCancel(mon); + else + ret = qemuMonitorTextMigrateCancel(mon); + return ret; } int qemuMonitorAddUSBDisk(qemuMonitorPtr mon, const char *path) { + int ret; DEBUG("mon=%p, fd=%d path=%s", mon, mon->fd, path); - return qemuMonitorTextAddUSBDisk(mon, path); + if (mon->json) + ret = qemuMonitorJSONAddUSBDisk(mon, path); + else + ret = qemuMonitorTextAddUSBDisk(mon, path); + return ret; } @@ -886,19 +996,29 @@ int qemuMonitorAddUSBDeviceExact(qemuMonitorPtr mon, int bus, int dev) { + int ret; DEBUG("mon=%p, fd=%d bus=%d dev=%d", mon, mon->fd, bus, dev); - return qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceExact(mon, bus, dev); + else + ret = qemuMonitorTextAddUSBDeviceExact(mon, bus, dev); + return ret; } int qemuMonitorAddUSBDeviceMatch(qemuMonitorPtr mon, int vendor, int product) { + int ret; DEBUG("mon=%p, fd=%d vendor=%d product=%d", mon, mon->fd, vendor, product); - return qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + if (mon->json) + ret = qemuMonitorJSONAddUSBDeviceMatch(mon, vendor, product); + else + ret = qemuMonitorTextAddUSBDeviceMatch(mon, vendor, product); + return ret; } @@ -911,16 +1031,26 @@ int qemuMonitorAddPCIHostDevice(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d function=%d", mon, mon->fd, hostDomain, hostBus, hostSlot, hostFunction); - return qemuMonitorTextAddPCIHostDevice(mon, hostDomain, - hostBus, hostSlot, - hostFunction, - guestDomain, - guestBus, - guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + else + ret = qemuMonitorTextAddPCIHostDevice(mon, hostDomain, + hostBus, hostSlot, + hostFunction, + guestDomain, + guestBus, + guestSlot); + return ret; } @@ -931,11 +1061,17 @@ int qemuMonitorAddPCIDisk(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d path=%s bus=%s", mon, mon->fd, path, bus); - return qemuMonitorTextAddPCIDisk(mon, path, bus, - guestDomain, guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCIDisk(mon, path, bus, + guestDomain, guestBus, guestSlot); + return ret; } @@ -945,10 +1081,16 @@ int qemuMonitorAddPCINetwork(qemuMonitorPtr mon, unsigned *guestBus, unsigned *guestSlot) { + int ret; DEBUG("mon=%p, fd=%d nicstr=%s", mon, mon->fd, nicstr); - return qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextAddPCINetwork(mon, nicstr, guestDomain, + guestBus, guestSlot); + return ret; } @@ -957,11 +1099,17 @@ int qemuMonitorRemovePCIDevice(qemuMonitorPtr mon, unsigned guestBus, unsigned guestSlot) { + int ret; DEBUG("mon=%p, fd=%d domain=%d bus=%d slot=%d", mon, mon->fd, guestDomain, guestBus, guestSlot); - return qemuMonitorTextRemovePCIDevice(mon, guestDomain, - guestBus, guestSlot); + if (mon->json) + ret = qemuMonitorJSONRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + else + ret = qemuMonitorTextRemovePCIDevice(mon, guestDomain, + guestBus, guestSlot); + return ret; } @@ -969,30 +1117,45 @@ int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s fd=%d", mon, mon->fd, fdname, fd); - return qemuMonitorTextSendFileHandle(mon, fdname, fd); + if (mon->json) + ret = qemuMonitorJSONSendFileHandle(mon, fdname, fd); + else + ret = qemuMonitorTextSendFileHandle(mon, fdname, fd); + return ret; } int qemuMonitorCloseFileHandle(qemuMonitorPtr mon, const char *fdname) { + int ret; DEBUG("mon=%p, fd=%d fdname=%s", mon, mon->fd, fdname); - return qemuMonitorTextCloseFileHandle(mon, fdname); + if (mon->json) + ret = qemuMonitorJSONCloseFileHandle(mon, fdname); + else + ret = qemuMonitorTextCloseFileHandle(mon, fdname); + return ret; } int qemuMonitorAddHostNetwork(qemuMonitorPtr mon, const char *netstr) { + int ret; DEBUG("mon=%p, fd=%d netstr=%s", mon, mon->fd, netstr); - return qemuMonitorTextAddHostNetwork(mon, netstr); + if (mon->json) + ret = qemuMonitorJSONAddHostNetwork(mon, netstr); + else + ret = qemuMonitorTextAddHostNetwork(mon, netstr); + return ret; } @@ -1000,8 +1163,13 @@ int qemuMonitorRemoveHostNetwork(qemuMonitorPtr mon, int vlan, const char *netname) { + int ret; DEBUG("mon=%p, fd=%d netname=%s", mon, mon->fd, netname); - return qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + if (mon->json) + ret = qemuMonitorJSONRemoveHostNetwork(mon, vlan, netname); + else + ret = qemuMonitorTextRemoveHostNetwork(mon, vlan, netname); + return ret; } diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 5f1a65d068..0d9e315608 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -80,6 +80,7 @@ char *qemuMonitorEscapeShell(const char *in); qemuMonitorPtr qemuMonitorOpen(virDomainObjPtr vm, virDomainChrDefPtr config, + int json, qemuMonitorEOFNotify eofCB); int qemuMonitorClose(qemuMonitorPtr mon); @@ -132,6 +133,9 @@ int qemuMonitorSetBalloon(qemuMonitorPtr mon, /* XXX should we pass the virDomainDiskDefPtr instead * and hide devname details inside monitor. Reconsider * this when doing the QMP implementation + * + * XXXX 'eject' has gained a 'force' flag we might like + * to make use of... */ int qemuMonitorEjectMedia(qemuMonitorPtr mon, const char *devname); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c new file mode 100644 index 0000000000..1aab304321 --- /dev/null +++ b/src/qemu/qemu_monitor_json.c @@ -0,0 +1,1426 @@ +/* + * 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 + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h new file mode 100644 index 0000000000..62a88c0093 --- /dev/null +++ b/src/qemu/qemu_monitor_json.h @@ -0,0 +1,156 @@ +/* + * qemu_monitor_json.h: interaction with QEMU monitor console + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange + */ + + +#ifndef QEMU_MONITOR_JSON_H +#define QEMU_MONITOR_JSON_H + +#include "internal.h" + +#include "qemu_monitor.h" + +int qemuMonitorJSONIOProcess(qemuMonitorPtr mon, + const char *data, + size_t len, + qemuMonitorMessagePtr msg); + +int qemuMonitorJSONStartCPUs(qemuMonitorPtr mon, + virConnectPtr conn); +int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon); + +int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon); + +int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon, + int **pids); +int qemuMonitorJSONGetBalloonInfo(qemuMonitorPtr mon, + unsigned long *currmem); +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 qemuMonitorJSONSetVNCPassword(qemuMonitorPtr mon, + const char *password); +int qemuMonitorJSONSetBalloon(qemuMonitorPtr mon, + unsigned long newmem); + +int qemuMonitorJSONEjectMedia(qemuMonitorPtr mon, + const char *devname); +int qemuMonitorJSONChangeMedia(qemuMonitorPtr mon, + const char *devname, + const char *newmedia, + const char *format); + + +int qemuMonitorJSONSaveVirtualMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); +int qemuMonitorJSONSavePhysicalMemory(qemuMonitorPtr mon, + unsigned long long offset, + size_t length, + const char *path); + +int qemuMonitorJSONSetMigrationSpeed(qemuMonitorPtr mon, + unsigned long bandwidth); + +int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, + int *status, + unsigned long long *transferred, + unsigned long long *remaining, + unsigned long long *total); + +int qemuMonitorJSONMigrateToHost(qemuMonitorPtr mon, + int background, + const char *hostname, + int port); + +int qemuMonitorJSONMigrateToCommand(qemuMonitorPtr mon, + int background, + const char * const *argv, + const char *target); + +int qemuMonitorJSONMigrateToUnix(qemuMonitorPtr mon, + int background, + const char *unixfile); + +int qemuMonitorJSONMigrateCancel(qemuMonitorPtr mon); + +int qemuMonitorJSONAddUSBDisk(qemuMonitorPtr mon, + const char *path); + +int qemuMonitorJSONAddUSBDeviceExact(qemuMonitorPtr mon, + int bus, + int dev); +int qemuMonitorJSONAddUSBDeviceMatch(qemuMonitorPtr mon, + int vendor, + int product); + + +int qemuMonitorJSONAddPCIHostDevice(qemuMonitorPtr mon, + unsigned hostDomain, + unsigned hostBus, + unsigned hostSlot, + unsigned hostFunction, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCIDisk(qemuMonitorPtr mon, + const char *path, + const char *bus, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, + const char *nicstr, + unsigned *guestDomain, + unsigned *guestBus, + unsigned *guestSlot); + +int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, + unsigned guestDomain, + unsigned guestBus, + unsigned guestSlot); + + +int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, + const char *fdname, + int fd); + +int qemuMonitorJSONCloseFileHandle(qemuMonitorPtr mon, + const char *fdname); + +int qemuMonitorJSONAddHostNetwork(qemuMonitorPtr mon, + const char *netstr); + +int qemuMonitorJSONRemoveHostNetwork(qemuMonitorPtr mon, + int vlan, + const char *netname); + +#endif /* QEMU_MONITOR_JSON_H */ diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 677c5b4ccb..c39cbbc1fb 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -60,7 +60,7 @@ static int testCompareXMLToArgvFiles(const char *xml, goto fail; if (qemudBuildCommandLine(NULL, &driver, - vmdef, &monitor_chr, flags, + vmdef, &monitor_chr, 0, flags, &argv, &qenv, NULL, NULL, migrateFrom) < 0) goto fail;