[libvirt] [PATCH RFC 1/4] qemu_agent: move agent into util

Joao Martins joao.m.martins at oracle.com
Wed Feb 8 16:44:37 UTC 2017


As it could be shared with libxl which now allows channels to
be created. Also changed filename to match others in the same
directory namely to virqemuagent.{h,c}

Signed-off-by: Joao Martins <joao.m.martins at oracle.com>
---
 po/POTFILES.in               |    2 +-
 src/Makefile.am              |    2 +-
 src/libvirt_private.syms     |   21 +
 src/qemu/qemu_agent.c        | 2248 ------------------------------------------
 src/qemu/qemu_agent.h        |  123 ---
 src/qemu/qemu_domain.h       |    2 +-
 src/qemu/qemu_driver.c       |    2 +-
 src/util/virqemuagent.c      | 2248 ++++++++++++++++++++++++++++++++++++++++++
 src/util/virqemuagent.h      |  123 +++
 tests/qemuagenttest.c        |    2 +-
 tests/qemumonitortestutils.c |    2 +-
 tests/qemumonitortestutils.h |    2 +-
 12 files changed, 2399 insertions(+), 2378 deletions(-)
 delete mode 100644 src/qemu/qemu_agent.c
 delete mode 100644 src/qemu/qemu_agent.h
 create mode 100644 src/util/virqemuagent.c
 create mode 100644 src/util/virqemuagent.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index 365ea66..ebb247b 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -122,7 +122,6 @@ src/openvz/openvz_conf.c
 src/openvz/openvz_driver.c
 src/openvz/openvz_util.c
 src/phyp/phyp_driver.c
-src/qemu/qemu_agent.c
 src/qemu/qemu_alias.c
 src/qemu/qemu_capabilities.c
 src/qemu/qemu_cgroup.c
@@ -239,6 +238,7 @@ src/util/virpolkit.c
 src/util/virportallocator.c
 src/util/virprocess.c
 src/util/virqemu.c
+src/util/virqemuagent.c
 src/util/virrandom.c
 src/util/virrotatingfile.c
 src/util/virscsi.c
diff --git a/src/Makefile.am b/src/Makefile.am
index 2f32d41..62c8733 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -161,6 +161,7 @@ UTIL_SOURCES =							\
 		util/virprobe.h					\
 		util/virprocess.c util/virprocess.h		\
 		util/virqemu.c util/virqemu.h			\
+		util/virqemuagent.c util/virqemuagent.h		\
 		util/virrandom.h util/virrandom.c		\
 		util/virrotatingfile.h util/virrotatingfile.c   \
 		util/virscsi.c util/virscsi.h			\
@@ -815,7 +816,6 @@ VBOX_DRIVER_EXTRA_DIST =					\
 		vbox/vbox_XPCOMCGlue.c vbox/vbox_XPCOMCGlue.h
 
 QEMU_DRIVER_SOURCES =							\
-		qemu/qemu_agent.c qemu/qemu_agent.h			\
 		qemu/qemu_alias.c qemu/qemu_alias.h			\
 		qemu/qemu_blockjob.c qemu/qemu_blockjob.h		\
 		qemu/qemu_capabilities.c qemu/qemu_capabilities.h	\
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index d556c7d..a5a1313 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -2306,6 +2306,27 @@ virQEMUBuildLuksOpts;
 virQEMUBuildObjectCommandlineFromJSON;
 
 
+# util/virqemuagent.h
+qemuAgentArbitraryCommand;
+qemuAgentClose;
+qemuAgentFSFreeze;
+qemuAgentFSThaw;
+qemuAgentFSTrim;
+qemuAgentGetFSInfo;
+qemuAgentGetInterfaces;
+qemuAgentGetTime;
+qemuAgentGetVCPUs;
+qemuAgentNotifyClose;
+qemuAgentNotifyEvent;
+qemuAgentOpen;
+qemuAgentSetVCPUs;
+qemuAgentSetUserPassword;
+qemuAgentSetTime;
+qemuAgentShutdown;
+qemuAgentSuspend;
+qemuAgentUpdateCPUInfo;
+
+
 # util/virrandom.h
 virRandom;
 virRandomBits;
diff --git a/src/qemu/qemu_agent.c b/src/qemu/qemu_agent.c
deleted file mode 100644
index 46cad53..0000000
--- a/src/qemu/qemu_agent.c
+++ /dev/null
@@ -1,2248 +0,0 @@
-/*
- * qemu_agent.c: interaction with QEMU guest agent
- *
- * 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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange at redhat.com>
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <poll.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "qemu_agent.h"
-#include "viralloc.h"
-#include "virlog.h"
-#include "virerror.h"
-#include "virjson.h"
-#include "virfile.h"
-#include "virprocess.h"
-#include "virtime.h"
-#include "virobject.h"
-#include "virstring.h"
-#include "base64.h"
-
-#define VIR_FROM_THIS VIR_FROM_QEMU
-
-VIR_LOG_INIT("qemu.qemu_agent");
-
-#define LINE_ENDING "\n"
-
-#define DEBUG_IO 0
-#define DEBUG_RAW_IO 0
-
-/* When you are the first to uncomment this,
- * don't forget to uncomment the corresponding
- * part in qemuAgentIOProcessEvent as well.
- *
-static struct {
-    const char *type;
-    void (*handler)(qemuAgentPtr mon, virJSONValuePtr data);
-} eventHandlers[] = {
-};
-*/
-
-typedef struct _qemuAgentMessage qemuAgentMessage;
-typedef qemuAgentMessage *qemuAgentMessagePtr;
-
-struct _qemuAgentMessage {
-    char *txBuffer;
-    int txOffset;
-    int txLength;
-
-    /* Used by the JSON monitor to hold reply / error */
-    char *rxBuffer;
-    int rxLength;
-    void *rxObject;
-
-    /* True if rxBuffer / rxObject are ready, or a
-     * fatal error occurred on the monitor channel
-     */
-    bool finished;
-    /* true for sync command */
-    bool sync;
-    /* id of the issued sync comand */
-    unsigned long long id;
-    bool first;
-};
-
-
-struct _qemuAgent {
-    virObjectLockable parent;
-
-    virCond notify;
-
-    int fd;
-    int watch;
-
-    bool connectPending;
-    bool running;
-
-    virDomainObjPtr vm;
-
-    qemuAgentCallbacksPtr cb;
-
-    /* If there's a command being processed this will be
-     * non-NULL */
-    qemuAgentMessagePtr msg;
-
-    /* Buffer incoming data ready for Agent monitor
-     * code to process & find message boundaries */
-    size_t bufferOffset;
-    size_t bufferLength;
-    char *buffer;
-
-    /* If anything went wrong, this will be fed back
-     * the next monitor msg */
-    virError lastError;
-
-    /* Some guest agent commands don't return anything
-     * but fire up an event on qemu monitor instead.
-     * Take that as indication of successful completion */
-    qemuAgentEvent await_event;
-};
-
-static virClassPtr qemuAgentClass;
-static void qemuAgentDispose(void *obj);
-
-static int qemuAgentOnceInit(void)
-{
-    if (!(qemuAgentClass = virClassNew(virClassForObjectLockable(),
-                                       "qemuAgent",
-                                       sizeof(qemuAgent),
-                                       qemuAgentDispose)))
-        return -1;
-
-    return 0;
-}
-
-VIR_ONCE_GLOBAL_INIT(qemuAgent)
-
-
-#if DEBUG_RAW_IO
-# include <c-ctype.h>
-static char *
-qemuAgentEscapeNonPrintable(const char *text)
-{
-    size_t i;
-    virBuffer buf = VIR_BUFFER_INITIALIZER;
-    for (i = 0; text[i] != '\0'; i++) {
-        if (text[i] == '\\')
-            virBufferAddLit(&buf, "\\\\");
-        else if (c_isprint(text[i]) || text[i] == '\n' ||
-                 (text[i] == '\r' && text[i+1] == '\n'))
-            virBufferAddChar(&buf, text[i]);
-        else
-            virBufferAsprintf(&buf, "\\x%02x", text[i]);
-    }
-    return virBufferContentAndReset(&buf);
-}
-#endif
-
-
-static void qemuAgentDispose(void *obj)
-{
-    qemuAgentPtr mon = obj;
-    VIR_DEBUG("mon=%p", mon);
-    if (mon->cb && mon->cb->destroy)
-        (mon->cb->destroy)(mon, mon->vm);
-    virCondDestroy(&mon->notify);
-    VIR_FREE(mon->buffer);
-    virResetError(&mon->lastError);
-}
-
-static int
-qemuAgentOpenUnix(const char *monitor, pid_t cpid, bool *inProgress)
-{
-    struct sockaddr_un addr;
-    int monfd;
-    virTimeBackOffVar timeout;
-    int ret = -1;
-
-    *inProgress = false;
-
-    if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
-        virReportSystemError(errno,
-                             "%s", _("failed to create socket"));
-        return -1;
-    }
-
-    if (virSetNonBlock(monfd) < 0) {
-        virReportSystemError(errno, "%s",
-                             _("Unable to put monitor "
-                               "into non-blocking mode"));
-        goto error;
-    }
-
-    if (virSetCloseExec(monfd) < 0) {
-        virReportSystemError(errno, "%s",
-                             _("Unable to set monitor "
-                               "close-on-exec flag"));
-        goto error;
-    }
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_UNIX;
-    if (virStrcpyStatic(addr.sun_path, monitor) == NULL) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Agent path %s too big for destination"), monitor);
-        goto error;
-    }
-
-    if (virTimeBackOffStart(&timeout, 1, 3*1000 /* ms */) < 0)
-        goto error;
-    while (virTimeBackOffWait(&timeout)) {
-        ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr));
-
-        if (ret == 0)
-            break;
-
-        if ((errno == ENOENT || errno == ECONNREFUSED) &&
-            virProcessKill(cpid, 0) == 0) {
-            /* ENOENT       : Socket may not have shown up yet
-             * ECONNREFUSED : Leftover socket hasn't been removed yet */
-            continue;
-        }
-
-        if ((errno == EINPROGRESS) ||
-            (errno == EAGAIN)) {
-            VIR_DEBUG("Connection attempt continuing in background");
-            *inProgress = true;
-            ret = 0;
-            break;
-        }
-
-        virReportSystemError(errno, "%s",
-                             _("failed to connect to monitor socket"));
-        goto error;
-
-    }
-
-    if (ret != 0) {
-        virReportSystemError(errno, "%s",
-                             _("monitor socket did not show up"));
-        goto error;
-    }
-
-    return monfd;
-
- error:
-    VIR_FORCE_CLOSE(monfd);
-    return -1;
-}
-
-static int
-qemuAgentOpenPty(const char *monitor)
-{
-    int monfd;
-
-    if ((monfd = open(monitor, O_RDWR | O_NONBLOCK)) < 0) {
-        virReportSystemError(errno,
-                             _("Unable to open monitor path %s"), monitor);
-        return -1;
-    }
-
-    if (virSetCloseExec(monfd) < 0) {
-        virReportSystemError(errno, "%s",
-                             _("Unable to set monitor close-on-exec flag"));
-        goto error;
-    }
-
-    return monfd;
-
- error:
-    VIR_FORCE_CLOSE(monfd);
-    return -1;
-}
-
-
-static int
-qemuAgentIOProcessEvent(qemuAgentPtr mon,
-                        virJSONValuePtr obj)
-{
-    const char *type;
-    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;
-    }
-
-/*
-    for (i = 0; i < ARRAY_CARDINALITY(eventHandlers); i++) {
-        if (STREQ(eventHandlers[i].type, type)) {
-            virJSONValuePtr data = virJSONValueObjectGet(obj, "data");
-            VIR_DEBUG("handle %s handler=%p data=%p", type,
-                      eventHandlers[i].handler, data);
-            (eventHandlers[i].handler)(mon, data);
-            break;
-        }
-    }
-*/
-    return 0;
-}
-
-static int
-qemuAgentIOProcessLine(qemuAgentPtr mon,
-                       const char *line,
-                       qemuAgentMessagePtr msg)
-{
-    virJSONValuePtr obj = NULL;
-    int ret = -1;
-
-    VIR_DEBUG("Line [%s]", line);
-
-    if (!(obj = virJSONValueFromString(line))) {
-        /* receiving garbage on first sync is regular situation */
-        if (msg && msg->sync && msg->first) {
-            VIR_DEBUG("Received garbage on sync");
-            msg->finished = 1;
-            return 0;
-        }
-
-        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) {
-        ret = qemuAgentIOProcessEvent(mon, obj);
-    } else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
-               virJSONValueObjectHasKey(obj, "return") == 1) {
-        if (msg) {
-            if (msg->sync) {
-                unsigned long long id;
-
-                if (virJSONValueObjectGetNumberUlong(obj, "return", &id) < 0) {
-                    VIR_DEBUG("Ignoring delayed reply on sync");
-                    ret = 0;
-                    goto cleanup;
-                }
-
-                VIR_DEBUG("Guest returned ID: %llu", id);
-
-                if (msg->id != id) {
-                    VIR_DEBUG("Guest agent returned ID: %llu instead of %llu",
-                              id, msg->id);
-                    ret = 0;
-                    goto cleanup;
-                }
-            }
-            msg->rxObject = obj;
-            msg->finished = 1;
-            obj = NULL;
-        } else {
-            /* we are out of sync */
-            VIR_DEBUG("Ignoring delayed reply");
-        }
-        ret = 0;
-    } else {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Unknown JSON reply '%s'"), line);
-    }
-
- cleanup:
-    virJSONValueFree(obj);
-    return ret;
-}
-
-static int qemuAgentIOProcessData(qemuAgentPtr mon,
-                                  char *data,
-                                  size_t len,
-                                  qemuAgentMessagePtr msg)
-{
-    int used = 0;
-    size_t i = 0;
-#if DEBUG_IO
-# if DEBUG_RAW_IO
-    char *str1 = qemuAgentEscapeNonPrintable(data);
-    VIR_ERROR("[%s]", str1);
-    VIR_FREE(str1);
-# else
-    VIR_DEBUG("Data %zu bytes [%s]", len, data);
-# endif
-#endif
-
-    while (used < len) {
-        char *nl = strstr(data + used, LINE_ENDING);
-
-        if (nl) {
-            int got = nl - (data + used);
-            for (i = 0; i < strlen(LINE_ENDING); i++)
-                data[used + got + i] = '\0';
-            if (qemuAgentIOProcessLine(mon, data + used, msg) < 0)
-                return -1;
-            used += got + strlen(LINE_ENDING);
-        } else {
-            break;
-        }
-    }
-
-    VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len);
-    return used;
-}
-
-/* This method processes data that has been received
- * from the monitor. Looking for async events and
- * replies/errors.
- */
-static int
-qemuAgentIOProcess(qemuAgentPtr mon)
-{
-    int len;
-    qemuAgentMessagePtr msg = NULL;
-
-    /* See if there's a message ready for reply; that is,
-     * one that has completed writing all its data.
-     */
-    if (mon->msg && mon->msg->txOffset == mon->msg->txLength)
-        msg = mon->msg;
-
-#if DEBUG_IO
-# if DEBUG_RAW_IO
-    char *str1 = qemuAgentEscapeNonPrintable(msg ? msg->txBuffer : "");
-    char *str2 = qemuAgentEscapeNonPrintable(mon->buffer);
-    VIR_ERROR(_("Process %zu %p %p [[[%s]]][[[%s]]]"),
-              mon->bufferOffset, mon->msg, msg, str1, str2);
-    VIR_FREE(str1);
-    VIR_FREE(str2);
-# else
-    VIR_DEBUG("Process %zu", mon->bufferOffset);
-# endif
-#endif
-
-    len = qemuAgentIOProcessData(mon,
-                                 mon->buffer, mon->bufferOffset,
-                                 msg);
-
-    if (len < 0)
-        return -1;
-
-    if (len < mon->bufferOffset) {
-        memmove(mon->buffer, mon->buffer + len, mon->bufferOffset - len);
-        mon->bufferOffset -= len;
-    } else {
-        VIR_FREE(mon->buffer);
-        mon->bufferOffset = mon->bufferLength = 0;
-    }
-#if DEBUG_IO
-    VIR_DEBUG("Process done %zu used %d", mon->bufferOffset, len);
-#endif
-    if (msg && msg->finished)
-        virCondBroadcast(&mon->notify);
-    return len;
-}
-
-
-static int
-qemuAgentIOConnect(qemuAgentPtr mon)
-{
-    int optval;
-    socklen_t optlen;
-
-    VIR_DEBUG("Checking on background connection status");
-
-    mon->connectPending = false;
-
-    optlen = sizeof(optval);
-
-    if (getsockopt(mon->fd, SOL_SOCKET, SO_ERROR,
-                   &optval, &optlen) < 0) {
-        virReportSystemError(errno, "%s",
-                             _("Cannot check socket connection status"));
-        return -1;
-    }
-
-    if (optval != 0) {
-        virReportSystemError(optval, "%s",
-                             _("Cannot connect to agent socket"));
-        return -1;
-    }
-
-    VIR_DEBUG("Agent is now connected");
-    return 0;
-}
-
-/*
- * Called when the monitor is able to write data
- * Call this function while holding the monitor lock.
- */
-static int
-qemuAgentIOWrite(qemuAgentPtr mon)
-{
-    int done;
-
-    /* If no active message, or fully transmitted, then no-op */
-    if (!mon->msg || mon->msg->txOffset == mon->msg->txLength)
-        return 0;
-
-    done = safewrite(mon->fd,
-                     mon->msg->txBuffer + mon->msg->txOffset,
-                     mon->msg->txLength - mon->msg->txOffset);
-
-    if (done < 0) {
-        if (errno == EAGAIN)
-            return 0;
-
-        virReportSystemError(errno, "%s",
-                             _("Unable to write to monitor"));
-        return -1;
-    }
-    mon->msg->txOffset += done;
-    return done;
-}
-
-/*
- * Called when the monitor has incoming data to read
- * Call this function while holding the monitor lock.
- *
- * Returns -1 on error, or number of bytes read
- */
-static int
-qemuAgentIORead(qemuAgentPtr mon)
-{
-    size_t avail = mon->bufferLength - mon->bufferOffset;
-    int ret = 0;
-
-    if (avail < 1024) {
-        if (VIR_REALLOC_N(mon->buffer,
-                          mon->bufferLength + 1024) < 0)
-            return -1;
-        mon->bufferLength += 1024;
-        avail += 1024;
-    }
-
-    /* Read as much as we can get into our buffer,
-       until we block on EAGAIN, or hit EOF */
-    while (avail > 1) {
-        int got;
-        got = read(mon->fd,
-                   mon->buffer + mon->bufferOffset,
-                   avail - 1);
-        if (got < 0) {
-            if (errno == EAGAIN)
-                break;
-            virReportSystemError(errno, "%s",
-                                 _("Unable to read from monitor"));
-            ret = -1;
-            break;
-        }
-        if (got == 0)
-            break;
-
-        ret += got;
-        avail -= got;
-        mon->bufferOffset += got;
-        mon->buffer[mon->bufferOffset] = '\0';
-    }
-
-#if DEBUG_IO
-    VIR_DEBUG("Now read %zu bytes of data", mon->bufferOffset);
-#endif
-
-    return ret;
-}
-
-
-static void qemuAgentUpdateWatch(qemuAgentPtr mon)
-{
-    int events =
-        VIR_EVENT_HANDLE_HANGUP |
-        VIR_EVENT_HANDLE_ERROR;
-
-    if (mon->lastError.code == VIR_ERR_OK) {
-        events |= VIR_EVENT_HANDLE_READABLE;
-
-        if (mon->msg && mon->msg->txOffset < mon->msg->txLength)
-            events |= VIR_EVENT_HANDLE_WRITABLE;
-    }
-
-    virEventUpdateHandle(mon->watch, events);
-}
-
-
-static void
-qemuAgentIO(int watch, int fd, int events, void *opaque)
-{
-    qemuAgentPtr mon = opaque;
-    bool error = false;
-    bool eof = false;
-
-    virObjectRef(mon);
-    /* lock access to the monitor and protect fd */
-    virObjectLock(mon);
-#if DEBUG_IO
-    VIR_DEBUG("Agent %p I/O on watch %d fd %d events %d", mon, watch, fd, events);
-#endif
-
-    if (mon->fd != fd || mon->watch != watch) {
-        if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
-            eof = true;
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("event from unexpected fd %d!=%d / watch %d!=%d"),
-                       mon->fd, fd, mon->watch, watch);
-        error = true;
-    } else if (mon->lastError.code != VIR_ERR_OK) {
-        if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
-            eof = true;
-        error = true;
-    } else {
-        if (events & VIR_EVENT_HANDLE_WRITABLE) {
-            if (mon->connectPending) {
-                if (qemuAgentIOConnect(mon) < 0)
-                    error = true;
-            } else {
-                if (qemuAgentIOWrite(mon) < 0)
-                    error = true;
-            }
-            events &= ~VIR_EVENT_HANDLE_WRITABLE;
-        }
-
-        if (!error &&
-            events & VIR_EVENT_HANDLE_READABLE) {
-            int got = qemuAgentIORead(mon);
-            events &= ~VIR_EVENT_HANDLE_READABLE;
-            if (got < 0) {
-                error = true;
-            } else if (got == 0) {
-                eof = true;
-            } else {
-                /* Ignore hangup/error events if we read some data, to
-                 * give time for that data to be consumed */
-                events = 0;
-
-                if (qemuAgentIOProcess(mon) < 0)
-                    error = true;
-            }
-        }
-
-        if (!error &&
-            events & VIR_EVENT_HANDLE_HANGUP) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("End of file from agent monitor"));
-            eof = true;
-            events &= ~VIR_EVENT_HANDLE_HANGUP;
-        }
-
-        if (!error && !eof &&
-            events & VIR_EVENT_HANDLE_ERROR) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("Invalid file descriptor while waiting for monitor"));
-            eof = true;
-            events &= ~VIR_EVENT_HANDLE_ERROR;
-        }
-        if (!error && events) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("Unhandled event %d for monitor fd %d"),
-                           events, mon->fd);
-            error = true;
-        }
-    }
-
-    if (error || eof) {
-        if (mon->lastError.code != VIR_ERR_OK) {
-            /* Already have an error, so clear any new error */
-            virResetLastError();
-        } else {
-            virErrorPtr err = virGetLastError();
-            if (!err)
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("Error while processing monitor IO"));
-            virCopyLastError(&mon->lastError);
-            virResetLastError();
-        }
-
-        VIR_DEBUG("Error on monitor %s", NULLSTR(mon->lastError.message));
-        /* If IO process resulted in an error & we have a message,
-         * then wakeup that waiter */
-        if (mon->msg && !mon->msg->finished) {
-            mon->msg->finished = 1;
-            virCondSignal(&mon->notify);
-        }
-    }
-
-    qemuAgentUpdateWatch(mon);
-
-    /* We have to unlock to avoid deadlock against command thread,
-     * but is this safe ?  I think it is, because the callback
-     * will try to acquire the virDomainObjPtr mutex next */
-    if (eof) {
-        void (*eofNotify)(qemuAgentPtr, virDomainObjPtr)
-            = mon->cb->eofNotify;
-        virDomainObjPtr vm = mon->vm;
-
-        /* Make sure anyone waiting wakes up now */
-        virCondSignal(&mon->notify);
-        virObjectUnlock(mon);
-        virObjectUnref(mon);
-        VIR_DEBUG("Triggering EOF callback");
-        (eofNotify)(mon, vm);
-    } else if (error) {
-        void (*errorNotify)(qemuAgentPtr, virDomainObjPtr)
-            = mon->cb->errorNotify;
-        virDomainObjPtr vm = mon->vm;
-
-        /* Make sure anyone waiting wakes up now */
-        virCondSignal(&mon->notify);
-        virObjectUnlock(mon);
-        virObjectUnref(mon);
-        VIR_DEBUG("Triggering error callback");
-        (errorNotify)(mon, vm);
-    } else {
-        virObjectUnlock(mon);
-        virObjectUnref(mon);
-    }
-}
-
-
-qemuAgentPtr
-qemuAgentOpen(virDomainObjPtr vm,
-              const virDomainChrSourceDef *config,
-              qemuAgentCallbacksPtr cb)
-{
-    qemuAgentPtr mon;
-
-    if (!cb || !cb->eofNotify) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("EOF notify callback must be supplied"));
-        return NULL;
-    }
-
-    if (qemuAgentInitialize() < 0)
-        return NULL;
-
-    if (!(mon = virObjectLockableNew(qemuAgentClass)))
-        return NULL;
-
-    mon->fd = -1;
-    if (virCondInit(&mon->notify) < 0) {
-        virReportSystemError(errno, "%s",
-                             _("cannot initialize monitor condition"));
-        virObjectUnref(mon);
-        return NULL;
-    }
-    mon->vm = vm;
-    mon->cb = cb;
-
-    switch (config->type) {
-    case VIR_DOMAIN_CHR_TYPE_UNIX:
-        mon->fd = qemuAgentOpenUnix(config->data.nix.path, vm->pid,
-                                    &mon->connectPending);
-        break;
-
-    case VIR_DOMAIN_CHR_TYPE_PTY:
-        mon->fd = qemuAgentOpenPty(config->data.file.path);
-        break;
-
-    default:
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("unable to handle monitor type: %s"),
-                       virDomainChrTypeToString(config->type));
-        goto cleanup;
-    }
-
-    if (mon->fd == -1)
-        goto cleanup;
-
-    virObjectRef(mon);
-    if ((mon->watch = virEventAddHandle(mon->fd,
-                                        VIR_EVENT_HANDLE_HANGUP |
-                                        VIR_EVENT_HANDLE_ERROR |
-                                        VIR_EVENT_HANDLE_READABLE |
-                                        (mon->connectPending ?
-                                         VIR_EVENT_HANDLE_WRITABLE :
-                                         0),
-                                        qemuAgentIO,
-                                        mon,
-                                        virObjectFreeCallback)) < 0) {
-        virObjectUnref(mon);
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("unable to register monitor events"));
-        goto cleanup;
-    }
-
-    mon->running = true;
-    VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch);
-
-    return mon;
-
- cleanup:
-    /* We don't want the 'destroy' callback invoked during
-     * cleanup from construction failure, because that can
-     * give a double-unref on virDomainObjPtr in the caller,
-     * so kill the callbacks now.
-     */
-    mon->cb = NULL;
-    qemuAgentClose(mon);
-    return NULL;
-}
-
-
-static void
-qemuAgentNotifyCloseLocked(qemuAgentPtr mon)
-{
-    if (mon) {
-        mon->running = false;
-
-        /* If there is somebody waiting for a message
-         * wake him up. No message will arrive anyway. */
-        if (mon->msg && !mon->msg->finished) {
-            mon->msg->finished = 1;
-            virCondSignal(&mon->notify);
-        }
-    }
-}
-
-
-void
-qemuAgentNotifyClose(qemuAgentPtr mon)
-{
-    if (!mon)
-        return;
-
-    VIR_DEBUG("mon=%p", mon);
-
-    virObjectLock(mon);
-    qemuAgentNotifyCloseLocked(mon);
-    virObjectUnlock(mon);
-}
-
-
-void qemuAgentClose(qemuAgentPtr mon)
-{
-    if (!mon)
-        return;
-
-    VIR_DEBUG("mon=%p", mon);
-
-    virObjectLock(mon);
-
-    if (mon->fd >= 0) {
-        if (mon->watch)
-            virEventRemoveHandle(mon->watch);
-        VIR_FORCE_CLOSE(mon->fd);
-    }
-
-    qemuAgentNotifyCloseLocked(mon);
-    virObjectUnlock(mon);
-
-    virObjectUnref(mon);
-}
-
-#define QEMU_AGENT_WAIT_TIME 5
-
-/**
- * qemuAgentSend:
- * @mon: Monitor
- * @msg: Message
- * @seconds: number of seconds to wait for the result, it can be either
- *           -2, -1, 0 or positive.
- *
- * Send @msg to agent @mon. If @seconds is equal to
- * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2), this function will block forever
- * waiting for the result. The value of
- * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) means use default timeout value
- * and VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0) makes this this function return
- * immediately without waiting. Any positive value means the number of seconds
- * to wait for the result.
- *
- * Returns: 0 on success,
- *          -2 on timeout,
- *          -1 otherwise
- */
-static int qemuAgentSend(qemuAgentPtr mon,
-                         qemuAgentMessagePtr msg,
-                         int seconds)
-{
-    int ret = -1;
-    unsigned long long then = 0;
-
-    /* Check whether qemu quit unexpectedly */
-    if (mon->lastError.code != VIR_ERR_OK) {
-        VIR_DEBUG("Attempt to send command while error is set %s",
-                  NULLSTR(mon->lastError.message));
-        virSetError(&mon->lastError);
-        return -1;
-    }
-
-    if (seconds > VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) {
-        unsigned long long now;
-        if (virTimeMillisNow(&now) < 0)
-            return -1;
-        if (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT)
-            seconds = QEMU_AGENT_WAIT_TIME;
-        then = now + seconds * 1000ull;
-    }
-
-    mon->msg = msg;
-    qemuAgentUpdateWatch(mon);
-
-    while (!mon->msg->finished) {
-        if ((then && virCondWaitUntil(&mon->notify, &mon->parent.lock, then) < 0) ||
-            (!then && virCondWait(&mon->notify, &mon->parent.lock) < 0)) {
-            if (errno == ETIMEDOUT) {
-                virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
-                               _("Guest agent not available for now"));
-                ret = -2;
-            } else {
-                virReportSystemError(errno, "%s",
-                                     _("Unable to wait on agent monitor "
-                                       "condition"));
-            }
-            goto cleanup;
-        }
-    }
-
-    if (mon->lastError.code != VIR_ERR_OK) {
-        VIR_DEBUG("Send command resulted in error %s",
-                  NULLSTR(mon->lastError.message));
-        virSetError(&mon->lastError);
-        goto cleanup;
-    }
-
-    ret = 0;
-
- cleanup:
-    mon->msg = NULL;
-    qemuAgentUpdateWatch(mon);
-
-    return ret;
-}
-
-
-/**
- * qemuAgentGuestSync:
- * @mon: Monitor
- *
- * Send guest-sync with unique ID
- * and wait for reply. If we get one, check if
- * received ID is equal to given.
- *
- * Returns: 0 on success,
- *          -1 otherwise
- */
-static int
-qemuAgentGuestSync(qemuAgentPtr mon)
-{
-    int ret = -1;
-    int send_ret;
-    unsigned long long id;
-    qemuAgentMessage sync_msg;
-
-    memset(&sync_msg, 0, sizeof(sync_msg));
-    /* set only on first sync */
-    sync_msg.first = true;
-
- retry:
-    if (virTimeMillisNow(&id) < 0)
-        return -1;
-
-    if (virAsprintf(&sync_msg.txBuffer,
-                    "{\"execute\":\"guest-sync\", "
-                    "\"arguments\":{\"id\":%llu}}\n", id) < 0)
-        return -1;
-
-    sync_msg.txLength = strlen(sync_msg.txBuffer);
-    sync_msg.sync = true;
-    sync_msg.id = id;
-
-    VIR_DEBUG("Sending guest-sync command with ID: %llu", id);
-
-    send_ret = qemuAgentSend(mon, &sync_msg,
-                             VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT);
-
-    VIR_DEBUG("qemuAgentSend returned: %d", send_ret);
-
-    if (send_ret < 0)
-        goto cleanup;
-
-    if (!sync_msg.rxObject) {
-        if (sync_msg.first) {
-            VIR_FREE(sync_msg.txBuffer);
-            memset(&sync_msg, 0, sizeof(sync_msg));
-            goto retry;
-        } else {
-            if (mon->running)
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("Missing monitor reply object"));
-            else
-                virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
-                               _("Guest agent disappeared while executing command"));
-            goto cleanup;
-        }
-    }
-
-    ret = 0;
-
- cleanup:
-    virJSONValueFree(sync_msg.rxObject);
-    VIR_FREE(sync_msg.txBuffer);
-    return ret;
-}
-
-static const char *
-qemuAgentStringifyErrorClass(const char *klass)
-{
-    if (STREQ_NULLABLE(klass, "BufferOverrun"))
-        return "Buffer overrun";
-    else if (STREQ_NULLABLE(klass, "CommandDisabled"))
-        return "The command has been disabled for this instance";
-    else if (STREQ_NULLABLE(klass, "CommandNotFound"))
-        return "The command has not been found";
-    else if (STREQ_NULLABLE(klass, "FdNotFound"))
-        return "File descriptor not found";
-    else if (STREQ_NULLABLE(klass, "InvalidParameter"))
-        return "Invalid parameter";
-    else if (STREQ_NULLABLE(klass, "InvalidParameterType"))
-        return "Invalid parameter type";
-    else if (STREQ_NULLABLE(klass, "InvalidParameterValue"))
-        return "Invalid parameter value";
-    else if (STREQ_NULLABLE(klass, "OpenFileFailed"))
-        return "Cannot open file";
-    else if (STREQ_NULLABLE(klass, "QgaCommandFailed"))
-        return "Guest agent command failed";
-    else if (STREQ_NULLABLE(klass, "QMPBadInputObjectMember"))
-        return "Bad QMP input object member";
-    else if (STREQ_NULLABLE(klass, "QMPExtraInputObjectMember"))
-        return "Unexpected extra object member";
-    else if (STREQ_NULLABLE(klass, "UndefinedError"))
-        return "An undefined error has occurred";
-    else if (STREQ_NULLABLE(klass, "Unsupported"))
-        return "this feature or command is not currently supported";
-    else if (klass)
-        return klass;
-    else
-        return "unknown QEMU command error";
-}
-
-/* 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 *
-qemuAgentStringifyError(virJSONValuePtr error)
-{
-    const char *klass = virJSONValueObjectGetString(error, "class");
-    const char *detail = virJSONValueObjectGetString(error, "desc");
-
-    /* The QMP 'desc' field is usually sufficient for our generic
-     * error reporting needs. However, if not present, translate
-     * the class into something readable.
-     */
-    if (!detail)
-        detail = qemuAgentStringifyErrorClass(klass);
-
-    return detail;
-}
-
-static const char *
-qemuAgentCommandName(virJSONValuePtr cmd)
-{
-    const char *name = virJSONValueObjectGetString(cmd, "execute");
-    if (name)
-        return name;
-    else
-        return "<unknown>";
-}
-
-static int
-qemuAgentCheckError(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 agent 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 agent command '%s'"),
-                           qemuAgentCommandName(cmd));
-        else
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("unable to execute QEMU agent command '%s': %s"),
-                           qemuAgentCommandName(cmd),
-                           qemuAgentStringifyError(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 agent command '%s'"),
-                       qemuAgentCommandName(cmd));
-        VIR_FREE(cmdstr);
-        VIR_FREE(replystr);
-        return -1;
-    }
-    return 0;
-}
-
-static int
-qemuAgentCommand(qemuAgentPtr mon,
-                 virJSONValuePtr cmd,
-                 virJSONValuePtr *reply,
-                 bool needReply,
-                 int seconds)
-{
-    int ret = -1;
-    qemuAgentMessage msg;
-    char *cmdstr = NULL;
-    int await_event = mon->await_event;
-
-    *reply = NULL;
-
-    if (!mon->running) {
-        virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
-                       _("Guest agent disappeared while executing command"));
-        return -1;
-    }
-
-    if (qemuAgentGuestSync(mon) < 0)
-        return -1;
-
-    memset(&msg, 0, sizeof(msg));
-
-    if (!(cmdstr = virJSONValueToString(cmd, false)))
-        goto cleanup;
-    if (virAsprintf(&msg.txBuffer, "%s" LINE_ENDING, cmdstr) < 0)
-        goto cleanup;
-    msg.txLength = strlen(msg.txBuffer);
-
-    VIR_DEBUG("Send command '%s' for write, seconds = %d", cmdstr, seconds);
-
-    ret = qemuAgentSend(mon, &msg, seconds);
-
-    VIR_DEBUG("Receive command reply ret=%d rxObject=%p",
-              ret, msg.rxObject);
-
-    if (ret == 0) {
-        /* If we haven't obtained any reply but we wait for an
-         * event, then don't report this as error */
-        if (!msg.rxObject) {
-            if (await_event && !needReply) {
-                VIR_DEBUG("Woken up by event %d", await_event);
-            } else {
-                if (mon->running)
-                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                                   _("Missing monitor reply object"));
-                else
-                    virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
-                                   _("Guest agent disappeared while executing command"));
-                ret = -1;
-            }
-        } else {
-            *reply = msg.rxObject;
-            ret = qemuAgentCheckError(cmd, *reply);
-        }
-    }
-
- cleanup:
-    VIR_FREE(cmdstr);
-    VIR_FREE(msg.txBuffer);
-
-    return ret;
-}
-
-static virJSONValuePtr ATTRIBUTE_SENTINEL
-qemuAgentMakeCommand(const char *cmdname,
-                     ...)
-{
-    virJSONValuePtr obj;
-    virJSONValuePtr jargs = NULL;
-    va_list args;
-
-    va_start(args, cmdname);
-
-    if (!(obj = virJSONValueNewObject()))
-        goto error;
-
-    if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
-        goto error;
-
-    if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
-        goto error;
-
-    if (jargs &&
-        virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
-        goto error;
-
-    va_end(args);
-
-    return obj;
-
- error:
-    virJSONValueFree(obj);
-    virJSONValueFree(jargs);
-    va_end(args);
-    return NULL;
-}
-
-static virJSONValuePtr
-qemuAgentMakeStringsArray(const char **strings, unsigned int len)
-{
-    size_t i;
-    virJSONValuePtr ret = virJSONValueNewArray(), str;
-
-    if (!ret)
-        return NULL;
-
-    for (i = 0; i < len; i++) {
-        str = virJSONValueNewString(strings[i]);
-        if (!str)
-            goto error;
-
-        if (virJSONValueArrayAppend(ret, str) < 0) {
-            virJSONValueFree(str);
-            goto error;
-        }
-    }
-    return ret;
-
- error:
-    virJSONValueFree(ret);
-    return NULL;
-}
-
-void qemuAgentNotifyEvent(qemuAgentPtr mon,
-                          qemuAgentEvent event)
-{
-    virObjectLock(mon);
-
-    VIR_DEBUG("mon=%p event=%d await_event=%d", mon, event, mon->await_event);
-    if (mon->await_event == event) {
-        mon->await_event = QEMU_AGENT_EVENT_NONE;
-        /* somebody waiting for this event, wake him up. */
-        if (mon->msg && !mon->msg->finished) {
-            mon->msg->finished = 1;
-            virCondSignal(&mon->notify);
-        }
-    }
-
-    virObjectUnlock(mon);
-}
-
-VIR_ENUM_DECL(qemuAgentShutdownMode);
-
-VIR_ENUM_IMPL(qemuAgentShutdownMode,
-              QEMU_AGENT_SHUTDOWN_LAST,
-              "powerdown", "reboot", "halt");
-
-int qemuAgentShutdown(qemuAgentPtr mon,
-                      qemuAgentShutdownMode mode)
-{
-    int ret = -1;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    cmd = qemuAgentMakeCommand("guest-shutdown",
-                               "s:mode", qemuAgentShutdownModeTypeToString(mode),
-                               NULL);
-    if (!cmd)
-        return -1;
-
-    if (mode == QEMU_AGENT_SHUTDOWN_REBOOT)
-        mon->await_event = QEMU_AGENT_EVENT_RESET;
-    else
-        mon->await_event = QEMU_AGENT_EVENT_SHUTDOWN;
-    ret = qemuAgentCommand(mon, cmd, &reply, false,
-                           VIR_DOMAIN_QEMU_AGENT_COMMAND_SHUTDOWN);
-
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-/*
- * qemuAgentFSFreeze:
- * @mon: Agent
- * @mountpoints: Array of mountpoint paths to be frozen, or NULL for all
- * @nmountpoints: Number of mountpoints to be frozen, or 0 for all
- *
- * Issue guest-fsfreeze-freeze command to guest agent,
- * which freezes file systems mounted on specified mountpoints
- * (or all file systems when @mountpoints is NULL), and returns
- * number of frozen file systems on success.
- *
- * Returns: number of file system frozen on success,
- *          -1 on error.
- */
-int qemuAgentFSFreeze(qemuAgentPtr mon, const char **mountpoints,
-                      unsigned int nmountpoints)
-{
-    int ret = -1;
-    virJSONValuePtr cmd, arg = NULL;
-    virJSONValuePtr reply = NULL;
-
-    if (mountpoints && nmountpoints) {
-        arg = qemuAgentMakeStringsArray(mountpoints, nmountpoints);
-        if (!arg)
-            return -1;
-
-        cmd = qemuAgentMakeCommand("guest-fsfreeze-freeze-list",
-                                   "a:mountpoints", arg, NULL);
-    } else {
-        cmd = qemuAgentMakeCommand("guest-fsfreeze-freeze", NULL);
-    }
-
-    if (!cmd)
-        goto cleanup;
-    arg = NULL;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("malformed return value"));
-    }
-
- cleanup:
-    virJSONValueFree(arg);
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-/*
- * qemuAgentFSThaw:
- * @mon: Agent
- *
- * Issue guest-fsfreeze-thaw command to guest agent,
- * which unfreezes all mounted file systems and returns
- * number of thawed file systems on success.
- *
- * Returns: number of file system thawed on success,
- *          -1 on error.
- */
-int qemuAgentFSThaw(qemuAgentPtr mon)
-{
-    int ret = -1;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    cmd = qemuAgentMakeCommand("guest-fsfreeze-thaw", NULL);
-
-    if (!cmd)
-        return -1;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("malformed return value"));
-    }
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-VIR_ENUM_DECL(qemuAgentSuspendMode);
-
-VIR_ENUM_IMPL(qemuAgentSuspendMode,
-              VIR_NODE_SUSPEND_TARGET_LAST,
-              "guest-suspend-ram",
-              "guest-suspend-disk",
-              "guest-suspend-hybrid");
-
-int
-qemuAgentSuspend(qemuAgentPtr mon,
-                 unsigned int target)
-{
-    int ret = -1;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    cmd = qemuAgentMakeCommand(qemuAgentSuspendModeTypeToString(target),
-                               NULL);
-    if (!cmd)
-        return -1;
-
-    mon->await_event = QEMU_AGENT_EVENT_SUSPEND;
-    ret = qemuAgentCommand(mon, cmd, &reply, false,
-                           VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK);
-
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-int
-qemuAgentArbitraryCommand(qemuAgentPtr mon,
-                          const char *cmd_str,
-                          char **result,
-                          int timeout)
-{
-    int ret = -1;
-    virJSONValuePtr cmd = NULL;
-    virJSONValuePtr reply = NULL;
-
-    *result = NULL;
-    if (timeout < VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN) {
-        virReportError(VIR_ERR_INVALID_ARG,
-                       _("guest agent timeout '%d' is "
-                         "less than the minimum '%d'"),
-                       timeout, VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN);
-        goto cleanup;
-    }
-
-    if (!(cmd = virJSONValueFromString(cmd_str)))
-        goto cleanup;
-
-    if ((ret = qemuAgentCommand(mon, cmd, &reply, true, timeout)) < 0)
-        goto cleanup;
-
-    if (!(*result = virJSONValueToString(reply, false)))
-        ret = -1;
-
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-int
-qemuAgentFSTrim(qemuAgentPtr mon,
-                unsigned long long minimum)
-{
-    int ret = -1;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    cmd = qemuAgentMakeCommand("guest-fstrim",
-                               "U:minimum", minimum,
-                               NULL);
-    if (!cmd)
-        return ret;
-
-    ret = qemuAgentCommand(mon, cmd, &reply, false,
-                           VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK);
-
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-int
-qemuAgentGetVCPUs(qemuAgentPtr mon,
-                  qemuAgentCPUInfoPtr *info)
-{
-    int ret = -1;
-    size_t i;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-    virJSONValuePtr data = NULL;
-    ssize_t ndata;
-
-    if (!(cmd = qemuAgentMakeCommand("guest-get-vcpus", NULL)))
-        return -1;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (!(data = virJSONValueObjectGet(reply, "return"))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest-get-vcpus reply was missing return data"));
-        goto cleanup;
-    }
-
-    if (data->type != VIR_JSON_TYPE_ARRAY) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest-get-vcpus return information was not an array"));
-        goto cleanup;
-    }
-
-    ndata = virJSONValueArraySize(data);
-
-    if (VIR_ALLOC_N(*info, ndata) < 0)
-        goto cleanup;
-
-    for (i = 0; i < ndata; i++) {
-        virJSONValuePtr entry = virJSONValueArrayGet(data, i);
-        qemuAgentCPUInfoPtr in = *info + i;
-
-        if (!entry) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("array element missing in guest-get-vcpus return "
-                             "value"));
-            goto cleanup;
-        }
-
-        if (virJSONValueObjectGetNumberUint(entry, "logical-id", &in->id) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'logical-id' missing in reply of guest-get-vcpus"));
-            goto cleanup;
-        }
-
-        if (virJSONValueObjectGetBoolean(entry, "online", &in->online) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'online' missing in reply of guest-get-vcpus"));
-            goto cleanup;
-        }
-
-        if (virJSONValueObjectGetBoolean(entry, "can-offline",
-                                         &in->offlinable) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'can-offline' missing in reply of guest-get-vcpus"));
-            goto cleanup;
-        }
-    }
-
-    ret = ndata;
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-
-/* returns the value provided by the guest agent or -1 on internal error */
-static int
-qemuAgentSetVCPUsCommand(qemuAgentPtr mon,
-                         qemuAgentCPUInfoPtr info,
-                         size_t ninfo,
-                         int *nmodified)
-{
-    int ret = -1;
-    virJSONValuePtr cmd = NULL;
-    virJSONValuePtr reply = NULL;
-    virJSONValuePtr cpus = NULL;
-    virJSONValuePtr cpu = NULL;
-    size_t i;
-
-    *nmodified = 0;
-
-    /* create the key data array */
-    if (!(cpus = virJSONValueNewArray()))
-        goto cleanup;
-
-    for (i = 0; i < ninfo; i++) {
-        qemuAgentCPUInfoPtr in = &info[i];
-
-        /* don't set state for cpus that were not touched */
-        if (!in->modified)
-            continue;
-
-        (*nmodified)++;
-
-        /* create single cpu object */
-        if (!(cpu = virJSONValueNewObject()))
-            goto cleanup;
-
-        if (virJSONValueObjectAppendNumberInt(cpu, "logical-id", in->id) < 0)
-            goto cleanup;
-
-        if (virJSONValueObjectAppendBoolean(cpu, "online", in->online) < 0)
-            goto cleanup;
-
-        if (virJSONValueArrayAppend(cpus, cpu) < 0)
-            goto cleanup;
-
-        cpu = NULL;
-    }
-
-    if (*nmodified == 0) {
-        ret = 0;
-        goto cleanup;
-    }
-
-    if (!(cmd = qemuAgentMakeCommand("guest-set-vcpus",
-                                     "a:vcpus", cpus,
-                                     NULL)))
-        goto cleanup;
-
-    cpus = NULL;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (qemuAgentCheckError(cmd, reply) < 0)
-        goto cleanup;
-
-    /* All negative values are invalid. Return of 0 is bogus since we wouldn't
-     * call the guest agent so that 0 cpus would be set successfully. Reporting
-     * more successfully set vcpus that we've asked for is invalid. */
-    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0 ||
-        ret <= 0 || ret > *nmodified) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest agent returned malformed or invalid return value"));
-        ret = -1;
-    }
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    virJSONValueFree(cpu);
-    virJSONValueFree(cpus);
-    return ret;
-}
-
-
-/**
- * Set the VCPU state using guest agent.
- *
- * Attempts to set the guest agent state for all cpus or until a proper error is
- * reported by the guest agent. This may require multiple calls.
- *
- * Returns -1 on error, 0 on success.
- */
-int
-qemuAgentSetVCPUs(qemuAgentPtr mon,
-                  qemuAgentCPUInfoPtr info,
-                  size_t ninfo)
-{
-    int rv;
-    int nmodified;
-    size_t i;
-
-    do {
-        if ((rv = qemuAgentSetVCPUsCommand(mon, info, ninfo, &nmodified)) < 0)
-            return -1;
-
-        /* all vcpus were set successfully */
-        if (rv == nmodified)
-            return 0;
-
-        /* un-mark vcpus that were already set */
-        for (i = 0; i < ninfo && rv > 0; i++) {
-            if (!info[i].modified)
-                continue;
-
-            info[i].modified = false;
-            rv--;
-        }
-    } while (1);
-
-    return 0;
-}
-
-
-/* modify the cpu info structure to set the correct amount of cpus */
-int
-qemuAgentUpdateCPUInfo(unsigned int nvcpus,
-                       qemuAgentCPUInfoPtr cpuinfo,
-                       int ncpuinfo)
-{
-    size_t i;
-    int nonline = 0;
-    int nofflinable = 0;
-    ssize_t cpu0 = -1;
-
-    /* count the active and offlinable cpus */
-    for (i = 0; i < ncpuinfo; i++) {
-        if (cpuinfo[i].id == 0)
-            cpu0 = i;
-
-        if (cpuinfo[i].online)
-            nonline++;
-
-        if (cpuinfo[i].offlinable && cpuinfo[i].online)
-            nofflinable++;
-
-        /* This shouldn't happen, but we can't trust the guest agent */
-        if (!cpuinfo[i].online && !cpuinfo[i].offlinable) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("Invalid data provided by guest agent"));
-            return -1;
-        }
-    }
-
-    /* CPU0 was made offlinable in linux a while ago, but certain parts (suspend
-     * to ram) of the kernel still don't cope well with that. Make sure that if
-     * all remaining vCPUs are offlinable, vCPU0 will not be selected to be
-     * offlined automatically */
-    if (nofflinable == nonline && cpu0 >= 0 && cpuinfo[cpu0].online) {
-        cpuinfo[cpu0].offlinable = false;
-        nofflinable--;
-    }
-
-    /* the guest agent reported less cpus than requested */
-    if (nvcpus > ncpuinfo) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest agent reports less cpu than requested"));
-        return -1;
-    }
-
-    /* not enough offlinable CPUs to support the request */
-    if (nvcpus < nonline - nofflinable) {
-        virReportError(VIR_ERR_INVALID_ARG, "%s",
-                       _("Cannot offline enough CPUs"));
-        return -1;
-    }
-
-    for (i = 0; i < ncpuinfo; i++) {
-        if (nvcpus < nonline) {
-            /* unplug */
-            if (cpuinfo[i].offlinable && cpuinfo[i].online) {
-                cpuinfo[i].online = false;
-                cpuinfo[i].modified = true;
-                nonline--;
-            }
-        } else if (nvcpus > nonline) {
-            /* plug */
-            if (!cpuinfo[i].online) {
-                cpuinfo[i].online = true;
-                cpuinfo[i].modified = true;
-                nonline++;
-            }
-        } else {
-            /* done */
-            break;
-        }
-    }
-
-    return 0;
-}
-
-
-int
-qemuAgentGetTime(qemuAgentPtr mon,
-                 long long *seconds,
-                 unsigned int *nseconds)
-{
-    int ret = -1;
-    unsigned long long json_time;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    cmd = qemuAgentMakeCommand("guest-get-time",
-                               NULL);
-    if (!cmd)
-        return ret;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("malformed return value"));
-        goto cleanup;
-    }
-
-    /* guest agent returns time in nanoseconds,
-     * we need it in seconds here */
-    *seconds = json_time / 1000000000LL;
-    *nseconds = json_time % 1000000000LL;
-    ret = 0;
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-
-/**
- * qemuAgentSetTime:
- * @setTime: time to set
- * @sync: let guest agent to read domain's RTC (@setTime is ignored)
- */
-int
-qemuAgentSetTime(qemuAgentPtr mon,
-                long long seconds,
-                unsigned int nseconds,
-                bool rtcSync)
-{
-    int ret = -1;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-
-    if (rtcSync) {
-        cmd = qemuAgentMakeCommand("guest-set-time", NULL);
-    } else {
-        /* guest agent expect time with nanosecond granularity.
-         * Impressing. */
-        long long json_time;
-
-        /* Check if we overflow. For some reason qemu doesn't handle unsigned
-         * long long on the monitor well as it silently truncates numbers to
-         * signed long long. Therefore we must check overflow against LLONG_MAX
-         * not ULLONG_MAX. */
-        if (seconds > LLONG_MAX / 1000000000LL) {
-            virReportError(VIR_ERR_INVALID_ARG,
-                           _("Time '%lld' is too big for guest agent"),
-                           seconds);
-            return ret;
-        }
-
-        json_time = seconds * 1000000000LL;
-        json_time += nseconds;
-        cmd = qemuAgentMakeCommand("guest-set-time",
-                                   "I:time", json_time,
-                                   NULL);
-    }
-
-    if (!cmd)
-        return ret;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    ret = 0;
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-
-int
-qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
-                   virDomainDefPtr vmdef)
-{
-    size_t i, j, k;
-    int ret = -1;
-    ssize_t ndata = 0, ndisk;
-    char **alias;
-    virJSONValuePtr cmd;
-    virJSONValuePtr reply = NULL;
-    virJSONValuePtr data;
-    virDomainFSInfoPtr *info_ret = NULL;
-    virPCIDeviceAddress pci_address;
-
-    cmd = qemuAgentMakeCommand("guest-get-fsinfo", NULL);
-    if (!cmd)
-        return ret;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    if (!(data = virJSONValueObjectGet(reply, "return"))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest-get-fsinfo reply was missing return data"));
-        goto cleanup;
-    }
-
-    if (data->type != VIR_JSON_TYPE_ARRAY) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("guest-get-fsinfo return information was not "
-                         "an array"));
-        goto cleanup;
-    }
-
-    ndata = virJSONValueArraySize(data);
-    if (!ndata) {
-        ret = 0;
-        *info = NULL;
-        goto cleanup;
-    }
-    if (VIR_ALLOC_N(info_ret, ndata) < 0)
-        goto cleanup;
-
-    for (i = 0; i < ndata; i++) {
-        /* Reverse the order to arrange in mount order */
-        virJSONValuePtr entry = virJSONValueArrayGet(data, ndata - 1 - i);
-
-        if (!entry) {
-            virReportError(VIR_ERR_INTERNAL_ERROR,
-                           _("array element '%zd' of '%zd' missing in "
-                             "guest-get-fsinfo return data"),
-                           i, ndata);
-            goto cleanup;
-        }
-
-        if (VIR_ALLOC(info_ret[i]) < 0)
-            goto cleanup;
-
-        if (VIR_STRDUP(info_ret[i]->mountpoint,
-                       virJSONValueObjectGetString(entry, "mountpoint")) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'mountpoint' missing in reply of "
-                             "guest-get-fsinfo"));
-            goto cleanup;
-        }
-
-        if (VIR_STRDUP(info_ret[i]->name,
-                       virJSONValueObjectGetString(entry, "name")) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'name' missing in reply of guest-get-fsinfo"));
-            goto cleanup;
-        }
-
-        if (VIR_STRDUP(info_ret[i]->fstype,
-                       virJSONValueObjectGetString(entry, "type")) < 0) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'type' missing in reply of guest-get-fsinfo"));
-            goto cleanup;
-        }
-
-        if (!(entry = virJSONValueObjectGet(entry, "disk"))) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("'disk' missing in reply of guest-get-fsinfo"));
-            goto cleanup;
-        }
-
-        if (entry->type != VIR_JSON_TYPE_ARRAY) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("guest-get-fsinfo 'disk' data was not an array"));
-            goto cleanup;
-        }
-
-        ndisk = virJSONValueArraySize(entry);
-        if (!ndisk)
-            continue;
-        if (VIR_ALLOC_N(info_ret[i]->devAlias, ndisk) < 0)
-            goto cleanup;
-
-        alias = info_ret[i]->devAlias;
-        info_ret[i]->ndevAlias = 0;
-        for (j = 0; j < ndisk; j++) {
-            virJSONValuePtr disk = virJSONValueArrayGet(entry, j);
-            virJSONValuePtr pci;
-            int diskaddr[3], pciaddr[4];
-            const char *diskaddr_comp[] = {"bus", "target", "unit"};
-            const char *pciaddr_comp[] = {"domain", "bus", "slot", "function"};
-            virDomainDiskDefPtr diskDef;
-
-            if (!disk) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("array element '%zd' of '%zd' missing in "
-                                 "guest-get-fsinfo 'disk' data"),
-                               j, ndisk);
-                goto cleanup;
-            }
-
-            if (!(pci = virJSONValueObjectGet(disk, "pci-controller"))) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("'pci-controller' missing in guest-get-fsinfo "
-                                 "'disk' data"));
-                goto cleanup;
-            }
-
-            for (k = 0; k < 3; k++) {
-                if (virJSONValueObjectGetNumberInt(
-                        disk, diskaddr_comp[k], &diskaddr[k]) < 0) {
-                    virReportError(VIR_ERR_INTERNAL_ERROR,
-                                   _("'%s' missing in guest-get-fsinfo "
-                                     "'disk' data"), diskaddr_comp[k]);
-                    goto cleanup;
-                }
-            }
-            for (k = 0; k < 4; k++) {
-                if (virJSONValueObjectGetNumberInt(
-                        pci, pciaddr_comp[k], &pciaddr[k]) < 0) {
-                    virReportError(VIR_ERR_INTERNAL_ERROR,
-                                   _("'%s' missing in guest-get-fsinfo "
-                                     "'pci-address' data"), pciaddr_comp[k]);
-                    goto cleanup;
-                }
-            }
-
-            pci_address.domain = pciaddr[0];
-            pci_address.bus = pciaddr[1];
-            pci_address.slot = pciaddr[2];
-            pci_address.function = pciaddr[3];
-            if (!(diskDef = virDomainDiskByAddress(
-                     vmdef, &pci_address,
-                     diskaddr[0], diskaddr[1], diskaddr[2])))
-                continue;
-
-            if (VIR_STRDUP(*alias, diskDef->dst) < 0)
-                goto cleanup;
-
-            if (*alias) {
-                alias++;
-                info_ret[i]->ndevAlias++;
-            }
-        }
-    }
-
-    *info = info_ret;
-    info_ret = NULL;
-    ret = ndata;
-
- cleanup:
-    if (info_ret) {
-        for (i = 0; i < ndata; i++)
-            virDomainFSInfoFree(info_ret[i]);
-        VIR_FREE(info_ret);
-    }
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    return ret;
-}
-
-/*
- * qemuAgentGetInterfaces:
- * @mon: Agent monitor
- * @ifaces: pointer to an array of pointers pointing to interface objects
- *
- * Issue guest-network-get-interfaces to guest agent, which returns a
- * list of interfaces of a running domain along with their IP and MAC
- * addresses.
- *
- * Returns: number of interfaces on success, -1 on error.
- */
-int
-qemuAgentGetInterfaces(qemuAgentPtr mon,
-                       virDomainInterfacePtr **ifaces)
-{
-    int ret = -1;
-    size_t i, j;
-    ssize_t size = -1;
-    virJSONValuePtr cmd = NULL;
-    virJSONValuePtr reply = NULL;
-    virJSONValuePtr ret_array = NULL;
-    size_t ifaces_count = 0;
-    size_t addrs_count = 0;
-    virDomainInterfacePtr *ifaces_ret = NULL;
-    virHashTablePtr ifaces_store = NULL;
-    char **ifname = NULL;
-
-    /* Hash table to handle the interface alias */
-    if (!(ifaces_store = virHashCreate(ifaces_count, NULL))) {
-        virHashFree(ifaces_store);
-        return -1;
-    }
-
-    if (!(cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL)))
-        goto cleanup;
-
-    if (qemuAgentCommand(mon, cmd, &reply, false, VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0 ||
-        qemuAgentCheckError(cmd, reply) < 0) {
-        goto cleanup;
-    }
-
-    if (!(ret_array = virJSONValueObjectGet(reply, "return"))) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("qemu agent didn't provide 'return' field"));
-        goto cleanup;
-    }
-
-    if ((size = virJSONValueArraySize(ret_array)) < 0) {
-        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                       _("qemu agent didn't return an array of interfaces"));
-        goto cleanup;
-    }
-
-    for (i = 0; i < size; i++) {
-        virJSONValuePtr tmp_iface = virJSONValueArrayGet(ret_array, i);
-        virJSONValuePtr ip_addr_arr = NULL;
-        const char *hwaddr, *ifname_s, *name = NULL;
-        ssize_t ip_addr_arr_size;
-        virDomainInterfacePtr iface = NULL;
-
-        /* Shouldn't happen but doesn't hurt to check neither */
-        if (!tmp_iface) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("qemu agent reply missing interface entry in array"));
-            goto error;
-        }
-
-        /* interface name is required to be presented */
-        name = virJSONValueObjectGetString(tmp_iface, "name");
-        if (!name) {
-            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                           _("qemu agent didn't provide 'name' field"));
-            goto error;
-        }
-
-        /* Handle interface alias (<ifname>:<alias>) */
-        ifname = virStringSplit(name, ":", 2);
-        ifname_s = ifname[0];
-
-        iface = virHashLookup(ifaces_store, ifname_s);
-
-        /* If the hash table doesn't contain this iface, add it */
-        if (!iface) {
-            if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0)
-                goto error;
-
-            if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0)
-                goto error;
-
-            if (virHashAddEntry(ifaces_store, ifname_s,
-                                ifaces_ret[ifaces_count - 1]) < 0)
-                goto error;
-
-            iface = ifaces_ret[ifaces_count - 1];
-            iface->naddrs = 0;
-
-            if (VIR_STRDUP(iface->name, ifname_s) < 0)
-                goto error;
-
-            hwaddr = virJSONValueObjectGetString(tmp_iface, "hardware-address");
-            if (VIR_STRDUP(iface->hwaddr, hwaddr) < 0)
-                goto error;
-        }
-
-        /* Has to be freed for each interface. */
-        virStringListFree(ifname);
-
-        /* as well as IP address which - moreover -
-         * can be presented multiple times */
-        ip_addr_arr = virJSONValueObjectGet(tmp_iface, "ip-addresses");
-        if (!ip_addr_arr)
-            continue;
-
-        if ((ip_addr_arr_size = virJSONValueArraySize(ip_addr_arr)) < 0)
-            /* Mmm, empty 'ip-address'? */
-            goto error;
-
-        /* If current iface already exists, continue with the count */
-        addrs_count = iface->naddrs;
-
-        for (j = 0; j < ip_addr_arr_size; j++) {
-            const char *type, *addr;
-            virJSONValuePtr ip_addr_obj = virJSONValueArrayGet(ip_addr_arr, j);
-            virDomainIPAddressPtr ip_addr;
-
-            if (VIR_EXPAND_N(iface->addrs, addrs_count, 1)  < 0)
-                goto error;
-
-            ip_addr = &iface->addrs[addrs_count - 1];
-
-            /* Shouldn't happen but doesn't hurt to check neither */
-            if (!ip_addr_obj) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("qemu agent reply missing IP addr in array"));
-                goto error;
-            }
-
-            type = virJSONValueObjectGetString(ip_addr_obj, "ip-address-type");
-            if (!type) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("qemu agent didn't provide 'ip-address-type'"
-                                 " field for interface '%s'"), name);
-                goto error;
-            } else if (STREQ(type, "ipv4")) {
-                ip_addr->type = VIR_IP_ADDR_TYPE_IPV4;
-            } else if (STREQ(type, "ipv6")) {
-                ip_addr->type = VIR_IP_ADDR_TYPE_IPV6;
-            } else {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("unknown ip address type '%s'"),
-                               type);
-                goto error;
-            }
-
-            addr = virJSONValueObjectGetString(ip_addr_obj, "ip-address");
-            if (!addr) {
-                virReportError(VIR_ERR_INTERNAL_ERROR,
-                               _("qemu agent didn't provide 'ip-address'"
-                                 " field for interface '%s'"), name);
-                goto error;
-            }
-            if (VIR_STRDUP(ip_addr->addr, addr) < 0)
-                goto error;
-
-            if (virJSONValueObjectGetNumberUint(ip_addr_obj, "prefix",
-                                                &ip_addr->prefix) < 0) {
-                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
-                               _("malformed 'prefix' field"));
-                goto error;
-            }
-        }
-
-        iface->naddrs = addrs_count;
-    }
-
-    *ifaces = ifaces_ret;
-    ifaces_ret = NULL;
-    ret = ifaces_count;
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    virHashFree(ifaces_store);
-    return ret;
-
- error:
-    if (ifaces_ret) {
-        for (i = 0; i < ifaces_count; i++)
-            virDomainInterfaceFree(ifaces_ret[i]);
-    }
-    VIR_FREE(ifaces_ret);
-    virStringListFree(ifname);
-
-    goto cleanup;
-}
-
-
-int
-qemuAgentSetUserPassword(qemuAgentPtr mon,
-                         const char *user,
-                         const char *password,
-                         bool crypted)
-{
-    int ret = -1;
-    virJSONValuePtr cmd = NULL;
-    virJSONValuePtr reply = NULL;
-    char *password64 = NULL;
-
-    if (!(password64 = virStringEncodeBase64((unsigned char *) password,
-                                             strlen(password))))
-        goto cleanup;
-
-    if (!(cmd = qemuAgentMakeCommand("guest-set-user-password",
-                                     "b:crypted", crypted,
-                                     "s:username", user,
-                                     "s:password", password64,
-                                     NULL)))
-        goto cleanup;
-
-    if (qemuAgentCommand(mon, cmd, &reply, true,
-                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
-        goto cleanup;
-
-    ret = 0;
-
- cleanup:
-    virJSONValueFree(cmd);
-    virJSONValueFree(reply);
-    VIR_FREE(password64);
-    return ret;
-}
diff --git a/src/qemu/qemu_agent.h b/src/qemu/qemu_agent.h
deleted file mode 100644
index 6dd9c70..0000000
--- a/src/qemu/qemu_agent.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * qemu_agent.h: interaction with QEMU guest agent
- *
- * Copyright (C) 2006-2012 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
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange at redhat.com>
- */
-
-
-#ifndef __QEMU_AGENT_H__
-# define __QEMU_AGENT_H__
-
-# include "internal.h"
-# include "domain_conf.h"
-
-typedef struct _qemuAgent qemuAgent;
-typedef qemuAgent *qemuAgentPtr;
-
-typedef struct _qemuAgentCallbacks qemuAgentCallbacks;
-typedef qemuAgentCallbacks *qemuAgentCallbacksPtr;
-struct _qemuAgentCallbacks {
-    void (*destroy)(qemuAgentPtr mon,
-                    virDomainObjPtr vm);
-    void (*eofNotify)(qemuAgentPtr mon,
-                      virDomainObjPtr vm);
-    void (*errorNotify)(qemuAgentPtr mon,
-                        virDomainObjPtr vm);
-};
-
-
-qemuAgentPtr qemuAgentOpen(virDomainObjPtr vm,
-                           const virDomainChrSourceDef *config,
-                           qemuAgentCallbacksPtr cb);
-
-void qemuAgentClose(qemuAgentPtr mon);
-
-void qemuAgentNotifyClose(qemuAgentPtr mon);
-
-typedef enum {
-    QEMU_AGENT_EVENT_NONE = 0,
-    QEMU_AGENT_EVENT_SHUTDOWN,
-    QEMU_AGENT_EVENT_SUSPEND,
-    QEMU_AGENT_EVENT_RESET,
-} qemuAgentEvent;
-
-void qemuAgentNotifyEvent(qemuAgentPtr mon,
-                          qemuAgentEvent event);
-
-typedef enum {
-    QEMU_AGENT_SHUTDOWN_POWERDOWN,
-    QEMU_AGENT_SHUTDOWN_REBOOT,
-    QEMU_AGENT_SHUTDOWN_HALT,
-
-    QEMU_AGENT_SHUTDOWN_LAST,
-} qemuAgentShutdownMode;
-
-int qemuAgentShutdown(qemuAgentPtr mon,
-                      qemuAgentShutdownMode mode);
-
-int qemuAgentFSFreeze(qemuAgentPtr mon,
-                      const char **mountpoints, unsigned int nmountpoints);
-int qemuAgentFSThaw(qemuAgentPtr mon);
-int qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
-                       virDomainDefPtr vmdef);
-
-int qemuAgentSuspend(qemuAgentPtr mon,
-                     unsigned int target);
-
-int qemuAgentArbitraryCommand(qemuAgentPtr mon,
-                              const char *cmd,
-                              char **result,
-                              int timeout);
-int qemuAgentFSTrim(qemuAgentPtr mon,
-                    unsigned long long minimum);
-
-
-typedef struct _qemuAgentCPUInfo qemuAgentCPUInfo;
-typedef qemuAgentCPUInfo *qemuAgentCPUInfoPtr;
-struct _qemuAgentCPUInfo {
-    unsigned int id;    /* logical cpu ID */
-    bool online;        /* true if the CPU is activated */
-    bool offlinable;    /* true if the CPU can be offlined */
-
-    bool modified; /* set to true if the vcpu state needs to be changed */
-};
-
-int qemuAgentGetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr *info);
-int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus, size_t ncpus);
-int qemuAgentUpdateCPUInfo(unsigned int nvcpus,
-                           qemuAgentCPUInfoPtr cpuinfo,
-                           int ncpuinfo);
-
-int qemuAgentGetTime(qemuAgentPtr mon,
-                     long long *seconds,
-                     unsigned int *nseconds);
-int qemuAgentSetTime(qemuAgentPtr mon,
-                     long long seconds,
-                     unsigned int nseconds,
-                     bool sync);
-
-int qemuAgentGetInterfaces(qemuAgentPtr mon,
-                           virDomainInterfacePtr **ifaces);
-
-int qemuAgentSetUserPassword(qemuAgentPtr mon,
-                             const char *user,
-                             const char *password,
-                             bool crypted);
-#endif /* __QEMU_AGENT_H__ */
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 3973182..8d8fd95 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -31,7 +31,7 @@
 # include "domain_conf.h"
 # include "snapshot_conf.h"
 # include "qemu_monitor.h"
-# include "qemu_agent.h"
+# include "virqemuagent.h"
 # include "qemu_conf.h"
 # include "qemu_capabilities.h"
 # include "virchrdev.h"
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 37ccfdf..177ca51 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -44,7 +44,6 @@
 
 
 #include "qemu_driver.h"
-#include "qemu_agent.h"
 #include "qemu_alias.h"
 #include "qemu_conf.h"
 #include "qemu_capabilities.h"
@@ -59,6 +58,7 @@
 #include "qemu_blockjob.h"
 #include "qemu_security.h"
 
+#include "virqemuagent.h"
 #include "virerror.h"
 #include "virlog.h"
 #include "datatypes.h"
diff --git a/src/util/virqemuagent.c b/src/util/virqemuagent.c
new file mode 100644
index 0000000..caabae0
--- /dev/null
+++ b/src/util/virqemuagent.c
@@ -0,0 +1,2248 @@
+/*
+ * virqemuagent.c: interaction with QEMU guest agent
+ *
+ * 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "virqemuagent.h"
+#include "viralloc.h"
+#include "virlog.h"
+#include "virerror.h"
+#include "virjson.h"
+#include "virfile.h"
+#include "virprocess.h"
+#include "virtime.h"
+#include "virobject.h"
+#include "virstring.h"
+#include "base64.h"
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_agent");
+
+#define LINE_ENDING "\n"
+
+#define DEBUG_IO 0
+#define DEBUG_RAW_IO 0
+
+/* When you are the first to uncomment this,
+ * don't forget to uncomment the corresponding
+ * part in qemuAgentIOProcessEvent as well.
+ *
+static struct {
+    const char *type;
+    void (*handler)(qemuAgentPtr mon, virJSONValuePtr data);
+} eventHandlers[] = {
+};
+*/
+
+typedef struct _qemuAgentMessage qemuAgentMessage;
+typedef qemuAgentMessage *qemuAgentMessagePtr;
+
+struct _qemuAgentMessage {
+    char *txBuffer;
+    int txOffset;
+    int txLength;
+
+    /* Used by the JSON monitor to hold reply / error */
+    char *rxBuffer;
+    int rxLength;
+    void *rxObject;
+
+    /* True if rxBuffer / rxObject are ready, or a
+     * fatal error occurred on the monitor channel
+     */
+    bool finished;
+    /* true for sync command */
+    bool sync;
+    /* id of the issued sync comand */
+    unsigned long long id;
+    bool first;
+};
+
+
+struct _qemuAgent {
+    virObjectLockable parent;
+
+    virCond notify;
+
+    int fd;
+    int watch;
+
+    bool connectPending;
+    bool running;
+
+    virDomainObjPtr vm;
+
+    qemuAgentCallbacksPtr cb;
+
+    /* If there's a command being processed this will be
+     * non-NULL */
+    qemuAgentMessagePtr msg;
+
+    /* Buffer incoming data ready for Agent monitor
+     * code to process & find message boundaries */
+    size_t bufferOffset;
+    size_t bufferLength;
+    char *buffer;
+
+    /* If anything went wrong, this will be fed back
+     * the next monitor msg */
+    virError lastError;
+
+    /* Some guest agent commands don't return anything
+     * but fire up an event on qemu monitor instead.
+     * Take that as indication of successful completion */
+    qemuAgentEvent await_event;
+};
+
+static virClassPtr qemuAgentClass;
+static void qemuAgentDispose(void *obj);
+
+static int qemuAgentOnceInit(void)
+{
+    if (!(qemuAgentClass = virClassNew(virClassForObjectLockable(),
+                                       "qemuAgent",
+                                       sizeof(qemuAgent),
+                                       qemuAgentDispose)))
+        return -1;
+
+    return 0;
+}
+
+VIR_ONCE_GLOBAL_INIT(qemuAgent)
+
+
+#if DEBUG_RAW_IO
+# include <c-ctype.h>
+static char *
+qemuAgentEscapeNonPrintable(const char *text)
+{
+    size_t i;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    for (i = 0; text[i] != '\0'; i++) {
+        if (text[i] == '\\')
+            virBufferAddLit(&buf, "\\\\");
+        else if (c_isprint(text[i]) || text[i] == '\n' ||
+                 (text[i] == '\r' && text[i+1] == '\n'))
+            virBufferAddChar(&buf, text[i]);
+        else
+            virBufferAsprintf(&buf, "\\x%02x", text[i]);
+    }
+    return virBufferContentAndReset(&buf);
+}
+#endif
+
+
+static void qemuAgentDispose(void *obj)
+{
+    qemuAgentPtr mon = obj;
+    VIR_DEBUG("mon=%p", mon);
+    if (mon->cb && mon->cb->destroy)
+        (mon->cb->destroy)(mon, mon->vm);
+    virCondDestroy(&mon->notify);
+    VIR_FREE(mon->buffer);
+    virResetError(&mon->lastError);
+}
+
+static int
+qemuAgentOpenUnix(const char *monitor, pid_t cpid, bool *inProgress)
+{
+    struct sockaddr_un addr;
+    int monfd;
+    virTimeBackOffVar timeout;
+    int ret = -1;
+
+    *inProgress = false;
+
+    if ((monfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+        virReportSystemError(errno,
+                             "%s", _("failed to create socket"));
+        return -1;
+    }
+
+    if (virSetNonBlock(monfd) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to put monitor "
+                               "into non-blocking mode"));
+        goto error;
+    }
+
+    if (virSetCloseExec(monfd) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to set monitor "
+                               "close-on-exec flag"));
+        goto error;
+    }
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    if (virStrcpyStatic(addr.sun_path, monitor) == NULL) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Agent path %s too big for destination"), monitor);
+        goto error;
+    }
+
+    if (virTimeBackOffStart(&timeout, 1, 3*1000 /* ms */) < 0)
+        goto error;
+    while (virTimeBackOffWait(&timeout)) {
+        ret = connect(monfd, (struct sockaddr *) &addr, sizeof(addr));
+
+        if (ret == 0)
+            break;
+
+        if ((errno == ENOENT || errno == ECONNREFUSED) &&
+            virProcessKill(cpid, 0) == 0) {
+            /* ENOENT       : Socket may not have shown up yet
+             * ECONNREFUSED : Leftover socket hasn't been removed yet */
+            continue;
+        }
+
+        if ((errno == EINPROGRESS) ||
+            (errno == EAGAIN)) {
+            VIR_DEBUG("Connection attempt continuing in background");
+            *inProgress = true;
+            ret = 0;
+            break;
+        }
+
+        virReportSystemError(errno, "%s",
+                             _("failed to connect to monitor socket"));
+        goto error;
+
+    }
+
+    if (ret != 0) {
+        virReportSystemError(errno, "%s",
+                             _("monitor socket did not show up"));
+        goto error;
+    }
+
+    return monfd;
+
+ error:
+    VIR_FORCE_CLOSE(monfd);
+    return -1;
+}
+
+static int
+qemuAgentOpenPty(const char *monitor)
+{
+    int monfd;
+
+    if ((monfd = open(monitor, O_RDWR | O_NONBLOCK)) < 0) {
+        virReportSystemError(errno,
+                             _("Unable to open monitor path %s"), monitor);
+        return -1;
+    }
+
+    if (virSetCloseExec(monfd) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Unable to set monitor close-on-exec flag"));
+        goto error;
+    }
+
+    return monfd;
+
+ error:
+    VIR_FORCE_CLOSE(monfd);
+    return -1;
+}
+
+
+static int
+qemuAgentIOProcessEvent(qemuAgentPtr mon,
+                        virJSONValuePtr obj)
+{
+    const char *type;
+    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;
+    }
+
+/*
+    for (i = 0; i < ARRAY_CARDINALITY(eventHandlers); i++) {
+        if (STREQ(eventHandlers[i].type, type)) {
+            virJSONValuePtr data = virJSONValueObjectGet(obj, "data");
+            VIR_DEBUG("handle %s handler=%p data=%p", type,
+                      eventHandlers[i].handler, data);
+            (eventHandlers[i].handler)(mon, data);
+            break;
+        }
+    }
+*/
+    return 0;
+}
+
+static int
+qemuAgentIOProcessLine(qemuAgentPtr mon,
+                       const char *line,
+                       qemuAgentMessagePtr msg)
+{
+    virJSONValuePtr obj = NULL;
+    int ret = -1;
+
+    VIR_DEBUG("Line [%s]", line);
+
+    if (!(obj = virJSONValueFromString(line))) {
+        /* receiving garbage on first sync is regular situation */
+        if (msg && msg->sync && msg->first) {
+            VIR_DEBUG("Received garbage on sync");
+            msg->finished = 1;
+            return 0;
+        }
+
+        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) {
+        ret = qemuAgentIOProcessEvent(mon, obj);
+    } else if (virJSONValueObjectHasKey(obj, "error") == 1 ||
+               virJSONValueObjectHasKey(obj, "return") == 1) {
+        if (msg) {
+            if (msg->sync) {
+                unsigned long long id;
+
+                if (virJSONValueObjectGetNumberUlong(obj, "return", &id) < 0) {
+                    VIR_DEBUG("Ignoring delayed reply on sync");
+                    ret = 0;
+                    goto cleanup;
+                }
+
+                VIR_DEBUG("Guest returned ID: %llu", id);
+
+                if (msg->id != id) {
+                    VIR_DEBUG("Guest agent returned ID: %llu instead of %llu",
+                              id, msg->id);
+                    ret = 0;
+                    goto cleanup;
+                }
+            }
+            msg->rxObject = obj;
+            msg->finished = 1;
+            obj = NULL;
+        } else {
+            /* we are out of sync */
+            VIR_DEBUG("Ignoring delayed reply");
+        }
+        ret = 0;
+    } else {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Unknown JSON reply '%s'"), line);
+    }
+
+ cleanup:
+    virJSONValueFree(obj);
+    return ret;
+}
+
+static int qemuAgentIOProcessData(qemuAgentPtr mon,
+                                  char *data,
+                                  size_t len,
+                                  qemuAgentMessagePtr msg)
+{
+    int used = 0;
+    size_t i = 0;
+#if DEBUG_IO
+# if DEBUG_RAW_IO
+    char *str1 = qemuAgentEscapeNonPrintable(data);
+    VIR_ERROR("[%s]", str1);
+    VIR_FREE(str1);
+# else
+    VIR_DEBUG("Data %zu bytes [%s]", len, data);
+# endif
+#endif
+
+    while (used < len) {
+        char *nl = strstr(data + used, LINE_ENDING);
+
+        if (nl) {
+            int got = nl - (data + used);
+            for (i = 0; i < strlen(LINE_ENDING); i++)
+                data[used + got + i] = '\0';
+            if (qemuAgentIOProcessLine(mon, data + used, msg) < 0)
+                return -1;
+            used += got + strlen(LINE_ENDING);
+        } else {
+            break;
+        }
+    }
+
+    VIR_DEBUG("Total used %d bytes out of %zd available in buffer", used, len);
+    return used;
+}
+
+/* This method processes data that has been received
+ * from the monitor. Looking for async events and
+ * replies/errors.
+ */
+static int
+qemuAgentIOProcess(qemuAgentPtr mon)
+{
+    int len;
+    qemuAgentMessagePtr msg = NULL;
+
+    /* See if there's a message ready for reply; that is,
+     * one that has completed writing all its data.
+     */
+    if (mon->msg && mon->msg->txOffset == mon->msg->txLength)
+        msg = mon->msg;
+
+#if DEBUG_IO
+# if DEBUG_RAW_IO
+    char *str1 = qemuAgentEscapeNonPrintable(msg ? msg->txBuffer : "");
+    char *str2 = qemuAgentEscapeNonPrintable(mon->buffer);
+    VIR_ERROR(_("Process %zu %p %p [[[%s]]][[[%s]]]"),
+              mon->bufferOffset, mon->msg, msg, str1, str2);
+    VIR_FREE(str1);
+    VIR_FREE(str2);
+# else
+    VIR_DEBUG("Process %zu", mon->bufferOffset);
+# endif
+#endif
+
+    len = qemuAgentIOProcessData(mon,
+                                 mon->buffer, mon->bufferOffset,
+                                 msg);
+
+    if (len < 0)
+        return -1;
+
+    if (len < mon->bufferOffset) {
+        memmove(mon->buffer, mon->buffer + len, mon->bufferOffset - len);
+        mon->bufferOffset -= len;
+    } else {
+        VIR_FREE(mon->buffer);
+        mon->bufferOffset = mon->bufferLength = 0;
+    }
+#if DEBUG_IO
+    VIR_DEBUG("Process done %zu used %d", mon->bufferOffset, len);
+#endif
+    if (msg && msg->finished)
+        virCondBroadcast(&mon->notify);
+    return len;
+}
+
+
+static int
+qemuAgentIOConnect(qemuAgentPtr mon)
+{
+    int optval;
+    socklen_t optlen;
+
+    VIR_DEBUG("Checking on background connection status");
+
+    mon->connectPending = false;
+
+    optlen = sizeof(optval);
+
+    if (getsockopt(mon->fd, SOL_SOCKET, SO_ERROR,
+                   &optval, &optlen) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("Cannot check socket connection status"));
+        return -1;
+    }
+
+    if (optval != 0) {
+        virReportSystemError(optval, "%s",
+                             _("Cannot connect to agent socket"));
+        return -1;
+    }
+
+    VIR_DEBUG("Agent is now connected");
+    return 0;
+}
+
+/*
+ * Called when the monitor is able to write data
+ * Call this function while holding the monitor lock.
+ */
+static int
+qemuAgentIOWrite(qemuAgentPtr mon)
+{
+    int done;
+
+    /* If no active message, or fully transmitted, then no-op */
+    if (!mon->msg || mon->msg->txOffset == mon->msg->txLength)
+        return 0;
+
+    done = safewrite(mon->fd,
+                     mon->msg->txBuffer + mon->msg->txOffset,
+                     mon->msg->txLength - mon->msg->txOffset);
+
+    if (done < 0) {
+        if (errno == EAGAIN)
+            return 0;
+
+        virReportSystemError(errno, "%s",
+                             _("Unable to write to monitor"));
+        return -1;
+    }
+    mon->msg->txOffset += done;
+    return done;
+}
+
+/*
+ * Called when the monitor has incoming data to read
+ * Call this function while holding the monitor lock.
+ *
+ * Returns -1 on error, or number of bytes read
+ */
+static int
+qemuAgentIORead(qemuAgentPtr mon)
+{
+    size_t avail = mon->bufferLength - mon->bufferOffset;
+    int ret = 0;
+
+    if (avail < 1024) {
+        if (VIR_REALLOC_N(mon->buffer,
+                          mon->bufferLength + 1024) < 0)
+            return -1;
+        mon->bufferLength += 1024;
+        avail += 1024;
+    }
+
+    /* Read as much as we can get into our buffer,
+       until we block on EAGAIN, or hit EOF */
+    while (avail > 1) {
+        int got;
+        got = read(mon->fd,
+                   mon->buffer + mon->bufferOffset,
+                   avail - 1);
+        if (got < 0) {
+            if (errno == EAGAIN)
+                break;
+            virReportSystemError(errno, "%s",
+                                 _("Unable to read from monitor"));
+            ret = -1;
+            break;
+        }
+        if (got == 0)
+            break;
+
+        ret += got;
+        avail -= got;
+        mon->bufferOffset += got;
+        mon->buffer[mon->bufferOffset] = '\0';
+    }
+
+#if DEBUG_IO
+    VIR_DEBUG("Now read %zu bytes of data", mon->bufferOffset);
+#endif
+
+    return ret;
+}
+
+
+static void qemuAgentUpdateWatch(qemuAgentPtr mon)
+{
+    int events =
+        VIR_EVENT_HANDLE_HANGUP |
+        VIR_EVENT_HANDLE_ERROR;
+
+    if (mon->lastError.code == VIR_ERR_OK) {
+        events |= VIR_EVENT_HANDLE_READABLE;
+
+        if (mon->msg && mon->msg->txOffset < mon->msg->txLength)
+            events |= VIR_EVENT_HANDLE_WRITABLE;
+    }
+
+    virEventUpdateHandle(mon->watch, events);
+}
+
+
+static void
+qemuAgentIO(int watch, int fd, int events, void *opaque)
+{
+    qemuAgentPtr mon = opaque;
+    bool error = false;
+    bool eof = false;
+
+    virObjectRef(mon);
+    /* lock access to the monitor and protect fd */
+    virObjectLock(mon);
+#if DEBUG_IO
+    VIR_DEBUG("Agent %p I/O on watch %d fd %d events %d", mon, watch, fd, events);
+#endif
+
+    if (mon->fd != fd || mon->watch != watch) {
+        if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
+            eof = true;
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("event from unexpected fd %d!=%d / watch %d!=%d"),
+                       mon->fd, fd, mon->watch, watch);
+        error = true;
+    } else if (mon->lastError.code != VIR_ERR_OK) {
+        if (events & (VIR_EVENT_HANDLE_HANGUP | VIR_EVENT_HANDLE_ERROR))
+            eof = true;
+        error = true;
+    } else {
+        if (events & VIR_EVENT_HANDLE_WRITABLE) {
+            if (mon->connectPending) {
+                if (qemuAgentIOConnect(mon) < 0)
+                    error = true;
+            } else {
+                if (qemuAgentIOWrite(mon) < 0)
+                    error = true;
+            }
+            events &= ~VIR_EVENT_HANDLE_WRITABLE;
+        }
+
+        if (!error &&
+            events & VIR_EVENT_HANDLE_READABLE) {
+            int got = qemuAgentIORead(mon);
+            events &= ~VIR_EVENT_HANDLE_READABLE;
+            if (got < 0) {
+                error = true;
+            } else if (got == 0) {
+                eof = true;
+            } else {
+                /* Ignore hangup/error events if we read some data, to
+                 * give time for that data to be consumed */
+                events = 0;
+
+                if (qemuAgentIOProcess(mon) < 0)
+                    error = true;
+            }
+        }
+
+        if (!error &&
+            events & VIR_EVENT_HANDLE_HANGUP) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("End of file from agent monitor"));
+            eof = true;
+            events &= ~VIR_EVENT_HANDLE_HANGUP;
+        }
+
+        if (!error && !eof &&
+            events & VIR_EVENT_HANDLE_ERROR) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Invalid file descriptor while waiting for monitor"));
+            eof = true;
+            events &= ~VIR_EVENT_HANDLE_ERROR;
+        }
+        if (!error && events) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("Unhandled event %d for monitor fd %d"),
+                           events, mon->fd);
+            error = true;
+        }
+    }
+
+    if (error || eof) {
+        if (mon->lastError.code != VIR_ERR_OK) {
+            /* Already have an error, so clear any new error */
+            virResetLastError();
+        } else {
+            virErrorPtr err = virGetLastError();
+            if (!err)
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Error while processing monitor IO"));
+            virCopyLastError(&mon->lastError);
+            virResetLastError();
+        }
+
+        VIR_DEBUG("Error on monitor %s", NULLSTR(mon->lastError.message));
+        /* If IO process resulted in an error & we have a message,
+         * then wakeup that waiter */
+        if (mon->msg && !mon->msg->finished) {
+            mon->msg->finished = 1;
+            virCondSignal(&mon->notify);
+        }
+    }
+
+    qemuAgentUpdateWatch(mon);
+
+    /* We have to unlock to avoid deadlock against command thread,
+     * but is this safe ?  I think it is, because the callback
+     * will try to acquire the virDomainObjPtr mutex next */
+    if (eof) {
+        void (*eofNotify)(qemuAgentPtr, virDomainObjPtr)
+            = mon->cb->eofNotify;
+        virDomainObjPtr vm = mon->vm;
+
+        /* Make sure anyone waiting wakes up now */
+        virCondSignal(&mon->notify);
+        virObjectUnlock(mon);
+        virObjectUnref(mon);
+        VIR_DEBUG("Triggering EOF callback");
+        (eofNotify)(mon, vm);
+    } else if (error) {
+        void (*errorNotify)(qemuAgentPtr, virDomainObjPtr)
+            = mon->cb->errorNotify;
+        virDomainObjPtr vm = mon->vm;
+
+        /* Make sure anyone waiting wakes up now */
+        virCondSignal(&mon->notify);
+        virObjectUnlock(mon);
+        virObjectUnref(mon);
+        VIR_DEBUG("Triggering error callback");
+        (errorNotify)(mon, vm);
+    } else {
+        virObjectUnlock(mon);
+        virObjectUnref(mon);
+    }
+}
+
+
+qemuAgentPtr
+qemuAgentOpen(virDomainObjPtr vm,
+              const virDomainChrSourceDef *config,
+              qemuAgentCallbacksPtr cb)
+{
+    qemuAgentPtr mon;
+
+    if (!cb || !cb->eofNotify) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("EOF notify callback must be supplied"));
+        return NULL;
+    }
+
+    if (qemuAgentInitialize() < 0)
+        return NULL;
+
+    if (!(mon = virObjectLockableNew(qemuAgentClass)))
+        return NULL;
+
+    mon->fd = -1;
+    if (virCondInit(&mon->notify) < 0) {
+        virReportSystemError(errno, "%s",
+                             _("cannot initialize monitor condition"));
+        virObjectUnref(mon);
+        return NULL;
+    }
+    mon->vm = vm;
+    mon->cb = cb;
+
+    switch (config->type) {
+    case VIR_DOMAIN_CHR_TYPE_UNIX:
+        mon->fd = qemuAgentOpenUnix(config->data.nix.path, vm->pid,
+                                    &mon->connectPending);
+        break;
+
+    case VIR_DOMAIN_CHR_TYPE_PTY:
+        mon->fd = qemuAgentOpenPty(config->data.file.path);
+        break;
+
+    default:
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("unable to handle monitor type: %s"),
+                       virDomainChrTypeToString(config->type));
+        goto cleanup;
+    }
+
+    if (mon->fd == -1)
+        goto cleanup;
+
+    virObjectRef(mon);
+    if ((mon->watch = virEventAddHandle(mon->fd,
+                                        VIR_EVENT_HANDLE_HANGUP |
+                                        VIR_EVENT_HANDLE_ERROR |
+                                        VIR_EVENT_HANDLE_READABLE |
+                                        (mon->connectPending ?
+                                         VIR_EVENT_HANDLE_WRITABLE :
+                                         0),
+                                        qemuAgentIO,
+                                        mon,
+                                        virObjectFreeCallback)) < 0) {
+        virObjectUnref(mon);
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("unable to register monitor events"));
+        goto cleanup;
+    }
+
+    mon->running = true;
+    VIR_DEBUG("New mon %p fd =%d watch=%d", mon, mon->fd, mon->watch);
+
+    return mon;
+
+ cleanup:
+    /* We don't want the 'destroy' callback invoked during
+     * cleanup from construction failure, because that can
+     * give a double-unref on virDomainObjPtr in the caller,
+     * so kill the callbacks now.
+     */
+    mon->cb = NULL;
+    qemuAgentClose(mon);
+    return NULL;
+}
+
+
+static void
+qemuAgentNotifyCloseLocked(qemuAgentPtr mon)
+{
+    if (mon) {
+        mon->running = false;
+
+        /* If there is somebody waiting for a message
+         * wake him up. No message will arrive anyway. */
+        if (mon->msg && !mon->msg->finished) {
+            mon->msg->finished = 1;
+            virCondSignal(&mon->notify);
+        }
+    }
+}
+
+
+void
+qemuAgentNotifyClose(qemuAgentPtr mon)
+{
+    if (!mon)
+        return;
+
+    VIR_DEBUG("mon=%p", mon);
+
+    virObjectLock(mon);
+    qemuAgentNotifyCloseLocked(mon);
+    virObjectUnlock(mon);
+}
+
+
+void qemuAgentClose(qemuAgentPtr mon)
+{
+    if (!mon)
+        return;
+
+    VIR_DEBUG("mon=%p", mon);
+
+    virObjectLock(mon);
+
+    if (mon->fd >= 0) {
+        if (mon->watch)
+            virEventRemoveHandle(mon->watch);
+        VIR_FORCE_CLOSE(mon->fd);
+    }
+
+    qemuAgentNotifyCloseLocked(mon);
+    virObjectUnlock(mon);
+
+    virObjectUnref(mon);
+}
+
+#define QEMU_AGENT_WAIT_TIME 5
+
+/**
+ * qemuAgentSend:
+ * @mon: Monitor
+ * @msg: Message
+ * @seconds: number of seconds to wait for the result, it can be either
+ *           -2, -1, 0 or positive.
+ *
+ * Send @msg to agent @mon. If @seconds is equal to
+ * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2), this function will block forever
+ * waiting for the result. The value of
+ * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1) means use default timeout value
+ * and VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0) makes this this function return
+ * immediately without waiting. Any positive value means the number of seconds
+ * to wait for the result.
+ *
+ * Returns: 0 on success,
+ *          -2 on timeout,
+ *          -1 otherwise
+ */
+static int qemuAgentSend(qemuAgentPtr mon,
+                         qemuAgentMessagePtr msg,
+                         int seconds)
+{
+    int ret = -1;
+    unsigned long long then = 0;
+
+    /* Check whether qemu quit unexpectedly */
+    if (mon->lastError.code != VIR_ERR_OK) {
+        VIR_DEBUG("Attempt to send command while error is set %s",
+                  NULLSTR(mon->lastError.message));
+        virSetError(&mon->lastError);
+        return -1;
+    }
+
+    if (seconds > VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) {
+        unsigned long long now;
+        if (virTimeMillisNow(&now) < 0)
+            return -1;
+        if (seconds == VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT)
+            seconds = QEMU_AGENT_WAIT_TIME;
+        then = now + seconds * 1000ull;
+    }
+
+    mon->msg = msg;
+    qemuAgentUpdateWatch(mon);
+
+    while (!mon->msg->finished) {
+        if ((then && virCondWaitUntil(&mon->notify, &mon->parent.lock, then) < 0) ||
+            (!then && virCondWait(&mon->notify, &mon->parent.lock) < 0)) {
+            if (errno == ETIMEDOUT) {
+                virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+                               _("Guest agent not available for now"));
+                ret = -2;
+            } else {
+                virReportSystemError(errno, "%s",
+                                     _("Unable to wait on agent monitor "
+                                       "condition"));
+            }
+            goto cleanup;
+        }
+    }
+
+    if (mon->lastError.code != VIR_ERR_OK) {
+        VIR_DEBUG("Send command resulted in error %s",
+                  NULLSTR(mon->lastError.message));
+        virSetError(&mon->lastError);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+ cleanup:
+    mon->msg = NULL;
+    qemuAgentUpdateWatch(mon);
+
+    return ret;
+}
+
+
+/**
+ * qemuAgentGuestSync:
+ * @mon: Monitor
+ *
+ * Send guest-sync with unique ID
+ * and wait for reply. If we get one, check if
+ * received ID is equal to given.
+ *
+ * Returns: 0 on success,
+ *          -1 otherwise
+ */
+static int
+qemuAgentGuestSync(qemuAgentPtr mon)
+{
+    int ret = -1;
+    int send_ret;
+    unsigned long long id;
+    qemuAgentMessage sync_msg;
+
+    memset(&sync_msg, 0, sizeof(sync_msg));
+    /* set only on first sync */
+    sync_msg.first = true;
+
+ retry:
+    if (virTimeMillisNow(&id) < 0)
+        return -1;
+
+    if (virAsprintf(&sync_msg.txBuffer,
+                    "{\"execute\":\"guest-sync\", "
+                    "\"arguments\":{\"id\":%llu}}\n", id) < 0)
+        return -1;
+
+    sync_msg.txLength = strlen(sync_msg.txBuffer);
+    sync_msg.sync = true;
+    sync_msg.id = id;
+
+    VIR_DEBUG("Sending guest-sync command with ID: %llu", id);
+
+    send_ret = qemuAgentSend(mon, &sync_msg,
+                             VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT);
+
+    VIR_DEBUG("qemuAgentSend returned: %d", send_ret);
+
+    if (send_ret < 0)
+        goto cleanup;
+
+    if (!sync_msg.rxObject) {
+        if (sync_msg.first) {
+            VIR_FREE(sync_msg.txBuffer);
+            memset(&sync_msg, 0, sizeof(sync_msg));
+            goto retry;
+        } else {
+            if (mon->running)
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("Missing monitor reply object"));
+            else
+                virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+                               _("Guest agent disappeared while executing command"));
+            goto cleanup;
+        }
+    }
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(sync_msg.rxObject);
+    VIR_FREE(sync_msg.txBuffer);
+    return ret;
+}
+
+static const char *
+qemuAgentStringifyErrorClass(const char *klass)
+{
+    if (STREQ_NULLABLE(klass, "BufferOverrun"))
+        return "Buffer overrun";
+    else if (STREQ_NULLABLE(klass, "CommandDisabled"))
+        return "The command has been disabled for this instance";
+    else if (STREQ_NULLABLE(klass, "CommandNotFound"))
+        return "The command has not been found";
+    else if (STREQ_NULLABLE(klass, "FdNotFound"))
+        return "File descriptor not found";
+    else if (STREQ_NULLABLE(klass, "InvalidParameter"))
+        return "Invalid parameter";
+    else if (STREQ_NULLABLE(klass, "InvalidParameterType"))
+        return "Invalid parameter type";
+    else if (STREQ_NULLABLE(klass, "InvalidParameterValue"))
+        return "Invalid parameter value";
+    else if (STREQ_NULLABLE(klass, "OpenFileFailed"))
+        return "Cannot open file";
+    else if (STREQ_NULLABLE(klass, "QgaCommandFailed"))
+        return "Guest agent command failed";
+    else if (STREQ_NULLABLE(klass, "QMPBadInputObjectMember"))
+        return "Bad QMP input object member";
+    else if (STREQ_NULLABLE(klass, "QMPExtraInputObjectMember"))
+        return "Unexpected extra object member";
+    else if (STREQ_NULLABLE(klass, "UndefinedError"))
+        return "An undefined error has occurred";
+    else if (STREQ_NULLABLE(klass, "Unsupported"))
+        return "this feature or command is not currently supported";
+    else if (klass)
+        return klass;
+    else
+        return "unknown QEMU command error";
+}
+
+/* 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 *
+qemuAgentStringifyError(virJSONValuePtr error)
+{
+    const char *klass = virJSONValueObjectGetString(error, "class");
+    const char *detail = virJSONValueObjectGetString(error, "desc");
+
+    /* The QMP 'desc' field is usually sufficient for our generic
+     * error reporting needs. However, if not present, translate
+     * the class into something readable.
+     */
+    if (!detail)
+        detail = qemuAgentStringifyErrorClass(klass);
+
+    return detail;
+}
+
+static const char *
+qemuAgentCommandName(virJSONValuePtr cmd)
+{
+    const char *name = virJSONValueObjectGetString(cmd, "execute");
+    if (name)
+        return name;
+    else
+        return "<unknown>";
+}
+
+static int
+qemuAgentCheckError(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 agent 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 agent command '%s'"),
+                           qemuAgentCommandName(cmd));
+        else
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("unable to execute QEMU agent command '%s': %s"),
+                           qemuAgentCommandName(cmd),
+                           qemuAgentStringifyError(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 agent command '%s'"),
+                       qemuAgentCommandName(cmd));
+        VIR_FREE(cmdstr);
+        VIR_FREE(replystr);
+        return -1;
+    }
+    return 0;
+}
+
+static int
+qemuAgentCommand(qemuAgentPtr mon,
+                 virJSONValuePtr cmd,
+                 virJSONValuePtr *reply,
+                 bool needReply,
+                 int seconds)
+{
+    int ret = -1;
+    qemuAgentMessage msg;
+    char *cmdstr = NULL;
+    int await_event = mon->await_event;
+
+    *reply = NULL;
+
+    if (!mon->running) {
+        virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+                       _("Guest agent disappeared while executing command"));
+        return -1;
+    }
+
+    if (qemuAgentGuestSync(mon) < 0)
+        return -1;
+
+    memset(&msg, 0, sizeof(msg));
+
+    if (!(cmdstr = virJSONValueToString(cmd, false)))
+        goto cleanup;
+    if (virAsprintf(&msg.txBuffer, "%s" LINE_ENDING, cmdstr) < 0)
+        goto cleanup;
+    msg.txLength = strlen(msg.txBuffer);
+
+    VIR_DEBUG("Send command '%s' for write, seconds = %d", cmdstr, seconds);
+
+    ret = qemuAgentSend(mon, &msg, seconds);
+
+    VIR_DEBUG("Receive command reply ret=%d rxObject=%p",
+              ret, msg.rxObject);
+
+    if (ret == 0) {
+        /* If we haven't obtained any reply but we wait for an
+         * event, then don't report this as error */
+        if (!msg.rxObject) {
+            if (await_event && !needReply) {
+                VIR_DEBUG("Woken up by event %d", await_event);
+            } else {
+                if (mon->running)
+                    virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                                   _("Missing monitor reply object"));
+                else
+                    virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s",
+                                   _("Guest agent disappeared while executing command"));
+                ret = -1;
+            }
+        } else {
+            *reply = msg.rxObject;
+            ret = qemuAgentCheckError(cmd, *reply);
+        }
+    }
+
+ cleanup:
+    VIR_FREE(cmdstr);
+    VIR_FREE(msg.txBuffer);
+
+    return ret;
+}
+
+static virJSONValuePtr ATTRIBUTE_SENTINEL
+qemuAgentMakeCommand(const char *cmdname,
+                     ...)
+{
+    virJSONValuePtr obj;
+    virJSONValuePtr jargs = NULL;
+    va_list args;
+
+    va_start(args, cmdname);
+
+    if (!(obj = virJSONValueNewObject()))
+        goto error;
+
+    if (virJSONValueObjectAppendString(obj, "execute", cmdname) < 0)
+        goto error;
+
+    if (virJSONValueObjectCreateVArgs(&jargs, args) < 0)
+        goto error;
+
+    if (jargs &&
+        virJSONValueObjectAppend(obj, "arguments", jargs) < 0)
+        goto error;
+
+    va_end(args);
+
+    return obj;
+
+ error:
+    virJSONValueFree(obj);
+    virJSONValueFree(jargs);
+    va_end(args);
+    return NULL;
+}
+
+static virJSONValuePtr
+qemuAgentMakeStringsArray(const char **strings, unsigned int len)
+{
+    size_t i;
+    virJSONValuePtr ret = virJSONValueNewArray(), str;
+
+    if (!ret)
+        return NULL;
+
+    for (i = 0; i < len; i++) {
+        str = virJSONValueNewString(strings[i]);
+        if (!str)
+            goto error;
+
+        if (virJSONValueArrayAppend(ret, str) < 0) {
+            virJSONValueFree(str);
+            goto error;
+        }
+    }
+    return ret;
+
+ error:
+    virJSONValueFree(ret);
+    return NULL;
+}
+
+void qemuAgentNotifyEvent(qemuAgentPtr mon,
+                          qemuAgentEvent event)
+{
+    virObjectLock(mon);
+
+    VIR_DEBUG("mon=%p event=%d await_event=%d", mon, event, mon->await_event);
+    if (mon->await_event == event) {
+        mon->await_event = QEMU_AGENT_EVENT_NONE;
+        /* somebody waiting for this event, wake him up. */
+        if (mon->msg && !mon->msg->finished) {
+            mon->msg->finished = 1;
+            virCondSignal(&mon->notify);
+        }
+    }
+
+    virObjectUnlock(mon);
+}
+
+VIR_ENUM_DECL(qemuAgentShutdownMode);
+
+VIR_ENUM_IMPL(qemuAgentShutdownMode,
+              QEMU_AGENT_SHUTDOWN_LAST,
+              "powerdown", "reboot", "halt");
+
+int qemuAgentShutdown(qemuAgentPtr mon,
+                      qemuAgentShutdownMode mode)
+{
+    int ret = -1;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    cmd = qemuAgentMakeCommand("guest-shutdown",
+                               "s:mode", qemuAgentShutdownModeTypeToString(mode),
+                               NULL);
+    if (!cmd)
+        return -1;
+
+    if (mode == QEMU_AGENT_SHUTDOWN_REBOOT)
+        mon->await_event = QEMU_AGENT_EVENT_RESET;
+    else
+        mon->await_event = QEMU_AGENT_EVENT_SHUTDOWN;
+    ret = qemuAgentCommand(mon, cmd, &reply, false,
+                           VIR_DOMAIN_QEMU_AGENT_COMMAND_SHUTDOWN);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+/*
+ * qemuAgentFSFreeze:
+ * @mon: Agent
+ * @mountpoints: Array of mountpoint paths to be frozen, or NULL for all
+ * @nmountpoints: Number of mountpoints to be frozen, or 0 for all
+ *
+ * Issue guest-fsfreeze-freeze command to guest agent,
+ * which freezes file systems mounted on specified mountpoints
+ * (or all file systems when @mountpoints is NULL), and returns
+ * number of frozen file systems on success.
+ *
+ * Returns: number of file system frozen on success,
+ *          -1 on error.
+ */
+int qemuAgentFSFreeze(qemuAgentPtr mon, const char **mountpoints,
+                      unsigned int nmountpoints)
+{
+    int ret = -1;
+    virJSONValuePtr cmd, arg = NULL;
+    virJSONValuePtr reply = NULL;
+
+    if (mountpoints && nmountpoints) {
+        arg = qemuAgentMakeStringsArray(mountpoints, nmountpoints);
+        if (!arg)
+            return -1;
+
+        cmd = qemuAgentMakeCommand("guest-fsfreeze-freeze-list",
+                                   "a:mountpoints", arg, NULL);
+    } else {
+        cmd = qemuAgentMakeCommand("guest-fsfreeze-freeze", NULL);
+    }
+
+    if (!cmd)
+        goto cleanup;
+    arg = NULL;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("malformed return value"));
+    }
+
+ cleanup:
+    virJSONValueFree(arg);
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+/*
+ * qemuAgentFSThaw:
+ * @mon: Agent
+ *
+ * Issue guest-fsfreeze-thaw command to guest agent,
+ * which unfreezes all mounted file systems and returns
+ * number of thawed file systems on success.
+ *
+ * Returns: number of file system thawed on success,
+ *          -1 on error.
+ */
+int qemuAgentFSThaw(qemuAgentPtr mon)
+{
+    int ret = -1;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    cmd = qemuAgentMakeCommand("guest-fsfreeze-thaw", NULL);
+
+    if (!cmd)
+        return -1;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("malformed return value"));
+    }
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+VIR_ENUM_DECL(qemuAgentSuspendMode);
+
+VIR_ENUM_IMPL(qemuAgentSuspendMode,
+              VIR_NODE_SUSPEND_TARGET_LAST,
+              "guest-suspend-ram",
+              "guest-suspend-disk",
+              "guest-suspend-hybrid");
+
+int
+qemuAgentSuspend(qemuAgentPtr mon,
+                 unsigned int target)
+{
+    int ret = -1;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    cmd = qemuAgentMakeCommand(qemuAgentSuspendModeTypeToString(target),
+                               NULL);
+    if (!cmd)
+        return -1;
+
+    mon->await_event = QEMU_AGENT_EVENT_SUSPEND;
+    ret = qemuAgentCommand(mon, cmd, &reply, false,
+                           VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+int
+qemuAgentArbitraryCommand(qemuAgentPtr mon,
+                          const char *cmd_str,
+                          char **result,
+                          int timeout)
+{
+    int ret = -1;
+    virJSONValuePtr cmd = NULL;
+    virJSONValuePtr reply = NULL;
+
+    *result = NULL;
+    if (timeout < VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN) {
+        virReportError(VIR_ERR_INVALID_ARG,
+                       _("guest agent timeout '%d' is "
+                         "less than the minimum '%d'"),
+                       timeout, VIR_DOMAIN_QEMU_AGENT_COMMAND_MIN);
+        goto cleanup;
+    }
+
+    if (!(cmd = virJSONValueFromString(cmd_str)))
+        goto cleanup;
+
+    if ((ret = qemuAgentCommand(mon, cmd, &reply, true, timeout)) < 0)
+        goto cleanup;
+
+    if (!(*result = virJSONValueToString(reply, false)))
+        ret = -1;
+
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+int
+qemuAgentFSTrim(qemuAgentPtr mon,
+                unsigned long long minimum)
+{
+    int ret = -1;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    cmd = qemuAgentMakeCommand("guest-fstrim",
+                               "U:minimum", minimum,
+                               NULL);
+    if (!cmd)
+        return ret;
+
+    ret = qemuAgentCommand(mon, cmd, &reply, false,
+                           VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK);
+
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+int
+qemuAgentGetVCPUs(qemuAgentPtr mon,
+                  qemuAgentCPUInfoPtr *info)
+{
+    int ret = -1;
+    size_t i;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr data = NULL;
+    ssize_t ndata;
+
+    if (!(cmd = qemuAgentMakeCommand("guest-get-vcpus", NULL)))
+        return -1;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (!(data = virJSONValueObjectGet(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest-get-vcpus reply was missing return data"));
+        goto cleanup;
+    }
+
+    if (data->type != VIR_JSON_TYPE_ARRAY) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest-get-vcpus return information was not an array"));
+        goto cleanup;
+    }
+
+    ndata = virJSONValueArraySize(data);
+
+    if (VIR_ALLOC_N(*info, ndata) < 0)
+        goto cleanup;
+
+    for (i = 0; i < ndata; i++) {
+        virJSONValuePtr entry = virJSONValueArrayGet(data, i);
+        qemuAgentCPUInfoPtr in = *info + i;
+
+        if (!entry) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("array element missing in guest-get-vcpus return "
+                             "value"));
+            goto cleanup;
+        }
+
+        if (virJSONValueObjectGetNumberUint(entry, "logical-id", &in->id) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'logical-id' missing in reply of guest-get-vcpus"));
+            goto cleanup;
+        }
+
+        if (virJSONValueObjectGetBoolean(entry, "online", &in->online) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'online' missing in reply of guest-get-vcpus"));
+            goto cleanup;
+        }
+
+        if (virJSONValueObjectGetBoolean(entry, "can-offline",
+                                         &in->offlinable) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'can-offline' missing in reply of guest-get-vcpus"));
+            goto cleanup;
+        }
+    }
+
+    ret = ndata;
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
+/* returns the value provided by the guest agent or -1 on internal error */
+static int
+qemuAgentSetVCPUsCommand(qemuAgentPtr mon,
+                         qemuAgentCPUInfoPtr info,
+                         size_t ninfo,
+                         int *nmodified)
+{
+    int ret = -1;
+    virJSONValuePtr cmd = NULL;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr cpus = NULL;
+    virJSONValuePtr cpu = NULL;
+    size_t i;
+
+    *nmodified = 0;
+
+    /* create the key data array */
+    if (!(cpus = virJSONValueNewArray()))
+        goto cleanup;
+
+    for (i = 0; i < ninfo; i++) {
+        qemuAgentCPUInfoPtr in = &info[i];
+
+        /* don't set state for cpus that were not touched */
+        if (!in->modified)
+            continue;
+
+        (*nmodified)++;
+
+        /* create single cpu object */
+        if (!(cpu = virJSONValueNewObject()))
+            goto cleanup;
+
+        if (virJSONValueObjectAppendNumberInt(cpu, "logical-id", in->id) < 0)
+            goto cleanup;
+
+        if (virJSONValueObjectAppendBoolean(cpu, "online", in->online) < 0)
+            goto cleanup;
+
+        if (virJSONValueArrayAppend(cpus, cpu) < 0)
+            goto cleanup;
+
+        cpu = NULL;
+    }
+
+    if (*nmodified == 0) {
+        ret = 0;
+        goto cleanup;
+    }
+
+    if (!(cmd = qemuAgentMakeCommand("guest-set-vcpus",
+                                     "a:vcpus", cpus,
+                                     NULL)))
+        goto cleanup;
+
+    cpus = NULL;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (qemuAgentCheckError(cmd, reply) < 0)
+        goto cleanup;
+
+    /* All negative values are invalid. Return of 0 is bogus since we wouldn't
+     * call the guest agent so that 0 cpus would be set successfully. Reporting
+     * more successfully set vcpus that we've asked for is invalid. */
+    if (virJSONValueObjectGetNumberInt(reply, "return", &ret) < 0 ||
+        ret <= 0 || ret > *nmodified) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest agent returned malformed or invalid return value"));
+        ret = -1;
+    }
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    virJSONValueFree(cpu);
+    virJSONValueFree(cpus);
+    return ret;
+}
+
+
+/**
+ * Set the VCPU state using guest agent.
+ *
+ * Attempts to set the guest agent state for all cpus or until a proper error is
+ * reported by the guest agent. This may require multiple calls.
+ *
+ * Returns -1 on error, 0 on success.
+ */
+int
+qemuAgentSetVCPUs(qemuAgentPtr mon,
+                  qemuAgentCPUInfoPtr info,
+                  size_t ninfo)
+{
+    int rv;
+    int nmodified;
+    size_t i;
+
+    do {
+        if ((rv = qemuAgentSetVCPUsCommand(mon, info, ninfo, &nmodified)) < 0)
+            return -1;
+
+        /* all vcpus were set successfully */
+        if (rv == nmodified)
+            return 0;
+
+        /* un-mark vcpus that were already set */
+        for (i = 0; i < ninfo && rv > 0; i++) {
+            if (!info[i].modified)
+                continue;
+
+            info[i].modified = false;
+            rv--;
+        }
+    } while (1);
+
+    return 0;
+}
+
+
+/* modify the cpu info structure to set the correct amount of cpus */
+int
+qemuAgentUpdateCPUInfo(unsigned int nvcpus,
+                       qemuAgentCPUInfoPtr cpuinfo,
+                       int ncpuinfo)
+{
+    size_t i;
+    int nonline = 0;
+    int nofflinable = 0;
+    ssize_t cpu0 = -1;
+
+    /* count the active and offlinable cpus */
+    for (i = 0; i < ncpuinfo; i++) {
+        if (cpuinfo[i].id == 0)
+            cpu0 = i;
+
+        if (cpuinfo[i].online)
+            nonline++;
+
+        if (cpuinfo[i].offlinable && cpuinfo[i].online)
+            nofflinable++;
+
+        /* This shouldn't happen, but we can't trust the guest agent */
+        if (!cpuinfo[i].online && !cpuinfo[i].offlinable) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("Invalid data provided by guest agent"));
+            return -1;
+        }
+    }
+
+    /* CPU0 was made offlinable in linux a while ago, but certain parts (suspend
+     * to ram) of the kernel still don't cope well with that. Make sure that if
+     * all remaining vCPUs are offlinable, vCPU0 will not be selected to be
+     * offlined automatically */
+    if (nofflinable == nonline && cpu0 >= 0 && cpuinfo[cpu0].online) {
+        cpuinfo[cpu0].offlinable = false;
+        nofflinable--;
+    }
+
+    /* the guest agent reported less cpus than requested */
+    if (nvcpus > ncpuinfo) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest agent reports less cpu than requested"));
+        return -1;
+    }
+
+    /* not enough offlinable CPUs to support the request */
+    if (nvcpus < nonline - nofflinable) {
+        virReportError(VIR_ERR_INVALID_ARG, "%s",
+                       _("Cannot offline enough CPUs"));
+        return -1;
+    }
+
+    for (i = 0; i < ncpuinfo; i++) {
+        if (nvcpus < nonline) {
+            /* unplug */
+            if (cpuinfo[i].offlinable && cpuinfo[i].online) {
+                cpuinfo[i].online = false;
+                cpuinfo[i].modified = true;
+                nonline--;
+            }
+        } else if (nvcpus > nonline) {
+            /* plug */
+            if (!cpuinfo[i].online) {
+                cpuinfo[i].online = true;
+                cpuinfo[i].modified = true;
+                nonline++;
+            }
+        } else {
+            /* done */
+            break;
+        }
+    }
+
+    return 0;
+}
+
+
+int
+qemuAgentGetTime(qemuAgentPtr mon,
+                 long long *seconds,
+                 unsigned int *nseconds)
+{
+    int ret = -1;
+    unsigned long long json_time;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    cmd = qemuAgentMakeCommand("guest-get-time",
+                               NULL);
+    if (!cmd)
+        return ret;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (virJSONValueObjectGetNumberUlong(reply, "return", &json_time) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("malformed return value"));
+        goto cleanup;
+    }
+
+    /* guest agent returns time in nanoseconds,
+     * we need it in seconds here */
+    *seconds = json_time / 1000000000LL;
+    *nseconds = json_time % 1000000000LL;
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
+/**
+ * qemuAgentSetTime:
+ * @setTime: time to set
+ * @sync: let guest agent to read domain's RTC (@setTime is ignored)
+ */
+int
+qemuAgentSetTime(qemuAgentPtr mon,
+                long long seconds,
+                unsigned int nseconds,
+                bool rtcSync)
+{
+    int ret = -1;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+
+    if (rtcSync) {
+        cmd = qemuAgentMakeCommand("guest-set-time", NULL);
+    } else {
+        /* guest agent expect time with nanosecond granularity.
+         * Impressing. */
+        long long json_time;
+
+        /* Check if we overflow. For some reason qemu doesn't handle unsigned
+         * long long on the monitor well as it silently truncates numbers to
+         * signed long long. Therefore we must check overflow against LLONG_MAX
+         * not ULLONG_MAX. */
+        if (seconds > LLONG_MAX / 1000000000LL) {
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("Time '%lld' is too big for guest agent"),
+                           seconds);
+            return ret;
+        }
+
+        json_time = seconds * 1000000000LL;
+        json_time += nseconds;
+        cmd = qemuAgentMakeCommand("guest-set-time",
+                                   "I:time", json_time,
+                                   NULL);
+    }
+
+    if (!cmd)
+        return ret;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    ret = 0;
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+
+int
+qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
+                   virDomainDefPtr vmdef)
+{
+    size_t i, j, k;
+    int ret = -1;
+    ssize_t ndata = 0, ndisk;
+    char **alias;
+    virJSONValuePtr cmd;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr data;
+    virDomainFSInfoPtr *info_ret = NULL;
+    virPCIDeviceAddress pci_address;
+
+    cmd = qemuAgentMakeCommand("guest-get-fsinfo", NULL);
+    if (!cmd)
+        return ret;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    if (!(data = virJSONValueObjectGet(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest-get-fsinfo reply was missing return data"));
+        goto cleanup;
+    }
+
+    if (data->type != VIR_JSON_TYPE_ARRAY) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("guest-get-fsinfo return information was not "
+                         "an array"));
+        goto cleanup;
+    }
+
+    ndata = virJSONValueArraySize(data);
+    if (!ndata) {
+        ret = 0;
+        *info = NULL;
+        goto cleanup;
+    }
+    if (VIR_ALLOC_N(info_ret, ndata) < 0)
+        goto cleanup;
+
+    for (i = 0; i < ndata; i++) {
+        /* Reverse the order to arrange in mount order */
+        virJSONValuePtr entry = virJSONValueArrayGet(data, ndata - 1 - i);
+
+        if (!entry) {
+            virReportError(VIR_ERR_INTERNAL_ERROR,
+                           _("array element '%zd' of '%zd' missing in "
+                             "guest-get-fsinfo return data"),
+                           i, ndata);
+            goto cleanup;
+        }
+
+        if (VIR_ALLOC(info_ret[i]) < 0)
+            goto cleanup;
+
+        if (VIR_STRDUP(info_ret[i]->mountpoint,
+                       virJSONValueObjectGetString(entry, "mountpoint")) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'mountpoint' missing in reply of "
+                             "guest-get-fsinfo"));
+            goto cleanup;
+        }
+
+        if (VIR_STRDUP(info_ret[i]->name,
+                       virJSONValueObjectGetString(entry, "name")) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'name' missing in reply of guest-get-fsinfo"));
+            goto cleanup;
+        }
+
+        if (VIR_STRDUP(info_ret[i]->fstype,
+                       virJSONValueObjectGetString(entry, "type")) < 0) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'type' missing in reply of guest-get-fsinfo"));
+            goto cleanup;
+        }
+
+        if (!(entry = virJSONValueObjectGet(entry, "disk"))) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("'disk' missing in reply of guest-get-fsinfo"));
+            goto cleanup;
+        }
+
+        if (entry->type != VIR_JSON_TYPE_ARRAY) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("guest-get-fsinfo 'disk' data was not an array"));
+            goto cleanup;
+        }
+
+        ndisk = virJSONValueArraySize(entry);
+        if (!ndisk)
+            continue;
+        if (VIR_ALLOC_N(info_ret[i]->devAlias, ndisk) < 0)
+            goto cleanup;
+
+        alias = info_ret[i]->devAlias;
+        info_ret[i]->ndevAlias = 0;
+        for (j = 0; j < ndisk; j++) {
+            virJSONValuePtr disk = virJSONValueArrayGet(entry, j);
+            virJSONValuePtr pci;
+            int diskaddr[3], pciaddr[4];
+            const char *diskaddr_comp[] = {"bus", "target", "unit"};
+            const char *pciaddr_comp[] = {"domain", "bus", "slot", "function"};
+            virDomainDiskDefPtr diskDef;
+
+            if (!disk) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("array element '%zd' of '%zd' missing in "
+                                 "guest-get-fsinfo 'disk' data"),
+                               j, ndisk);
+                goto cleanup;
+            }
+
+            if (!(pci = virJSONValueObjectGet(disk, "pci-controller"))) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("'pci-controller' missing in guest-get-fsinfo "
+                                 "'disk' data"));
+                goto cleanup;
+            }
+
+            for (k = 0; k < 3; k++) {
+                if (virJSONValueObjectGetNumberInt(
+                        disk, diskaddr_comp[k], &diskaddr[k]) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("'%s' missing in guest-get-fsinfo "
+                                     "'disk' data"), diskaddr_comp[k]);
+                    goto cleanup;
+                }
+            }
+            for (k = 0; k < 4; k++) {
+                if (virJSONValueObjectGetNumberInt(
+                        pci, pciaddr_comp[k], &pciaddr[k]) < 0) {
+                    virReportError(VIR_ERR_INTERNAL_ERROR,
+                                   _("'%s' missing in guest-get-fsinfo "
+                                     "'pci-address' data"), pciaddr_comp[k]);
+                    goto cleanup;
+                }
+            }
+
+            pci_address.domain = pciaddr[0];
+            pci_address.bus = pciaddr[1];
+            pci_address.slot = pciaddr[2];
+            pci_address.function = pciaddr[3];
+            if (!(diskDef = virDomainDiskByAddress(
+                     vmdef, &pci_address,
+                     diskaddr[0], diskaddr[1], diskaddr[2])))
+                continue;
+
+            if (VIR_STRDUP(*alias, diskDef->dst) < 0)
+                goto cleanup;
+
+            if (*alias) {
+                alias++;
+                info_ret[i]->ndevAlias++;
+            }
+        }
+    }
+
+    *info = info_ret;
+    info_ret = NULL;
+    ret = ndata;
+
+ cleanup:
+    if (info_ret) {
+        for (i = 0; i < ndata; i++)
+            virDomainFSInfoFree(info_ret[i]);
+        VIR_FREE(info_ret);
+    }
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    return ret;
+}
+
+/*
+ * qemuAgentGetInterfaces:
+ * @mon: Agent monitor
+ * @ifaces: pointer to an array of pointers pointing to interface objects
+ *
+ * Issue guest-network-get-interfaces to guest agent, which returns a
+ * list of interfaces of a running domain along with their IP and MAC
+ * addresses.
+ *
+ * Returns: number of interfaces on success, -1 on error.
+ */
+int
+qemuAgentGetInterfaces(qemuAgentPtr mon,
+                       virDomainInterfacePtr **ifaces)
+{
+    int ret = -1;
+    size_t i, j;
+    ssize_t size = -1;
+    virJSONValuePtr cmd = NULL;
+    virJSONValuePtr reply = NULL;
+    virJSONValuePtr ret_array = NULL;
+    size_t ifaces_count = 0;
+    size_t addrs_count = 0;
+    virDomainInterfacePtr *ifaces_ret = NULL;
+    virHashTablePtr ifaces_store = NULL;
+    char **ifname = NULL;
+
+    /* Hash table to handle the interface alias */
+    if (!(ifaces_store = virHashCreate(ifaces_count, NULL))) {
+        virHashFree(ifaces_store);
+        return -1;
+    }
+
+    if (!(cmd = qemuAgentMakeCommand("guest-network-get-interfaces", NULL)))
+        goto cleanup;
+
+    if (qemuAgentCommand(mon, cmd, &reply, false, VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0 ||
+        qemuAgentCheckError(cmd, reply) < 0) {
+        goto cleanup;
+    }
+
+    if (!(ret_array = virJSONValueObjectGet(reply, "return"))) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("qemu agent didn't provide 'return' field"));
+        goto cleanup;
+    }
+
+    if ((size = virJSONValueArraySize(ret_array)) < 0) {
+        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                       _("qemu agent didn't return an array of interfaces"));
+        goto cleanup;
+    }
+
+    for (i = 0; i < size; i++) {
+        virJSONValuePtr tmp_iface = virJSONValueArrayGet(ret_array, i);
+        virJSONValuePtr ip_addr_arr = NULL;
+        const char *hwaddr, *ifname_s, *name = NULL;
+        ssize_t ip_addr_arr_size;
+        virDomainInterfacePtr iface = NULL;
+
+        /* Shouldn't happen but doesn't hurt to check neither */
+        if (!tmp_iface) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("qemu agent reply missing interface entry in array"));
+            goto error;
+        }
+
+        /* interface name is required to be presented */
+        name = virJSONValueObjectGetString(tmp_iface, "name");
+        if (!name) {
+            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                           _("qemu agent didn't provide 'name' field"));
+            goto error;
+        }
+
+        /* Handle interface alias (<ifname>:<alias>) */
+        ifname = virStringSplit(name, ":", 2);
+        ifname_s = ifname[0];
+
+        iface = virHashLookup(ifaces_store, ifname_s);
+
+        /* If the hash table doesn't contain this iface, add it */
+        if (!iface) {
+            if (VIR_EXPAND_N(ifaces_ret, ifaces_count, 1) < 0)
+                goto error;
+
+            if (VIR_ALLOC(ifaces_ret[ifaces_count - 1]) < 0)
+                goto error;
+
+            if (virHashAddEntry(ifaces_store, ifname_s,
+                                ifaces_ret[ifaces_count - 1]) < 0)
+                goto error;
+
+            iface = ifaces_ret[ifaces_count - 1];
+            iface->naddrs = 0;
+
+            if (VIR_STRDUP(iface->name, ifname_s) < 0)
+                goto error;
+
+            hwaddr = virJSONValueObjectGetString(tmp_iface, "hardware-address");
+            if (VIR_STRDUP(iface->hwaddr, hwaddr) < 0)
+                goto error;
+        }
+
+        /* Has to be freed for each interface. */
+        virStringListFree(ifname);
+
+        /* as well as IP address which - moreover -
+         * can be presented multiple times */
+        ip_addr_arr = virJSONValueObjectGet(tmp_iface, "ip-addresses");
+        if (!ip_addr_arr)
+            continue;
+
+        if ((ip_addr_arr_size = virJSONValueArraySize(ip_addr_arr)) < 0)
+            /* Mmm, empty 'ip-address'? */
+            goto error;
+
+        /* If current iface already exists, continue with the count */
+        addrs_count = iface->naddrs;
+
+        for (j = 0; j < ip_addr_arr_size; j++) {
+            const char *type, *addr;
+            virJSONValuePtr ip_addr_obj = virJSONValueArrayGet(ip_addr_arr, j);
+            virDomainIPAddressPtr ip_addr;
+
+            if (VIR_EXPAND_N(iface->addrs, addrs_count, 1)  < 0)
+                goto error;
+
+            ip_addr = &iface->addrs[addrs_count - 1];
+
+            /* Shouldn't happen but doesn't hurt to check neither */
+            if (!ip_addr_obj) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("qemu agent reply missing IP addr in array"));
+                goto error;
+            }
+
+            type = virJSONValueObjectGetString(ip_addr_obj, "ip-address-type");
+            if (!type) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("qemu agent didn't provide 'ip-address-type'"
+                                 " field for interface '%s'"), name);
+                goto error;
+            } else if (STREQ(type, "ipv4")) {
+                ip_addr->type = VIR_IP_ADDR_TYPE_IPV4;
+            } else if (STREQ(type, "ipv6")) {
+                ip_addr->type = VIR_IP_ADDR_TYPE_IPV6;
+            } else {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("unknown ip address type '%s'"),
+                               type);
+                goto error;
+            }
+
+            addr = virJSONValueObjectGetString(ip_addr_obj, "ip-address");
+            if (!addr) {
+                virReportError(VIR_ERR_INTERNAL_ERROR,
+                               _("qemu agent didn't provide 'ip-address'"
+                                 " field for interface '%s'"), name);
+                goto error;
+            }
+            if (VIR_STRDUP(ip_addr->addr, addr) < 0)
+                goto error;
+
+            if (virJSONValueObjectGetNumberUint(ip_addr_obj, "prefix",
+                                                &ip_addr->prefix) < 0) {
+                virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+                               _("malformed 'prefix' field"));
+                goto error;
+            }
+        }
+
+        iface->naddrs = addrs_count;
+    }
+
+    *ifaces = ifaces_ret;
+    ifaces_ret = NULL;
+    ret = ifaces_count;
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    virHashFree(ifaces_store);
+    return ret;
+
+ error:
+    if (ifaces_ret) {
+        for (i = 0; i < ifaces_count; i++)
+            virDomainInterfaceFree(ifaces_ret[i]);
+    }
+    VIR_FREE(ifaces_ret);
+    virStringListFree(ifname);
+
+    goto cleanup;
+}
+
+
+int
+qemuAgentSetUserPassword(qemuAgentPtr mon,
+                         const char *user,
+                         const char *password,
+                         bool crypted)
+{
+    int ret = -1;
+    virJSONValuePtr cmd = NULL;
+    virJSONValuePtr reply = NULL;
+    char *password64 = NULL;
+
+    if (!(password64 = virStringEncodeBase64((unsigned char *) password,
+                                             strlen(password))))
+        goto cleanup;
+
+    if (!(cmd = qemuAgentMakeCommand("guest-set-user-password",
+                                     "b:crypted", crypted,
+                                     "s:username", user,
+                                     "s:password", password64,
+                                     NULL)))
+        goto cleanup;
+
+    if (qemuAgentCommand(mon, cmd, &reply, true,
+                         VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK) < 0)
+        goto cleanup;
+
+    ret = 0;
+
+ cleanup:
+    virJSONValueFree(cmd);
+    virJSONValueFree(reply);
+    VIR_FREE(password64);
+    return ret;
+}
diff --git a/src/util/virqemuagent.h b/src/util/virqemuagent.h
new file mode 100644
index 0000000..2e81020
--- /dev/null
+++ b/src/util/virqemuagent.h
@@ -0,0 +1,123 @@
+/*
+ * virqemuagent.h: interaction with QEMU guest agent
+ *
+ * Copyright (C) 2006-2012 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
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange at redhat.com>
+ */
+
+
+#ifndef __QEMU_AGENT_H__
+# define __QEMU_AGENT_H__
+
+# include "internal.h"
+# include "domain_conf.h"
+
+typedef struct _qemuAgent qemuAgent;
+typedef qemuAgent *qemuAgentPtr;
+
+typedef struct _qemuAgentCallbacks qemuAgentCallbacks;
+typedef qemuAgentCallbacks *qemuAgentCallbacksPtr;
+struct _qemuAgentCallbacks {
+    void (*destroy)(qemuAgentPtr mon,
+                    virDomainObjPtr vm);
+    void (*eofNotify)(qemuAgentPtr mon,
+                      virDomainObjPtr vm);
+    void (*errorNotify)(qemuAgentPtr mon,
+                        virDomainObjPtr vm);
+};
+
+
+qemuAgentPtr qemuAgentOpen(virDomainObjPtr vm,
+                           const virDomainChrSourceDef *config,
+                           qemuAgentCallbacksPtr cb);
+
+void qemuAgentClose(qemuAgentPtr mon);
+
+void qemuAgentNotifyClose(qemuAgentPtr mon);
+
+typedef enum {
+    QEMU_AGENT_EVENT_NONE = 0,
+    QEMU_AGENT_EVENT_SHUTDOWN,
+    QEMU_AGENT_EVENT_SUSPEND,
+    QEMU_AGENT_EVENT_RESET,
+} qemuAgentEvent;
+
+void qemuAgentNotifyEvent(qemuAgentPtr mon,
+                          qemuAgentEvent event);
+
+typedef enum {
+    QEMU_AGENT_SHUTDOWN_POWERDOWN,
+    QEMU_AGENT_SHUTDOWN_REBOOT,
+    QEMU_AGENT_SHUTDOWN_HALT,
+
+    QEMU_AGENT_SHUTDOWN_LAST,
+} qemuAgentShutdownMode;
+
+int qemuAgentShutdown(qemuAgentPtr mon,
+                      qemuAgentShutdownMode mode);
+
+int qemuAgentFSFreeze(qemuAgentPtr mon,
+                      const char **mountpoints, unsigned int nmountpoints);
+int qemuAgentFSThaw(qemuAgentPtr mon);
+int qemuAgentGetFSInfo(qemuAgentPtr mon, virDomainFSInfoPtr **info,
+                       virDomainDefPtr vmdef);
+
+int qemuAgentSuspend(qemuAgentPtr mon,
+                     unsigned int target);
+
+int qemuAgentArbitraryCommand(qemuAgentPtr mon,
+                              const char *cmd,
+                              char **result,
+                              int timeout);
+int qemuAgentFSTrim(qemuAgentPtr mon,
+                    unsigned long long minimum);
+
+
+typedef struct _qemuAgentCPUInfo qemuAgentCPUInfo;
+typedef qemuAgentCPUInfo *qemuAgentCPUInfoPtr;
+struct _qemuAgentCPUInfo {
+    unsigned int id;    /* logical cpu ID */
+    bool online;        /* true if the CPU is activated */
+    bool offlinable;    /* true if the CPU can be offlined */
+
+    bool modified; /* set to true if the vcpu state needs to be changed */
+};
+
+int qemuAgentGetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr *info);
+int qemuAgentSetVCPUs(qemuAgentPtr mon, qemuAgentCPUInfoPtr cpus, size_t ncpus);
+int qemuAgentUpdateCPUInfo(unsigned int nvcpus,
+                           qemuAgentCPUInfoPtr cpuinfo,
+                           int ncpuinfo);
+
+int qemuAgentGetTime(qemuAgentPtr mon,
+                     long long *seconds,
+                     unsigned int *nseconds);
+int qemuAgentSetTime(qemuAgentPtr mon,
+                     long long seconds,
+                     unsigned int nseconds,
+                     bool sync);
+
+int qemuAgentGetInterfaces(qemuAgentPtr mon,
+                           virDomainInterfacePtr **ifaces);
+
+int qemuAgentSetUserPassword(qemuAgentPtr mon,
+                             const char *user,
+                             const char *password,
+                             bool crypted);
+#endif /* __QEMU_AGENT_H__ */
diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c
index 3be745e..a011838 100644
--- a/tests/qemuagenttest.c
+++ b/tests/qemuagenttest.c
@@ -23,7 +23,7 @@
 #include "testutilsqemu.h"
 #include "qemumonitortestutils.h"
 #include "qemu/qemu_conf.h"
-#include "qemu/qemu_agent.h"
+#include "virqemuagent.h"
 #include "virthread.h"
 #include "virerror.h"
 #include "virstring.h"
diff --git a/tests/qemumonitortestutils.c b/tests/qemumonitortestutils.c
index cfd0a38..f509011 100644
--- a/tests/qemumonitortestutils.c
+++ b/tests/qemumonitortestutils.c
@@ -30,7 +30,7 @@
 #include "virthread.h"
 #include "qemu/qemu_processpriv.h"
 #include "qemu/qemu_monitor.h"
-#include "qemu/qemu_agent.h"
+#include "virqemuagent.h"
 #include "rpc/virnetsocket.h"
 #include "viralloc.h"
 #include "virlog.h"
diff --git a/tests/qemumonitortestutils.h b/tests/qemumonitortestutils.h
index 8b19b37..b9e219d 100644
--- a/tests/qemumonitortestutils.h
+++ b/tests/qemumonitortestutils.h
@@ -23,7 +23,7 @@
 # include "domain_conf.h"
 # include "qemu/qemu_conf.h"
 # include "qemu/qemu_monitor.h"
-# include "qemu/qemu_agent.h"
+# include "virqemuagent.h"
 
 typedef struct _qemuMonitorTest qemuMonitorTest;
 typedef qemuMonitorTest *qemuMonitorTestPtr;
-- 
2.1.4




More information about the libvir-list mailing list