/*
 * libvirt-domain.c: entry points for virDomainPtr APIs
 *
 * Copyright (C) 2006-2015 Red Hat, Inc.
 *
 * 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/>.
 */

#include <config.h>
#include <sys/stat.h>

#include "datatypes.h"
#include "viralloc.h"
#include "virfile.h"
#include "virlog.h"
#include "virtypedparam.h"
#include "virutil.h"

VIR_LOG_INIT("libvirt.domain");

#define VIR_FROM_THIS VIR_FROM_DOMAIN


/**
 * virConnectListDomains:
 * @conn: pointer to the hypervisor connection
 * @ids: array to collect the list of IDs of active domains
 * @maxids: size of @ids
 *
 * Collect the list of active domains, and store their IDs in array @ids
 *
 * For inactive domains, see virConnectListDefinedDomains().  For more
 * control over the results, see virConnectListAllDomains().
 *
 * Returns the number of domains found or -1 in case of error.  Note that
 * this command is inherently racy; a domain can be started between a
 * call to virConnectNumOfDomains() and this call; you are only guaranteed
 * that all currently active domains were listed if the return is less
 * than @maxids.
 */
int
virConnectListDomains(virConnectPtr conn, int *ids, int maxids)
{
    VIR_DEBUG("conn=%p, ids=%p, maxids=%d", conn, ids, maxids);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArrayArgGoto(ids, maxids, error);
    virCheckNonNegativeArgGoto(maxids, error);

    if (conn->driver->connectListDomains) {
        int ret = conn->driver->connectListDomains(conn, ids, maxids);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectNumOfDomains:
 * @conn: pointer to the hypervisor connection
 *
 * Provides the number of active domains.
 *
 * Returns the number of domain found or -1 in case of error
 */
int
virConnectNumOfDomains(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    if (conn->driver->connectNumOfDomains) {
        int ret = conn->driver->connectNumOfDomains(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainGetConnect:
 * @dom: pointer to a domain
 *
 * Provides the connection pointer associated with a domain.  The
 * reference counter on the connection is not increased by this
 * call.
 *
 * Returns the virConnectPtr or NULL in case of failure.
 */
virConnectPtr
virDomainGetConnect(virDomainPtr dom)
{
    VIR_DOMAIN_DEBUG(dom);

    virResetLastError();

    virCheckDomainReturn(dom, NULL);

    return dom->conn;
}


/**
 * virDomainCreateXML:
 * @conn: pointer to the hypervisor connection
 * @xmlDesc: string containing an XML description of the domain
 * @flags: bitwise-OR of supported virDomainCreateFlags
 *
 * Launch a new guest domain, based on an XML description similar
 * to the one returned by virDomainGetXMLDesc()
 * This function may require privileged access to the hypervisor.
 * The domain is not persistent, so its definition will disappear when it
 * is destroyed, or if the host is restarted (see virDomainDefineXML() to
 * define persistent domains).
 *
 * If the VIR_DOMAIN_START_PAUSED flag is set, the guest domain
 * will be started, but its CPUs will remain paused. The CPUs
 * can later be manually started using virDomainResume.
 *
 * If the VIR_DOMAIN_START_AUTODESTROY flag is set, the guest
 * domain will be automatically destroyed when the virConnectPtr
 * object is finally released. This will also happen if the
 * client application crashes / loses its connection to the
 * libvirtd daemon. Any domains marked for auto destroy will
 * block attempts at migration. Hypervisors may also block save-to-file,
 * or snapshots.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure
 */
virDomainPtr
virDomainCreateXML(virConnectPtr conn, const char *xmlDesc,
                   unsigned int flags)
{
    VIR_DEBUG("conn=%p, xmlDesc=%s, flags=0x%x", conn, NULLSTR(xmlDesc), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(xmlDesc, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainCreateXML) {
        virDomainPtr ret;
        ret = conn->driver->domainCreateXML(conn, xmlDesc, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainCreateXMLWithFiles:
 * @conn: pointer to the hypervisor connection
 * @xmlDesc: string containing an XML description of the domain
 * @nfiles: number of file descriptors passed
 * @files: list of file descriptors passed
 * @flags: bitwise-OR of supported virDomainCreateFlags
 *
 * Launch a new guest domain, based on an XML description similar
 * to the one returned by virDomainGetXMLDesc()
 * This function may require privileged access to the hypervisor.
 * The domain is not persistent, so its definition will disappear when it
 * is destroyed, or if the host is restarted (see virDomainDefineXML() to
 * define persistent domains).
 *
 * @files provides an array of file descriptors which will be
 * made available to the 'init' process of the guest. The file
 * handles exposed to the guest will be renumbered to start
 * from 3 (ie immediately following stderr). This is only
 * supported for guests which use container based virtualization
 * technology.
 *
 * If the VIR_DOMAIN_START_PAUSED flag is set, the guest domain
 * will be started, but its CPUs will remain paused. The CPUs
 * can later be manually started using virDomainResume.
 *
 * If the VIR_DOMAIN_START_AUTODESTROY flag is set, the guest
 * domain will be automatically destroyed when the virConnectPtr
 * object is finally released. This will also happen if the
 * client application crashes / loses its connection to the
 * libvirtd daemon. Any domains marked for auto destroy will
 * block attempts at migration. Hypervisors may also block
 * save-to-file, or snapshots.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure
 */
virDomainPtr
virDomainCreateXMLWithFiles(virConnectPtr conn, const char *xmlDesc,
                            unsigned int nfiles,
                            int *files,
                            unsigned int flags)
{
    VIR_DEBUG("conn=%p, xmlDesc=%s, nfiles=%u, files=%p, flags=0x%x",
              conn, NULLSTR(xmlDesc), nfiles, files, flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(xmlDesc, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainCreateXMLWithFiles) {
        virDomainPtr ret;
        ret = conn->driver->domainCreateXMLWithFiles(conn, xmlDesc,
                                                     nfiles, files,
                                                     flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainCreateLinux:
 * @conn: pointer to the hypervisor connection
 * @xmlDesc: string containing an XML description of the domain
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Deprecated after 0.4.6.
 * Renamed to virDomainCreateXML() providing identical functionality.
 * This existing name will be left indefinitely for API compatibility.
 *
 * Returns a new domain object or NULL in case of failure
 */
virDomainPtr
virDomainCreateLinux(virConnectPtr conn, const char *xmlDesc,
                     unsigned int flags)
{
    return virDomainCreateXML(conn, xmlDesc, flags);
}


/**
 * virDomainLookupByID:
 * @conn: pointer to the hypervisor connection
 * @id: the domain ID number
 *
 * Try to find a domain based on the hypervisor ID number
 * Note that this won't work for inactive domains which have an ID of -1,
 * in that case a lookup based on the Name or UUID need to be done instead.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure.  If the
 * domain cannot be found, then VIR_ERR_NO_DOMAIN error is raised.
 */
virDomainPtr
virDomainLookupByID(virConnectPtr conn, int id)
{
    VIR_DEBUG("conn=%p, id=%d", conn, id);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNegativeArgGoto(id, error);

    if (conn->driver->domainLookupByID) {
        virDomainPtr ret;
        ret = conn->driver->domainLookupByID(conn, id);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainLookupByUUID:
 * @conn: pointer to the hypervisor connection
 * @uuid: the raw UUID for the domain
 *
 * Try to lookup a domain on the given hypervisor based on its UUID.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure.  If the
 * domain cannot be found, then VIR_ERR_NO_DOMAIN error is raised.
 */
virDomainPtr
virDomainLookupByUUID(virConnectPtr conn, const unsigned char *uuid)
{
    VIR_UUID_DEBUG(conn, uuid);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(uuid, error);

    if (conn->driver->domainLookupByUUID) {
        virDomainPtr ret;
        ret = conn->driver->domainLookupByUUID(conn, uuid);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainLookupByUUIDString:
 * @conn: pointer to the hypervisor connection
 * @uuidstr: the string UUID for the domain
 *
 * Try to lookup a domain on the given hypervisor based on its UUID.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure.  If the
 * domain cannot be found, then VIR_ERR_NO_DOMAIN error is raised.
 */
virDomainPtr
virDomainLookupByUUIDString(virConnectPtr conn, const char *uuidstr)
{
    unsigned char uuid[VIR_UUID_BUFLEN];
    VIR_DEBUG("conn=%p, uuidstr=%s", conn, NULLSTR(uuidstr));

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(uuidstr, error);

    if (virUUIDParse(uuidstr, uuid) < 0) {
        virReportInvalidArg(uuidstr, "%s", _("Invalid UUID"));
        goto error;
    }

    return virDomainLookupByUUID(conn, &uuid[0]);

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainLookupByName:
 * @conn: pointer to the hypervisor connection
 * @name: name for the domain
 *
 * Try to lookup a domain on the given hypervisor based on its name.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns a new domain object or NULL in case of failure.  If the
 * domain cannot be found, then VIR_ERR_NO_DOMAIN error is raised.
 */
virDomainPtr
virDomainLookupByName(virConnectPtr conn, const char *name)
{
    VIR_DEBUG("conn=%p, name=%s", conn, NULLSTR(name));

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(name, error);

    if (conn->driver->domainLookupByName) {
        virDomainPtr dom;
        dom = conn->driver->domainLookupByName(conn, name);
        if (!dom)
            goto error;
        return dom;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainDestroy:
 * @domain: a domain object
 *
 * Destroy the domain object. The running instance is shutdown if not down
 * already and all resources used by it are given back to the hypervisor. This
 * does not free the associated virDomainPtr object.
 * This function may require privileged access.
 *
 * virDomainDestroy first requests that a guest terminate
 * (e.g. SIGTERM), then waits for it to comply. After a reasonable
 * timeout, if the guest still exists, virDomainDestroy will
 * forcefully terminate the guest (e.g. SIGKILL) if necessary (which
 * may produce undesirable results, for example unflushed disk cache
 * in the guest). To avoid this possibility, it's recommended to
 * instead call virDomainDestroyFlags, sending the
 * VIR_DOMAIN_DESTROY_GRACEFUL flag.
 *
 * If the domain is transient and has any snapshot metadata (see
 * virDomainSnapshotNum()), then that metadata will automatically
 * be deleted when the domain quits.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainDestroy(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainDestroy) {
        int ret;
        ret = conn->driver->domainDestroy(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainDestroyFlags:
 * @domain: a domain object
 * @flags: bitwise-OR of virDomainDestroyFlagsValues
 *
 * Destroy the domain object. The running instance is shutdown if not down
 * already and all resources used by it are given back to the hypervisor.
 * This does not free the associated virDomainPtr object.
 * This function may require privileged access.
 *
 * Calling this function with no @flags set (equal to zero) is
 * equivalent to calling virDomainDestroy, and after a reasonable
 * timeout will forcefully terminate the guest (e.g. SIGKILL) if
 * necessary (which may produce undesirable results, for example
 * unflushed disk cache in the guest). Including
 * VIR_DOMAIN_DESTROY_GRACEFUL in the flags will prevent the forceful
 * termination of the guest, and virDomainDestroyFlags will instead
 * return an error if the guest doesn't terminate by the end of the
 * timeout; at that time, the management application can decide if
 * calling again without VIR_DOMAIN_DESTROY_GRACEFUL is appropriate.
 *
 * Another alternative which may produce cleaner results for the
 * guest's disks is to use virDomainShutdown() instead, but that
 * depends on guest support (some hypervisor/guest combinations may
 * ignore the shutdown request).
 *
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainDestroyFlags(virDomainPtr domain,
                      unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainDestroyFlags) {
        int ret;
        ret = conn->driver->domainDestroyFlags(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainFree:
 * @domain: a domain object
 *
 * Free the domain object. The running instance is kept alive.
 * The data structure is freed and should not be used thereafter.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainFree(virDomainPtr domain)
{
    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virObjectUnref(domain);
    return 0;
}


/**
 * virDomainRef:
 * @domain: the domain to hold a reference on
 *
 * Increment the reference count on the domain. For each
 * additional call to this method, there shall be a corresponding
 * call to virDomainFree to release the reference count, once
 * the caller no longer needs the reference to this object.
 *
 * This method is typically useful for applications where multiple
 * threads are using a connection, and it is required that the
 * connection remain open until all threads have finished using
 * it. ie, each new thread using a domain would increment
 * the reference count.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainRef(virDomainPtr domain)
{
    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virObjectRef(domain);
    return 0;
}


/**
 * virDomainSuspend:
 * @domain: a domain object
 *
 * Suspends an active domain, the process is frozen without further access
 * to CPU resources and I/O but the memory used by the domain at the
 * hypervisor level will stay allocated. Use virDomainResume() to reactivate
 * the domain.
 * This function may require privileged access.
 * Moreover, suspend may not be supported if domain is in some
 * special state like VIR_DOMAIN_PMSUSPENDED.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSuspend(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainSuspend) {
        int ret;
        ret = conn->driver->domainSuspend(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainResume:
 * @domain: a domain object
 *
 * Resume a suspended domain, the process is restarted from the state where
 * it was frozen by calling virDomainSuspend().
 * This function may require privileged access
 * Moreover, resume may not be supported if domain is in some
 * special state like VIR_DOMAIN_PMSUSPENDED.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainResume(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainResume) {
        int ret;
        ret = conn->driver->domainResume(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainPMSuspendForDuration:
 * @dom: a domain object
 * @target: a value from virNodeSuspendTarget
 * @duration: duration in seconds to suspend, or 0 for indefinite
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Attempt to have the guest enter the given @target power management
 * suspension level.  If @duration is non-zero, also schedule the guest to
 * resume normal operation after that many seconds, if nothing else has
 * resumed it earlier.  Some hypervisors require that @duration be 0, for
 * an indefinite suspension.
 *
 * Dependent on hypervisor used, this may require a
 * guest agent to be available, e.g. QEMU.
 *
 * Beware that at least for QEMU, the domain's process will be terminated
 * when VIR_NODE_SUSPEND_TARGET_DISK is used and a new process will be
 * launched when libvirt is asked to wake up the domain. As a result of
 * this, any runtime changes, such as device hotplug or memory settings,
 * are lost unless such changes were made with VIR_DOMAIN_AFFECT_CONFIG
 * flag.
 *
 * Returns: 0 on success,
 *          -1 on failure.
 */
int
virDomainPMSuspendForDuration(virDomainPtr dom,
                              unsigned int target,
                              unsigned long long duration,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "target=%u duration=%llu flags=0x%x",
                     target, duration, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainPMSuspendForDuration) {
        int ret;
        ret = conn->driver->domainPMSuspendForDuration(dom, target,
                                                       duration, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainPMWakeup:
 * @dom: a domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Inject a wakeup into the guest that previously used
 * virDomainPMSuspendForDuration, rather than waiting for the
 * previously requested duration (if any) to elapse.
 *
 * Returns: 0 on success,
 *          -1 on failure.
 */
int
virDomainPMWakeup(virDomainPtr dom,
                  unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainPMWakeup) {
        int ret;
        ret = conn->driver->domainPMWakeup(dom, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainSave:
 * @domain: a domain object
 * @to: path for the output file
 *
 * This method will suspend a domain and save its memory contents to
 * a file on disk. After the call, if successful, the domain is not
 * listed as running anymore (this ends the life of a transient domain).
 * Use virDomainRestore() to restore a domain after saving.
 *
 * See virDomainSaveFlags() for more control.  Also, a save file can
 * be inspected or modified slightly with virDomainSaveImageGetXMLDesc()
 * and virDomainSaveImageDefineXML().
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSave(virDomainPtr domain, const char *to)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "to=%s", to);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(to, error);

    if (conn->driver->domainSave) {
        int ret;
        char *absolute_to;

        /* We must absolutize the file path as the save is done out of process */
        if (virFileAbsPath(to, &absolute_to) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute output file path"));
            goto error;
        }

        ret = conn->driver->domainSave(domain, absolute_to);

        VIR_FREE(absolute_to);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSaveFlags:
 * @domain: a domain object
 * @to: path for the output file
 * @dxml: (optional) XML config for adjusting guest xml used on restore
 * @flags: bitwise-OR of virDomainSaveRestoreFlags
 *
 * This method will suspend a domain and save its memory contents to
 * a file on disk. After the call, if successful, the domain is not
 * listed as running anymore (this ends the life of a transient domain).
 * Use virDomainRestore() to restore a domain after saving.
 *
 * If the hypervisor supports it, @dxml can be used to alter
 * host-specific portions of the domain XML that will be used when
 * restoring an image.  For example, it is possible to alter the
 * backing filename that is associated with a disk device, in order to
 * prepare for file renaming done as part of backing up the disk
 * device while the domain is stopped.
 *
 * If @flags includes VIR_DOMAIN_SAVE_BYPASS_CACHE, then libvirt will
 * attempt to bypass the file system cache while creating the file, or
 * fail if it cannot do so for the given system; this can allow less
 * pressure on file system cache, but also risks slowing saves to NFS.
 *
 * Normally, the saved state file will remember whether the domain was
 * running or paused, and restore defaults to the same state.
 * Specifying VIR_DOMAIN_SAVE_RUNNING or VIR_DOMAIN_SAVE_PAUSED in
 * @flags will override what state gets saved into the file.  These
 * two flags are mutually exclusive.
 *
 * A save file can be inspected or modified slightly with
 * virDomainSaveImageGetXMLDesc() and virDomainSaveImageDefineXML().
 *
 * Some hypervisors may prevent this operation if there is a current
 * block job running; in that case, use virDomainBlockJobAbort()
 * to stop the block job first.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSaveFlags(virDomainPtr domain, const char *to,
                   const char *dxml, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "to=%s, dxml=%s, flags=0x%x",
                     to, NULLSTR(dxml), flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(to, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SAVE_RUNNING,
                             VIR_DOMAIN_SAVE_PAUSED,
                             error);

    if (conn->driver->domainSaveFlags) {
        int ret;
        char *absolute_to;

        /* We must absolutize the file path as the save is done out of process */
        if (virFileAbsPath(to, &absolute_to) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute output file path"));
            goto error;
        }

        ret = conn->driver->domainSaveFlags(domain, absolute_to, dxml, flags);

        VIR_FREE(absolute_to);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainRestore:
 * @conn: pointer to the hypervisor connection
 * @from: path to the input file
 *
 * This method will restore a domain saved to disk by virDomainSave().
 *
 * See virDomainRestoreFlags() for more control.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainRestore(virConnectPtr conn, const char *from)
{
    VIR_DEBUG("conn=%p, from=%s", conn, NULLSTR(from));

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(from, error);

    if (conn->driver->domainRestore) {
        int ret;
        char *absolute_from;

        /* We must absolutize the file path as the restore is done out of process */
        if (virFileAbsPath(from, &absolute_from) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute input file path"));
            goto error;
        }

        ret = conn->driver->domainRestore(conn, absolute_from);

        VIR_FREE(absolute_from);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainRestoreFlags:
 * @conn: pointer to the hypervisor connection
 * @from: path to the input file
 * @dxml: (optional) XML config for adjusting guest xml used on restore
 * @flags: bitwise-OR of virDomainSaveRestoreFlags
 *
 * This method will restore a domain saved to disk by virDomainSave().
 *
 * If the hypervisor supports it, @dxml can be used to alter
 * host-specific portions of the domain XML that will be used when
 * restoring an image.  For example, it is possible to alter the
 * backing filename that is associated with a disk device, in order to
 * prepare for file renaming done as part of backing up the disk
 * device while the domain is stopped.
 *
 * If @flags includes VIR_DOMAIN_SAVE_BYPASS_CACHE, then libvirt will
 * attempt to bypass the file system cache while restoring the file, or
 * fail if it cannot do so for the given system; this can allow less
 * pressure on file system cache, but also risks slowing restores from NFS.
 *
 * Normally, the saved state file will remember whether the domain was
 * running or paused, and restore defaults to the same state.
 * Specifying VIR_DOMAIN_SAVE_RUNNING or VIR_DOMAIN_SAVE_PAUSED in
 * @flags will override the default read from the file.  These two
 * flags are mutually exclusive.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainRestoreFlags(virConnectPtr conn, const char *from, const char *dxml,
                      unsigned int flags)
{
    VIR_DEBUG("conn=%p, from=%s, dxml=%s, flags=0x%x",
              conn, NULLSTR(from), NULLSTR(dxml), flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(from, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SAVE_RUNNING,
                             VIR_DOMAIN_SAVE_PAUSED,
                             error);

    if (conn->driver->domainRestoreFlags) {
        int ret;
        char *absolute_from;

        /* We must absolutize the file path as the restore is done out of process */
        if (virFileAbsPath(from, &absolute_from) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute input file path"));
            goto error;
        }

        ret = conn->driver->domainRestoreFlags(conn, absolute_from, dxml,
                                               flags);

        VIR_FREE(absolute_from);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainSaveImageGetXMLDesc:
 * @conn: pointer to the hypervisor connection
 * @file: path to saved state file
 * @flags: bitwise-OR of supported virDomainSaveImageXMLFlags
 *
 * This method will extract the XML describing the domain at the time
 * a saved state file was created.  @file must be a file created
 * previously by virDomainSave() or virDomainSaveFlags().
 *
 * No security-sensitive data will be included unless @flags contains
 * VIR_DOMAIN_SAVE_IMAGE_XML_SECURE.
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of
 * error.  The caller must free() the returned value.
 */
char *
virDomainSaveImageGetXMLDesc(virConnectPtr conn, const char *file,
                             unsigned int flags)
{
    VIR_DEBUG("conn=%p, file=%s, flags=0x%x",
              conn, NULLSTR(file), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckNonNullArgGoto(file, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainSaveImageGetXMLDesc) {
        char *ret;
        char *absolute_file;

        /* We must absolutize the file path as the read is done out of process */
        if (virFileAbsPath(file, &absolute_file) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute input file path"));
            goto error;
        }

        ret = conn->driver->domainSaveImageGetXMLDesc(conn, absolute_file,
                                                      flags);

        VIR_FREE(absolute_file);

        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainSaveImageDefineXML:
 * @conn: pointer to the hypervisor connection
 * @file: path to saved state file
 * @dxml: XML config for adjusting guest xml used on restore
 * @flags: bitwise-OR of virDomainSaveRestoreFlags
 *
 * This updates the definition of a domain stored in a saved state
 * file.  @file must be a file created previously by virDomainSave()
 * or virDomainSaveFlags().
 *
 * @dxml can be used to alter host-specific portions of the domain XML
 * that will be used when restoring an image.  For example, it is
 * possible to alter the backing filename that is associated with a
 * disk device, to match renaming done as part of backing up the disk
 * device while the domain is stopped.
 *
 * Normally, the saved state file will remember whether the domain was
 * running or paused, and restore defaults to the same state.
 * Specifying VIR_DOMAIN_SAVE_RUNNING or VIR_DOMAIN_SAVE_PAUSED in
 * @flags will override the default saved into the file; omitting both
 * leaves the file's default unchanged.  These two flags are mutually
 * exclusive.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSaveImageDefineXML(virConnectPtr conn, const char *file,
                            const char *dxml, unsigned int flags)
{
    VIR_DEBUG("conn=%p, file=%s, dxml=%s, flags=0x%x",
              conn, NULLSTR(file), NULLSTR(dxml), flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(file, error);
    virCheckNonNullArgGoto(dxml, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SAVE_RUNNING,
                             VIR_DOMAIN_SAVE_PAUSED,
                             error);

    if (conn->driver->domainSaveImageDefineXML) {
        int ret;
        char *absolute_file;

        /* We must absolutize the file path as the read is done out of process */
        if (virFileAbsPath(file, &absolute_file) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute input file path"));
            goto error;
        }

        ret = conn->driver->domainSaveImageDefineXML(conn, absolute_file,
                                                     dxml, flags);

        VIR_FREE(absolute_file);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainCoreDump:
 * @domain: a domain object
 * @to: path for the core file
 * @flags: bitwise-OR of virDomainCoreDumpFlags
 *
 * This method will dump the core of a domain on a given file for analysis.
 * Note that for remote Xen Daemon the file path will be interpreted in
 * the remote host. Hypervisors may require  the user to manually ensure
 * proper permissions on the file named by @to.
 *
 * If @flags includes VIR_DUMP_CRASH, then leave the guest shut off with
 * a crashed state after the dump completes.  If @flags includes
 * VIR_DUMP_LIVE, then make the core dump while continuing to allow
 * the guest to run; otherwise, the guest is suspended during the dump.
 * VIR_DUMP_RESET flag forces reset of the guest after dump.
 * The above three flags are mutually exclusive.
 *
 * Additionally, if @flags includes VIR_DUMP_BYPASS_CACHE, then libvirt
 * will attempt to bypass the file system cache while creating the file,
 * or fail if it cannot do so for the given system; this can allow less
 * pressure on file system cache, but also risks slowing saves to NFS.
 *
 * For more control over the output format, see virDomainCoreDumpWithFormat().
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainCoreDump(virDomainPtr domain, const char *to, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "to=%s, flags=0x%x", to, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(to, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_CRASH, VIR_DUMP_LIVE, error);
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_CRASH, VIR_DUMP_RESET, error);
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_LIVE, VIR_DUMP_RESET, error);

    if (conn->driver->domainCoreDump) {
        int ret;
        char *absolute_to;

        /* We must absolutize the file path as the save is done out of process */
        if (virFileAbsPath(to, &absolute_to) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute core file path"));
            goto error;
        }

        ret = conn->driver->domainCoreDump(domain, absolute_to, flags);

        VIR_FREE(absolute_to);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}

/**
 * virDomainCoreDumpWithFormat:
 * @domain: a domain object
 * @to: path for the core file
 * @dumpformat: format of domain memory's dump (one of virDomainCoreDumpFormat enum)
 * @flags: bitwise-OR of virDomainCoreDumpFlags
 *
 * This method will dump the core of a domain on a given file for analysis.
 * Note that for remote Xen Daemon the file path will be interpreted in
 * the remote host. Hypervisors may require  the user to manually ensure
 * proper permissions on the file named by @to.
 *
 * @dumpformat controls which format the dump will have; use of
 * VIR_DOMAIN_CORE_DUMP_FORMAT_RAW mirrors what virDomainCoreDump() will
 * perform.  Not all hypervisors are able to support all formats.
 *
 * If @flags includes VIR_DUMP_CRASH, then leave the guest shut off with
 * a crashed state after the dump completes.  If @flags includes
 * VIR_DUMP_LIVE, then make the core dump while continuing to allow
 * the guest to run; otherwise, the guest is suspended during the dump.
 * VIR_DUMP_RESET flag forces reset of the guest after dump.
 * The above three flags are mutually exclusive.
 *
 * Additionally, if @flags includes VIR_DUMP_BYPASS_CACHE, then libvirt
 * will attempt to bypass the file system cache while creating the file,
 * or fail if it cannot do so for the given system; this can allow less
 * pressure on file system cache, but also risks slowing saves to NFS.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainCoreDumpWithFormat(virDomainPtr domain, const char *to,
                            unsigned int dumpformat, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "to=%s, dumpformat=%u, flags=0x%x",
                     to, dumpformat, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(to, error);

    if (dumpformat >= VIR_DOMAIN_CORE_DUMP_FORMAT_LAST) {
        virReportInvalidArg(flags, _("dumpformat '%d' is not supported"),
                            dumpformat);
        goto error;
    }

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_CRASH, VIR_DUMP_LIVE, error);
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_CRASH, VIR_DUMP_RESET, error);
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DUMP_LIVE, VIR_DUMP_RESET, error);

    if (conn->driver->domainCoreDumpWithFormat) {
        int ret;
        char *absolute_to;

        /* We must absolutize the file path as the save is done out of process */
        if (virFileAbsPath(to, &absolute_to) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("could not build absolute core file path"));
            goto error;
        }

        ret = conn->driver->domainCoreDumpWithFormat(domain, absolute_to,
                                                     dumpformat, flags);

        VIR_FREE(absolute_to);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainScreenshot:
 * @domain: a domain object
 * @stream: stream to use as output
 * @screen: monitor ID to take screenshot from
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Take a screenshot of current domain console as a stream. The image format
 * is hypervisor specific. Moreover, some hypervisors supports multiple
 * displays per domain. These can be distinguished by @screen argument.
 *
 * This call sets up a stream; subsequent use of stream API is necessary
 * to transfer actual data, determine how much data is successfully
 * transferred, and detect any errors.
 *
 * The screen ID is the sequential number of screen. In case of multiple
 * graphics cards, heads are enumerated before devices, e.g. having
 * two graphics cards, both with four heads, screen ID 5 addresses
 * the second head on the second card.
 *
 * Returns a string representing the mime-type of the image format, or
 * NULL upon error. The caller must free() the returned value.
 */
char *
virDomainScreenshot(virDomainPtr domain,
                    virStreamPtr stream,
                    unsigned int screen,
                    unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "stream=%p, flags=0x%x", stream, flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    virCheckStreamGoto(stream, error);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    if (domain->conn != stream->conn) {
        virReportInvalidArg(stream,
                            _("stream must match connection of domain '%s'"),
                            domain->name);
        goto error;
    }

    if (domain->conn->driver->domainScreenshot) {
        char *ret;
        ret = domain->conn->driver->domainScreenshot(domain, stream,
                                                     screen, flags);

        if (ret == NULL)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainShutdown:
 * @domain: a domain object
 *
 * Shutdown a domain, the domain object is still usable thereafter, but
 * the domain OS is being stopped. Note that the guest OS may ignore the
 * request. Additionally, the hypervisor may check and support the domain
 * 'on_poweroff' XML setting resulting in a domain that reboots instead of
 * shutting down. For guests that react to a shutdown request, the differences
 * from virDomainDestroy() are that the guests disk storage will be in a
 * stable state rather than having the (virtual) power cord pulled, and
 * this command returns as soon as the shutdown request is issued rather
 * than blocking until the guest is no longer running.
 *
 * If the domain is transient and has any snapshot metadata (see
 * virDomainSnapshotNum()), then that metadata will automatically
 * be deleted when the domain quits.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainShutdown(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainShutdown) {
        int ret;
        ret = conn->driver->domainShutdown(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainShutdownFlags:
 * @domain: a domain object
 * @flags: bitwise-OR of virDomainShutdownFlagValues
 *
 * Shutdown a domain, the domain object is still usable thereafter but
 * the domain OS is being stopped. Note that the guest OS may ignore the
 * request. Additionally, the hypervisor may check and support the domain
 * 'on_poweroff' XML setting resulting in a domain that reboots instead of
 * shutting down. For guests that react to a shutdown request, the differences
 * from virDomainDestroy() are that the guest's disk storage will be in a
 * stable state rather than having the (virtual) power cord pulled, and
 * this command returns as soon as the shutdown request is issued rather
 * than blocking until the guest is no longer running.
 *
 * If the domain is transient and has any snapshot metadata (see
 * virDomainSnapshotNum()), then that metadata will automatically
 * be deleted when the domain quits.
 *
 * If @flags is set to zero, then the hypervisor will choose the
 * method of shutdown it considers best. To have greater control
 * pass one or more of the virDomainShutdownFlagValues. The order
 * in which the hypervisor tries each shutdown method is undefined,
 * and a hypervisor is not required to support all methods.
 *
 * To use guest agent (VIR_DOMAIN_SHUTDOWN_GUEST_AGENT) the domain XML
 * must have <channel> configured.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainShutdownFlags(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainShutdownFlags) {
        int ret;
        ret = conn->driver->domainShutdownFlags(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainReboot:
 * @domain: a domain object
 * @flags: bitwise-OR of virDomainRebootFlagValues
 *
 * Reboot a domain, the domain object is still usable thereafter, but
 * the domain OS is being stopped for a restart.
 * Note that the guest OS may ignore the request.
 * Additionally, the hypervisor may check and support the domain
 * 'on_reboot' XML setting resulting in a domain that shuts down instead
 * of rebooting.
 *
 * If @flags is set to zero, then the hypervisor will choose the
 * method of shutdown it considers best. To have greater control
 * pass one or more of the virDomainRebootFlagValues. The order
 * in which the hypervisor tries each shutdown method is undefined,
 * and a hypervisor is not required to support all methods.
 *
 * To use guest agent (VIR_DOMAIN_REBOOT_GUEST_AGENT) the domain XML
 * must have <channel> configured.
 *
 * Due to implementation limitations in some drivers (the qemu driver,
 * for instance) it is not advised to migrate or save a guest that is
 * rebooting as a result of this API. Migrating such a guest can lead
 * to a plain shutdown on the destination.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainReboot(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainReboot) {
        int ret;
        ret = conn->driver->domainReboot(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainReset:
 * @domain: a domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Reset a domain immediately without any guest OS shutdown.
 * Reset emulates the power reset button on a machine, where all
 * hardware sees the RST line set and reinitializes internal state.
 *
 * Note that there is a risk of data loss caused by reset without any
 * guest OS shutdown.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainReset(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainReset) {
        int ret;
        ret = conn->driver->domainReset(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetName:
 * @domain: a domain object
 *
 * Get the public name for that domain
 *
 * Returns a pointer to the name or NULL, the string need not be deallocated
 * its lifetime will be the same as the domain object.
 */
const char *
virDomainGetName(virDomainPtr domain)
{
    VIR_DEBUG("domain=%p", domain);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);

    return domain->name;
}


/**
 * virDomainGetUUID:
 * @domain: a domain object
 * @uuid: pointer to a VIR_UUID_BUFLEN bytes array
 *
 * Get the UUID for a domain
 *
 * Returns -1 in case of error, 0 in case of success
 */
int
virDomainGetUUID(virDomainPtr domain, unsigned char *uuid)
{
    VIR_DOMAIN_DEBUG(domain, "uuid=%p", uuid);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(uuid, error);

    memcpy(uuid, &domain->uuid[0], VIR_UUID_BUFLEN);

    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetUUIDString:
 * @domain: a domain object
 * @buf: pointer to a VIR_UUID_STRING_BUFLEN bytes array
 *
 * Get the UUID for a domain as string. For more information about
 * UUID see RFC4122.
 *
 * Returns -1 in case of error, 0 in case of success
 */
int
virDomainGetUUIDString(virDomainPtr domain, char *buf)
{
    VIR_DOMAIN_DEBUG(domain, "buf=%p", buf);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(buf, error);

    virUUIDFormat(domain->uuid, buf);
    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetID:
 * @domain: a domain object
 *
 * Get the hypervisor ID number for the domain
 *
 * Returns the domain ID number or (unsigned int) -1 in case of error
 */
unsigned int
virDomainGetID(virDomainPtr domain)
{
    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, (unsigned int)-1);

    return domain->id;
}


/**
 * virDomainGetOSType:
 * @domain: a domain object
 *
 * Get the type of domain operation system.
 *
 * Returns the new string or NULL in case of error, the string must be
 *         freed by the caller.
 */
char *
virDomainGetOSType(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    if (conn->driver->domainGetOSType) {
        char *ret;
        ret = conn->driver->domainGetOSType(domain);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainGetMaxMemory:
 * @domain: a domain object or NULL
 *
 * Retrieve the maximum amount of physical memory allocated to a
 * domain. If domain is NULL, then this get the amount of memory reserved
 * to Domain0 i.e. the domain where the application runs.
 *
 * Returns the memory size in kibibytes (blocks of 1024 bytes), or 0 in
 * case of error.
 */
unsigned long
virDomainGetMaxMemory(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, 0);
    conn = domain->conn;

    if (conn->driver->domainGetMaxMemory) {
        unsigned long long ret;
        ret = conn->driver->domainGetMaxMemory(domain);
        if (ret == 0)
            goto error;
        if ((unsigned long) ret != ret) {
            virReportError(VIR_ERR_OVERFLOW, _("result too large: %llu"),
                           ret);
            goto error;
        }
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return 0;
}


/**
 * virDomainSetMaxMemory:
 * @domain: a domain object or NULL
 * @memory: the memory size in kibibytes (blocks of 1024 bytes)
 *
 * Dynamically change the maximum amount of physical memory allocated to a
 * domain. If domain is NULL, then this change the amount of memory reserved
 * to Domain0 i.e. the domain where the application runs.
 * This function may require privileged access to the hypervisor.
 *
 * This command is hypervisor-specific for whether active, persistent,
 * or both configurations are changed; for more control, use
 * virDomainSetMemoryFlags().
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSetMaxMemory(virDomainPtr domain, unsigned long memory)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "memory=%lu", memory);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonZeroArgGoto(memory, error);

    if (virMemoryMaxValue(true) / 1024 <= memory) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %lu"),
                       memory);
        goto error;
    }

    if (conn->driver->domainSetMaxMemory) {
        int ret;
        ret = conn->driver->domainSetMaxMemory(domain, memory);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetMemory:
 * @domain: a domain object or NULL
 * @memory: the memory size in kibibytes (blocks of 1024 bytes)
 *
 * Dynamically change the target amount of physical memory allocated to a
 * domain. If domain is NULL, then this change the amount of memory reserved
 * to Domain0 i.e. the domain where the application runs.
 * This function may require privileged access to the hypervisor.
 *
 * This command is hypervisor-specific for whether active, persistent,
 * or both configurations are changed; for more control, use
 * virDomainSetMemoryFlags().
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainSetMemory(virDomainPtr domain, unsigned long memory)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "memory=%lu", memory);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonZeroArgGoto(memory, error);

    if (virMemoryMaxValue(true) / 1024 <= memory) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %lu"),
                       memory);
        goto error;
    }

    if (conn->driver->domainSetMemory) {
        int ret;
        ret = conn->driver->domainSetMemory(domain, memory);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetMemoryFlags:
 * @domain: a domain object or NULL
 * @memory: the memory size in kibibytes (blocks of 1024 bytes)
 * @flags: bitwise-OR of virDomainMemoryModFlags
 *
 * Dynamically change the target amount of physical memory allocated to a
 * domain. If domain is NULL, then this change the amount of memory reserved
 * to Domain0 i.e. the domain where the application runs.
 * This function may require privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set. If VIR_DOMAIN_AFFECT_LIVE is set, the change affects
 * a running domain and will fail if domain is not active.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified
 * (that is, @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain
 * modifies persistent setup, while an active domain is hypervisor-dependent
 * on whether just live or both live and persistent state is changed.
 * If VIR_DOMAIN_MEM_MAXIMUM is set, the change affects domain's maximum memory
 * size rather than current memory size.
 * Not all hypervisors can support all flag combinations.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSetMemoryFlags(virDomainPtr domain, unsigned long memory,
                        unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "memory=%lu, flags=0x%x", memory, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonZeroArgGoto(memory, error);

    if (virMemoryMaxValue(true) / 1024 <= memory) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %lu"),
                       memory);
        goto error;
    }

    if (conn->driver->domainSetMemoryFlags) {
        int ret;
        ret = conn->driver->domainSetMemoryFlags(domain, memory, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetMemoryStatsPeriod:
 * @domain: a domain object or NULL
 * @period: the period in seconds for stats collection
 * @flags: bitwise-OR of virDomainMemoryModFlags
 *
 * Dynamically change the domain memory balloon driver statistics collection
 * period. Use 0 to disable and a positive value to enable.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set. If VIR_DOMAIN_AFFECT_LIVE is set, the change affects
 * a running domain and will fail if domain is not active.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified
 * (that is, @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain
 * modifies persistent setup, while an active domain is hypervisor-dependent
 * on whether just live or both live and persistent state is changed.
 *
 * Not all hypervisors can support all flag combinations.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSetMemoryStatsPeriod(virDomainPtr domain, int period,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "period=%d, flags=0x%x", period, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    /* This must be positive to set the balloon collection period */
    virCheckNonNegativeArgGoto(period, error);

    if (conn->driver->domainSetMemoryStatsPeriod) {
        int ret;
        ret = conn->driver->domainSetMemoryStatsPeriod(domain, period, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetMemoryParameters:
 * @domain: pointer to domain object
 * @params: pointer to memory parameter objects
 * @nparams: number of memory parameter (this value can be the same or
 *          less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change all or a subset of the memory tunables.
 * This function may require privileged access to the hypervisor.
 *
 * Possible values for all *_limit memory tunables are in range from 0 to
 * VIR_DOMAIN_MEMORY_PARAM_UNLIMITED.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetMemoryParameters(virDomainPtr domain,
                             virTypedParameterPtr params,
                             int nparams, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckPositiveArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetMemoryParameters) {
        int ret;
        ret = conn->driver->domainSetMemoryParameters(domain, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetMemoryParameters:
 * @domain: pointer to domain object
 * @params: pointer to memory parameter object
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of memory parameters; input and output
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all memory parameters.  On input, @nparams gives the size of the
 * @params array; on output, @nparams gives how many slots were filled
 * with parameter information, which might be less but will not exceed
 * the input value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0 on
 * input will cause @nparams on output to contain the number of parameters
 * supported by the hypervisor. The caller should then allocate @params
 * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the API
 * again.
 *
 * Here is a sample code snippet:
 *
 *   if (virDomainGetMemoryParameters(dom, NULL, &nparams, 0) == 0 &&
 *       nparams != 0) {
 *       if ((params = malloc(sizeof(*params) * nparams)) == NULL)
 *           goto error;
 *       memset(params, 0, sizeof(*params) * nparams);
 *       if (virDomainGetMemoryParameters(dom, params, &nparams, 0))
 *           goto error;
 *   }
 *
 * This function may require privileged access to the hypervisor. This function
 * expects the caller to allocate the @params.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetMemoryParameters(virDomainPtr domain,
                             virTypedParameterPtr params,
                             int *nparams, unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, (nparams) ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0)
        virCheckNonNullArgGoto(params, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    conn = domain->conn;

    if (conn->driver->domainGetMemoryParameters) {
        int ret;
        ret = conn->driver->domainGetMemoryParameters(domain, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetNumaParameters:
 * @domain: pointer to domain object
 * @params: pointer to numa parameter objects
 * @nparams: number of numa parameters (this value can be the same or
 *          less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change all or a subset of the numa tunables.
 * This function may require privileged access to the hypervisor.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetNumaParameters(virDomainPtr domain,
                           virTypedParameterPtr params,
                           int nparams, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckPositiveArgGoto(nparams, error);
    if (virTypedParameterValidateSet(domain->conn, params, nparams) < 0)
        goto error;

    conn = domain->conn;

    if (conn->driver->domainSetNumaParameters) {
        int ret;
        ret = conn->driver->domainSetNumaParameters(domain, params, nparams,
                                                    flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetNumaParameters:
 * @domain: pointer to domain object
 * @params: pointer to numa parameter object
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of numa parameters
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all numa parameters.  On input, @nparams gives the size of the
 * @params array; on output, @nparams gives how many slots were filled
 * with parameter information, which might be less but will not exceed
 * the input value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0 on
 * input will cause @nparams on output to contain the number of parameters
 * supported by the hypervisor. The caller should then allocate @params
 * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the API
 * again.
 *
 * See virDomainGetMemoryParameters() for an equivalent usage example.
 *
 * This function may require privileged access to the hypervisor. This function
 * expects the caller to allocate the @params.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetNumaParameters(virDomainPtr domain,
                           virTypedParameterPtr params,
                           int *nparams, unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, (nparams) ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0)
        virCheckNonNullArgGoto(params, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    conn = domain->conn;

    if (conn->driver->domainGetNumaParameters) {
        int ret;
        ret = conn->driver->domainGetNumaParameters(domain, params, nparams,
                                                    flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetBlkioParameters:
 * @domain: pointer to domain object
 * @params: pointer to blkio parameter objects
 * @nparams: number of blkio parameters (this value can be the same or
 *          less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change all or a subset of the blkio tunables.
 * This function may require privileged access to the hypervisor.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetBlkioParameters(virDomainPtr domain,
                            virTypedParameterPtr params,
                            int nparams, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNegativeArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetBlkioParameters) {
        int ret;
        ret = conn->driver->domainSetBlkioParameters(domain, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetBlkioParameters:
 * @domain: pointer to domain object
 * @params: pointer to blkio parameter object
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of blkio parameters; input and output
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all blkio parameters.  On input, @nparams gives the size of the
 * @params array; on output, @nparams gives how many slots were filled
 * with parameter information, which might be less but will not exceed
 * the input value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0 on
 * input will cause @nparams on output to contain the number of parameters
 * supported by the hypervisor. The caller should then allocate @params
 * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the API
 * again.
 *
 * See virDomainGetMemoryParameters() for an equivalent usage example.
 *
 * This function may require privileged access to the hypervisor. This function
 * expects the caller to allocate the @params.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetBlkioParameters(virDomainPtr domain,
                            virTypedParameterPtr params,
                            int *nparams, unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, (nparams) ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0)
        virCheckNonNullArgGoto(params, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    conn = domain->conn;

    if (conn->driver->domainGetBlkioParameters) {
        int ret;
        ret = conn->driver->domainGetBlkioParameters(domain, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetInfo:
 * @domain: a domain object
 * @info: pointer to a virDomainInfo structure allocated by the user
 *
 * Extract information about a domain. Note that if the connection
 * used to get the domain is limited only a partial set of the information
 * can be extracted.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetInfo(virDomainPtr domain, virDomainInfoPtr info)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "info=%p", info);

    virResetLastError();

    if (info)
        memset(info, 0, sizeof(*info));

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(info, error);

    conn = domain->conn;

    if (conn->driver->domainGetInfo) {
        int ret;
        ret = conn->driver->domainGetInfo(domain, info);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetState:
 * @domain: a domain object
 * @state: returned state of the domain (one of virDomainState)
 * @reason: returned reason which led to @state (one of virDomain*Reason
 * corresponding to the current state); it is allowed to be NULL
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Extract domain state. Each state can be accompanied with a reason (if known)
 * which led to the state.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetState(virDomainPtr domain,
                  int *state,
                  int *reason,
                  unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "state=%p, reason=%p, flags=0x%x",
                     state, reason, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(state, error);

    conn = domain->conn;
    if (conn->driver->domainGetState) {
        int ret;
        ret = conn->driver->domainGetState(domain, state, reason, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetControlInfo:
 * @domain: a domain object
 * @info: pointer to a virDomainControlInfo structure allocated by the user
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Extract details about current state of control interface to a domain.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetControlInfo(virDomainPtr domain,
                        virDomainControlInfoPtr info,
                        unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "info=%p, flags=0x%x", info, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(info, error);

    conn = domain->conn;
    if (conn->driver->domainGetControlInfo) {
        int ret;
        ret = conn->driver->domainGetControlInfo(domain, info, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetXMLDesc:
 * @domain: a domain object
 * @flags: bitwise-OR of virDomainXMLFlags
 *
 * Provide an XML description of the domain. The description may be reused
 * later to relaunch the domain with virDomainCreateXML().
 *
 * No security-sensitive data will be included unless @flags contains
 * VIR_DOMAIN_XML_SECURE; this flag is rejected on read-only
 * connections.  If @flags includes VIR_DOMAIN_XML_INACTIVE, then the
 * XML represents the configuration that will be used on the next boot
 * of a persistent domain; otherwise, the configuration represents the
 * currently running domain.  If @flags contains
 * VIR_DOMAIN_XML_UPDATE_CPU, then the portion of the domain XML
 * describing CPU capabilities is modified to match actual
 * capabilities of the host.
 *
 * If @flags contains VIR_DOMAIN_XML_MIGRATABLE, the XML is altered to
 * assist in migrations, since the source and destination may be
 * running different libvirt versions.  This may include trimming
 * redundant or default information that might confuse an older
 * recipient, or exposing internal details that aid a newer recipient;
 * this flag is rejected on read-only connections, and the resulting
 * XML might not validate against the schema, so it is mainly for
 * internal use.
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case
 * of error. The caller must free() the returned value.
 */
char *
virDomainGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    if ((conn->flags & VIR_CONNECT_RO) &&
        (flags & (VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_MIGRATABLE))) {
        virReportError(VIR_ERR_OPERATION_DENIED, "%s",
                       _("virDomainGetXMLDesc with secure flag"));
        goto error;
    }

    if (conn->driver->domainGetXMLDesc) {
        char *ret;
        ret = conn->driver->domainGetXMLDesc(domain, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virConnectDomainXMLFromNative:
 * @conn: a connection object
 * @nativeFormat: configuration format importing from
 * @nativeConfig: the configuration data to import
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Reads native configuration data  describing a domain, and
 * generates libvirt domain XML. The format of the native
 * data is hypervisor dependent.
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case
 * of error. The caller must free() the returned value.
 */
char *
virConnectDomainXMLFromNative(virConnectPtr conn,
                              const char *nativeFormat,
                              const char *nativeConfig,
                              unsigned int flags)
{
    VIR_DEBUG("conn=%p, format=%s, config=%s, flags=0x%x",
              conn, NULLSTR(nativeFormat), NULLSTR(nativeConfig), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckReadOnlyGoto(conn->flags, error);

    virCheckNonNullArgGoto(nativeFormat, error);
    virCheckNonNullArgGoto(nativeConfig, error);

    if (conn->driver->connectDomainXMLFromNative) {
        char *ret;
        ret = conn->driver->connectDomainXMLFromNative(conn,
                                                       nativeFormat,
                                                       nativeConfig,
                                                       flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectDomainXMLToNative:
 * @conn: a connection object
 * @nativeFormat: configuration format exporting to
 * @domainXml: the domain configuration to export
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Reads a domain XML configuration document, and generates
 * a native configuration file describing the domain.
 * The format of the native data is hypervisor dependent.
 *
 * Returns a 0 terminated UTF-8 encoded native config datafile, or
 * NULL in case of error. The caller must free() the returned value.
 */
char *
virConnectDomainXMLToNative(virConnectPtr conn,
                            const char *nativeFormat,
                            const char *domainXml,
                            unsigned int flags)
{
    VIR_DEBUG("conn=%p, format=%s, xml=%s, flags=0x%x",
              conn, NULLSTR(nativeFormat), NULLSTR(domainXml), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckReadOnlyGoto(conn->flags, error);

    virCheckNonNullArgGoto(nativeFormat, error);
    virCheckNonNullArgGoto(domainXml, error);

    if (conn->driver->connectDomainXMLToNative) {
        char *ret;
        ret = conn->driver->connectDomainXMLToNative(conn,
                                                     nativeFormat,
                                                     domainXml,
                                                     flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/*
 * Sequence v1:
 *
 *  Dst: Prepare
 *        - Get ready to accept incoming VM
 *        - Generate optional cookie to pass to src
 *
 *  Src: Perform
 *        - Start migration and wait for send completion
 *        - Kill off VM if successful, resume if failed
 *
 *  Dst: Finish
 *        - Wait for recv completion and check status
 *        - Kill off VM if unsuccessful
 *
 */
static virDomainPtr
virDomainMigrateVersion1(virDomainPtr domain,
                         virConnectPtr dconn,
                         unsigned long flags,
                         const char *dname,
                         const char *uri,
                         unsigned long bandwidth)
{
    g_autofree char *uri_out = NULL;
    g_autofree char *cookie = NULL;
    int cookielen = 0, ret;
    virDomainInfo info;
    unsigned int destflags;

    VIR_DOMAIN_DEBUG(domain,
                     "dconn=%p, flags=0x%lx, dname=%s, uri=%s, bandwidth=%lu",
                     dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth);

    ret = virDomainGetInfo(domain, &info);
    if (ret == 0 && info.state == VIR_DOMAIN_PAUSED)
        flags |= VIR_MIGRATE_PAUSED;

    destflags = flags & ~(VIR_MIGRATE_ABORT_ON_ERROR |
                          VIR_MIGRATE_AUTO_CONVERGE);

    /* Prepare the migration.
     *
     * The destination host may return a cookie, or leave cookie as
     * NULL.
     *
     * The destination host MUST set uri_out if uri_in is NULL.
     *
     * If uri_in is non-NULL, then the destination host may modify
     * the URI by setting uri_out.  If it does not wish to modify
     * the URI, it should leave uri_out as NULL.
     */
    if (dconn->driver->domainMigratePrepare
        (dconn, &cookie, &cookielen, uri, &uri_out, destflags, dname,
         bandwidth) == -1)
        return NULL;

    if (uri == NULL && uri_out == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("domainMigratePrepare did not set uri"));
        return NULL;
    }
    if (uri_out)
        uri = uri_out; /* Did domainMigratePrepare change URI? */

    /* Perform the migration.  The driver isn't supposed to return
     * until the migration is complete.
     */
    if (domain->conn->driver->domainMigratePerform
        (domain, cookie, cookielen, uri, flags, dname, bandwidth) == -1)
        return NULL;

    /* Get the destination domain and return it or error.
     * 'domain' no longer actually exists at this point
     * (or so we hope), but we still use the object in memory
     * in order to get the name.
     */
    dname = dname ? dname : domain->name;
    if (dconn->driver->domainMigrateFinish)
        return dconn->driver->domainMigrateFinish
            (dconn, dname, cookie, cookielen, uri, destflags);

    return virDomainLookupByName(dconn, dname);
}


/*
 * Sequence v2:
 *
 *  Src: DumpXML
 *        - Generate XML to pass to dst
 *
 *  Dst: Prepare
 *        - Get ready to accept incoming VM
 *        - Generate optional cookie to pass to src
 *
 *  Src: Perform
 *        - Start migration and wait for send completion
 *        - Kill off VM if successful, resume if failed
 *
 *  Dst: Finish
 *        - Wait for recv completion and check status
 *        - Kill off VM if unsuccessful
 *
 */
static virDomainPtr
virDomainMigrateVersion2(virDomainPtr domain,
                         virConnectPtr dconn,
                         unsigned long flags,
                         const char *dname,
                         const char *uri,
                         unsigned long bandwidth)
{
    virDomainPtr ddomain = NULL;
    g_autofree char *uri_out = NULL;
    g_autofree char *cookie = NULL;
    g_autofree char *dom_xml = NULL;
    int cookielen = 0, ret;
    virDomainInfo info;
    virErrorPtr orig_err = NULL;
    unsigned int getxml_flags = 0;
    int cancelled;
    unsigned long destflags;

    VIR_DOMAIN_DEBUG(domain,
                     "dconn=%p, flags=0x%lx, dname=%s, uri=%s, bandwidth=%lu",
                     dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth);

    /* Prepare the migration.
     *
     * The destination host may return a cookie, or leave cookie as
     * NULL.
     *
     * The destination host MUST set uri_out if uri_in is NULL.
     *
     * If uri_in is non-NULL, then the destination host may modify
     * the URI by setting uri_out.  If it does not wish to modify
     * the URI, it should leave uri_out as NULL.
     */

    /* In version 2 of the protocol, the prepare step is slightly
     * different.  We fetch the domain XML of the source domain
     * and pass it to Prepare2.
     */
    if (!domain->conn->driver->domainGetXMLDesc) {
        virReportUnsupportedError();
        return NULL;
    }

    ret = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                   VIR_DRV_FEATURE_XML_MIGRATABLE);
    if (ret < 0)
        return NULL;
    if (ret)
        getxml_flags |= VIR_DOMAIN_XML_MIGRATABLE;
    else
        getxml_flags |= VIR_DOMAIN_XML_SECURE | VIR_DOMAIN_XML_UPDATE_CPU;

    dom_xml = domain->conn->driver->domainGetXMLDesc(domain, getxml_flags);
    if (!dom_xml)
        return NULL;

    ret = virDomainGetInfo(domain, &info);
    if (ret == 0 && info.state == VIR_DOMAIN_PAUSED)
        flags |= VIR_MIGRATE_PAUSED;

    destflags = flags & ~(VIR_MIGRATE_ABORT_ON_ERROR |
                          VIR_MIGRATE_AUTO_CONVERGE);

    VIR_DEBUG("Prepare2 %p flags=0x%lx", dconn, destflags);
    ret = dconn->driver->domainMigratePrepare2
        (dconn, &cookie, &cookielen, uri, &uri_out, destflags, dname,
         bandwidth, dom_xml);
    if (ret == -1)
        goto done;

    if (uri == NULL && uri_out == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("domainMigratePrepare2 did not set uri"));
        cancelled = 1;
        /* Make sure Finish doesn't overwrite the error */
        virErrorPreserveLast(&orig_err);
        goto finish;
    }
    if (uri_out)
        uri = uri_out; /* Did domainMigratePrepare2 change URI? */

    /* Perform the migration.  The driver isn't supposed to return
     * until the migration is complete.
     */
    VIR_DEBUG("Perform %p", domain->conn);
    ret = domain->conn->driver->domainMigratePerform
        (domain, cookie, cookielen, uri, flags, dname, bandwidth);

    /* Perform failed. Make sure Finish doesn't overwrite the error */
    if (ret < 0)
        virErrorPreserveLast(&orig_err);

    /* If Perform returns < 0, then we need to cancel the VM
     * startup on the destination
     */
    cancelled = ret < 0 ? 1 : 0;

 finish:
    /* In version 2 of the migration protocol, we pass the
     * status code from the sender to the destination host,
     * so it can do any cleanup if the migration failed.
     */
    dname = dname ? dname : domain->name;
    VIR_DEBUG("Finish2 %p ret=%d", dconn, ret);
    ddomain = dconn->driver->domainMigrateFinish2
        (dconn, dname, cookie, cookielen, uri, destflags, cancelled);
    if (cancelled && ddomain)
        VIR_ERROR(_("finish step ignored that migration was cancelled"));

 done:
    virErrorRestore(&orig_err);
    return ddomain;
}


/*
 * Sequence v3:
 *
 *  Src: Begin
 *        - Generate XML to pass to dst
 *        - Generate optional cookie to pass to dst
 *
 *  Dst: Prepare
 *        - Get ready to accept incoming VM
 *        - Generate optional cookie to pass to src
 *
 *  Src: Perform
 *        - Start migration and wait for send completion
 *        - Generate optional cookie to pass to dst
 *
 *  Dst: Finish
 *        - Wait for recv completion and check status
 *        - Kill off VM if failed, resume if success
 *        - Generate optional cookie to pass to src
 *
 *  Src: Confirm
 *        - Kill off VM if success, resume if failed
 *
  * If useParams is true, params and nparams contain migration parameters and
  * we know it's safe to call the API which supports extensible parameters.
  * Otherwise, we have to use xmlin, dname, uri, and bandwidth and pass them
  * to the old-style APIs.
 */
static virDomainPtr
virDomainMigrateVersion3Full(virDomainPtr domain,
                             virConnectPtr dconn,
                             const char *xmlin,
                             const char *dname,
                             const char *uri,
                             unsigned long long bandwidth,
                             virTypedParameterPtr params,
                             int nparams,
                             bool useParams,
                             unsigned int flags)
{
    virDomainPtr ddomain = NULL;
    g_autofree char *uri_out = NULL;
    g_autofree char *cookiein = NULL;
    g_autofree char *cookieout = NULL;
    g_autofree char *dom_xml = NULL;
    int cookieinlen = 0;
    int cookieoutlen = 0;
    int ret;
    virDomainInfo info;
    virErrorPtr orig_err = NULL;
    int cancelled = 1;
    unsigned long protection = 0;
    bool notify_source = true;
    unsigned int destflags;
    int state;
    virTypedParameterPtr tmp;

    VIR_DOMAIN_DEBUG(domain,
                     "dconn=%p, xmlin=%s, dname=%s, uri=%s, bandwidth=%llu, "
                     "params=%p, nparams=%d, useParams=%d, flags=0x%x",
                     dconn, NULLSTR(xmlin), NULLSTR(dname), NULLSTR(uri),
                     bandwidth, params, nparams, useParams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    if ((!useParams &&
         (!domain->conn->driver->domainMigrateBegin3 ||
          !domain->conn->driver->domainMigratePerform3 ||
          !domain->conn->driver->domainMigrateConfirm3 ||
          !dconn->driver->domainMigratePrepare3 ||
          !dconn->driver->domainMigrateFinish3)) ||
        (useParams &&
         (!domain->conn->driver->domainMigrateBegin3Params ||
          !domain->conn->driver->domainMigratePerform3Params ||
          !domain->conn->driver->domainMigrateConfirm3Params ||
          !dconn->driver->domainMigratePrepare3Params ||
          !dconn->driver->domainMigrateFinish3Params))) {
        virReportUnsupportedError();
        return NULL;
    }

    if (virTypedParamsCopy(&tmp, params, nparams) < 0)
        return NULL;
    params = tmp;

    ret = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                   VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION);
    if (ret < 0)
        goto done;
    if (ret)
        protection = VIR_MIGRATE_CHANGE_PROTECTION;

    VIR_DEBUG("Begin3 %p", domain->conn);
    if (useParams) {
        dom_xml = domain->conn->driver->domainMigrateBegin3Params
            (domain, params, nparams, &cookieout, &cookieoutlen,
             flags | protection);
    } else {
        dom_xml = domain->conn->driver->domainMigrateBegin3
            (domain, xmlin, &cookieout, &cookieoutlen,
             flags | protection, dname, bandwidth);
    }
    if (!dom_xml)
        goto done;

    if (useParams) {
        /* If source is new enough to support extensible migration parameters,
         * it's certainly new enough to support virDomainGetState. */
        ret = virDomainGetState(domain, &state, NULL, 0);
    } else {
        ret = virDomainGetInfo(domain, &info);
        state = info.state;
    }
    if (ret == 0 && state == VIR_DOMAIN_PAUSED)
        flags |= VIR_MIGRATE_PAUSED;

    destflags = flags & ~(VIR_MIGRATE_ABORT_ON_ERROR |
                          VIR_MIGRATE_AUTO_CONVERGE);

    VIR_DEBUG("Prepare3 %p flags=0x%x", dconn, destflags);
    cookiein = g_steal_pointer(&cookieout);
    cookieinlen = cookieoutlen;
    cookieoutlen = 0;
    if (useParams) {
        if (virTypedParamsReplaceString(&params, &nparams,
                                        VIR_MIGRATE_PARAM_DEST_XML,
                                        dom_xml) < 0)
            goto done;
        ret = dconn->driver->domainMigratePrepare3Params
            (dconn, params, nparams, cookiein, cookieinlen,
             &cookieout, &cookieoutlen, &uri_out, destflags);
    } else {
        ret = dconn->driver->domainMigratePrepare3
            (dconn, cookiein, cookieinlen, &cookieout, &cookieoutlen,
             uri, &uri_out, destflags, dname, bandwidth, dom_xml);
    }
    if (ret == -1) {
        if (protection) {
            /* Begin already started a migration job so we need to cancel it by
             * calling Confirm while making sure it doesn't overwrite the error
             */
            virErrorPreserveLast(&orig_err);
            goto confirm;
        } else {
            goto done;
        }
    }

    /* Did domainMigratePrepare3 change URI? */
    if (uri_out) {
        uri = uri_out;
        if (useParams &&
            virTypedParamsReplaceString(&params, &nparams,
                                        VIR_MIGRATE_PARAM_URI,
                                        uri_out) < 0) {
            cancelled = 1;
            virErrorPreserveLast(&orig_err);
            goto finish;
        }
    } else if (!uri &&
               virTypedParamsGetString(params, nparams,
                                       VIR_MIGRATE_PARAM_URI, &uri) <= 0) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("domainMigratePrepare3 did not set uri"));
        cancelled = 1;
        virErrorPreserveLast(&orig_err);
        goto finish;
    }

    if (flags & VIR_MIGRATE_OFFLINE) {
        VIR_DEBUG("Offline migration, skipping Perform phase");
        VIR_FREE(cookieout);
        cookieoutlen = 0;
        cancelled = 0;
        goto finish;
    }

    /* Perform the migration.  The driver isn't supposed to return
     * until the migration is complete. The src VM should remain
     * running, but in paused state until the destination can
     * confirm migration completion.
     */
    VIR_DEBUG("Perform3 %p uri=%s", domain->conn, uri);
    VIR_FREE(cookiein);
    cookiein = g_steal_pointer(&cookieout);
    cookieinlen = cookieoutlen;
    cookieoutlen = 0;
    /* dconnuri not relevant in non-P2P modes, so left NULL here */
    if (useParams) {
        ret = domain->conn->driver->domainMigratePerform3Params
            (domain, NULL, params, nparams, cookiein, cookieinlen,
             &cookieout, &cookieoutlen, flags | protection);
    } else {
        ret = domain->conn->driver->domainMigratePerform3
            (domain, NULL, cookiein, cookieinlen,
             &cookieout, &cookieoutlen, NULL,
             uri, flags | protection, dname, bandwidth);
    }

    /* Perform failed. Make sure Finish doesn't overwrite the error */
    if (ret < 0) {
        virErrorPreserveLast(&orig_err);
        /* Perform failed so we don't need to call confirm to let source know
         * about the failure.
         */
        notify_source = false;
    }

    /* If Perform returns < 0, then we need to cancel the VM
     * startup on the destination
     */
    cancelled = ret < 0 ? 1 : 0;

 finish:
    /*
     * The status code from the source is passed to the destination.
     * The dest can cleanup if the source indicated it failed to
     * send all migration data. Returns NULL for ddomain if
     * the dest was unable to complete migration.
     */
    VIR_DEBUG("Finish3 %p ret=%d", dconn, ret);
    VIR_FREE(cookiein);
    cookiein = g_steal_pointer(&cookieout);
    cookieinlen = cookieoutlen;
    cookieoutlen = 0;
    if (useParams) {
        if (virTypedParamsGetString(params, nparams,
                                    VIR_MIGRATE_PARAM_DEST_NAME, NULL) <= 0 &&
            virTypedParamsReplaceString(&params, &nparams,
                                        VIR_MIGRATE_PARAM_DEST_NAME,
                                        domain->name) < 0) {
            ddomain = NULL;
        } else {
            ddomain = dconn->driver->domainMigrateFinish3Params
                (dconn, params, nparams, cookiein, cookieinlen,
                 &cookieout, &cookieoutlen, destflags, cancelled);
        }
    } else {
        dname = dname ? dname : domain->name;
        ddomain = dconn->driver->domainMigrateFinish3
            (dconn, dname, cookiein, cookieinlen, &cookieout, &cookieoutlen,
             NULL, uri, destflags, cancelled);
    }

    if (cancelled) {
        if (ddomain) {
            VIR_ERROR(_("finish step ignored that migration was cancelled"));
        } else {
            /* If Finish reported a useful error, use it instead of the
             * original "migration unexpectedly failed" error.
             *
             * This is ugly but we can't do better with the APIs we have. We
             * only replace the error if Finish was called with cancelled == 1
             * and reported a real error (old libvirt would report an error
             * from RPC instead of MIGRATE_FINISH_OK), which only happens when
             * the domain died on destination. To further reduce a possibility
             * of false positives we also check that Perform returned
             * VIR_ERR_OPERATION_FAILED.
             */
            if (orig_err &&
                orig_err->domain == VIR_FROM_QEMU &&
                orig_err->code == VIR_ERR_OPERATION_FAILED) {
                virErrorPtr err = virGetLastError();
                if (err &&
                    err->domain == VIR_FROM_QEMU &&
                    err->code != VIR_ERR_MIGRATE_FINISH_OK) {
                    virFreeError(orig_err);
                    orig_err = NULL;
                }
            }
        }
    }

    /* If ddomain is NULL, then we were unable to start
     * the guest on the target, and must restart on the
     * source. There is a small chance that the ddomain
     * is NULL due to an RPC failure, in which case
     * ddomain could in fact be running on the dest.
     * The lock manager plugins should take care of
     * safety in this scenario.
     */
    cancelled = ddomain == NULL ? 1 : 0;

    /* If finish3 set an error, and we don't have an earlier
     * one we need to preserve it in case confirm3 overwrites
     */
    if (!orig_err)
        virErrorPreserveLast(&orig_err);

 confirm:
    /*
     * If cancelled, then src VM will be restarted, else it will be killed.
     * Don't do this if migration failed on source and thus it was already
     * cancelled there.
     */
    if (notify_source) {
        VIR_DEBUG("Confirm3 %p ret=%d domain=%p", domain->conn, ret, domain);
        VIR_FREE(cookiein);
        cookiein = g_steal_pointer(&cookieout);
        cookieinlen = cookieoutlen;
        cookieoutlen = 0;
        if (useParams) {
            ret = domain->conn->driver->domainMigrateConfirm3Params
                (domain, params, nparams, cookiein, cookieinlen,
                 flags | protection, cancelled);
        } else {
            ret = domain->conn->driver->domainMigrateConfirm3
                (domain, cookiein, cookieinlen,
                 flags | protection, cancelled);
        }
        /* If Confirm3 returns -1, there's nothing more we can
         * do, but fortunately worst case is that there is a
         * domain left in 'paused' state on source.
         */
        if (ret < 0) {
            VIR_WARN("Guest %s probably left in 'paused' state on source",
                     domain->name);
        }
    }

 done:
    virErrorRestore(&orig_err);
    virTypedParamsFree(params, nparams);
    return ddomain;
}


static virDomainPtr
virDomainMigrateVersion3(virDomainPtr domain,
                         virConnectPtr dconn,
                         const char *xmlin,
                         unsigned long flags,
                         const char *dname,
                         const char *uri,
                         unsigned long bandwidth)
{
    return virDomainMigrateVersion3Full(domain, dconn, xmlin, dname, uri,
                                        bandwidth, NULL, 0, false, flags);
}


static virDomainPtr
virDomainMigrateVersion3Params(virDomainPtr domain,
                               virConnectPtr dconn,
                               virTypedParameterPtr params,
                               int nparams,
                               unsigned int flags)
{
    return virDomainMigrateVersion3Full(domain, dconn, NULL, NULL, NULL, 0,
                                        params, nparams, true, flags);
}

int
virDomainMigrateCheckNotLocal(const char *dconnuri)
{
    g_autoptr(virURI) tempuri = NULL;

    if (!(tempuri = virURIParse(dconnuri)))
        return -1;

    /*
     * If someone migrates explicitly to a unix socket, then they have to know
     * what they are doing and it most probably was not a mistake.
     */
    if ((tempuri->server && STRPREFIX(tempuri->server, "localhost")) ||
        (!tempuri->server && !virURICheckUnixSocket(tempuri))) {
        virReportInvalidArg(dconnuri, "%s",
                            _("Attempt to migrate guest to the same host"));
        return -1;
    }

    return 0;
}


static int
virDomainMigrateUnmanagedProto2(virDomainPtr domain,
                                const char *dconnuri,
                                virTypedParameterPtr params,
                                int nparams,
                                unsigned int flags)
{
    /* uri parameter is added for direct case */
    const char *compatParams[] = { VIR_MIGRATE_PARAM_DEST_NAME,
                                   VIR_MIGRATE_PARAM_BANDWIDTH,
                                   VIR_MIGRATE_PARAM_URI };
    const char *uri = NULL;
    const char *miguri = NULL;
    const char *dname = NULL;
    unsigned long long bandwidth = 0;

    if (!virTypedParamsCheck(params, nparams, compatParams,
                             G_N_ELEMENTS(compatParams))) {
        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                       _("Some parameters are not supported by migration "
                         "protocol 2"));
        return -1;
    }

    if (virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_URI, &miguri) < 0 ||
        virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0 ||
        virTypedParamsGetULLong(params, nparams,
                                VIR_MIGRATE_PARAM_BANDWIDTH, &bandwidth) < 0) {
        return -1;
    }

    if (flags & VIR_MIGRATE_PEER2PEER) {
        if (miguri) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("Unable to override peer2peer migration URI"));
            return -1;
        }
        uri = dconnuri;
    } else {
        uri = miguri;
    }

    return domain->conn->driver->domainMigratePerform
            (domain, NULL, 0, uri, flags, dname, bandwidth);
}


static int
virDomainMigrateUnmanagedProto3(virDomainPtr domain,
                                const char *dconnuri,
                                virTypedParameterPtr params,
                                int nparams,
                                unsigned int flags)
{
    const char *compatParams[] = { VIR_MIGRATE_PARAM_URI,
                                   VIR_MIGRATE_PARAM_DEST_NAME,
                                   VIR_MIGRATE_PARAM_DEST_XML,
                                   VIR_MIGRATE_PARAM_BANDWIDTH };
    const char *miguri = NULL;
    const char *dname = NULL;
    const char *xmlin = NULL;
    unsigned long long bandwidth = 0;

    if (!virTypedParamsCheck(params, nparams, compatParams,
                             G_N_ELEMENTS(compatParams))) {
        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                       _("Some parameters are not supported by migration "
                         "protocol 3"));
        return -1;
    }

    if (virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_URI, &miguri) < 0 ||
        virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0 ||
        virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_DEST_XML, &xmlin) < 0 ||
        virTypedParamsGetULLong(params, nparams,
                                VIR_MIGRATE_PARAM_BANDWIDTH, &bandwidth) < 0) {
        return -1;
    }

    return domain->conn->driver->domainMigratePerform3
            (domain, xmlin, NULL, 0, NULL, NULL, dconnuri,
             miguri, flags, dname, bandwidth);
}


/*
 * In normal migration, the libvirt client coordinates communication
 * between the 2 libvirtd instances on source & dest hosts.
 *
 * This function encapsulates 2 alternatives to the above case.
 *
 * 1. peer-2-peer migration, the libvirt client only talks to the source
 * libvirtd instance. The source libvirtd then opens its own
 * connection to the destination and coordinates migration itself.
 *
 * 2. direct migration, where there is no requirement for a libvirtd instance
 * on the dest host. Eg, XenD can talk direct to XenD, so libvirtd on dest
 * does not need to be involved at all, or even running.
 */
static int
virDomainMigrateUnmanagedParams(virDomainPtr domain,
                                const char *dconnuri,
                                virTypedParameterPtr params,
                                int nparams,
                                unsigned int flags)
{
    int rc;

    VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, params=%p, nparams=%d, flags=0x%x",
                     NULLSTR(dconnuri), params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    if ((flags & VIR_MIGRATE_PEER2PEER) &&
        virDomainMigrateCheckNotLocal(dconnuri) < 0)
        return -1;

    if (flags & VIR_MIGRATE_PEER2PEER) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_PARAMS);
        if (rc < 0)
            return -1;
        if (rc) {
            VIR_DEBUG("Using migration protocol 3 with extensible parameters");
            if (!domain->conn->driver->domainMigratePerform3Params) {
                virReportUnsupportedError();
                return -1;
            }
            return domain->conn->driver->domainMigratePerform3Params
                    (domain, dconnuri, params, nparams,
                     NULL, 0, NULL, NULL, flags);
        }
    }

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_MIGRATION_V3);
    if (rc < 0)
        return -1;
    if (rc) {
        VIR_DEBUG("Using migration protocol 3");
        if (!domain->conn->driver->domainMigratePerform3) {
            virReportUnsupportedError();
            return -1;
        }
        return virDomainMigrateUnmanagedProto3(domain, dconnuri,
                                               params, nparams, flags);
    } else {
        VIR_DEBUG("Using migration protocol 2");
        if (!domain->conn->driver->domainMigratePerform) {
            virReportUnsupportedError();
            return -1;
        }
        return virDomainMigrateUnmanagedProto2(domain, dconnuri,
                                               params, nparams, flags);
    }
}


static int
virDomainMigrateUnmanaged(virDomainPtr domain,
                          const char *xmlin,
                          unsigned int flags,
                          const char *dname,
                          const char *dconnuri,
                          const char *miguri,
                          unsigned long long bandwidth)
{
    int ret = -1;
    virTypedParameterPtr params = NULL;
    int nparams = 0;
    int maxparams = 0;

    if (miguri &&
        virTypedParamsAddString(&params, &nparams, &maxparams,
                                VIR_MIGRATE_PARAM_URI, miguri) < 0)
        goto cleanup;
    if (dname &&
        virTypedParamsAddString(&params, &nparams, &maxparams,
                                VIR_MIGRATE_PARAM_DEST_NAME, dname) < 0)
        goto cleanup;
    if (xmlin &&
        virTypedParamsAddString(&params, &nparams, &maxparams,
                                VIR_MIGRATE_PARAM_DEST_XML, xmlin) < 0)
        goto cleanup;
    if (virTypedParamsAddULLong(&params, &nparams, &maxparams,
                                VIR_MIGRATE_PARAM_BANDWIDTH, bandwidth) < 0)
        goto cleanup;

    ret = virDomainMigrateUnmanagedParams(domain, dconnuri, params,
                                          nparams, flags);

 cleanup:
    virTypedParamsFree(params, nparams);

    return ret;
}


/**
 * virDomainMigrate:
 * @domain: a domain object
 * @dconn: destination host (a connection object)
 * @flags: bitwise-OR of virDomainMigrateFlags
 * @dname: (optional) rename domain to this at destination
 * @uri: (optional) dest hostname/URI as seen from the source host
 * @bandwidth: (optional) specify migration bandwidth limit in MiB/s
 *
 * Migrate the domain object from its current host to the destination
 * host given by dconn (a connection to the destination host).
 *
 * This function is similar to virDomainMigrate3, but it only supports a fixed
 * set of parameters: @dname corresponds to VIR_MIGRATE_PARAM_DEST_NAME, @uri
 * is VIR_MIGRATE_PARAM_URI, and @bandwidth is VIR_MIGRATE_PARAM_BANDWIDTH.
 *
 * virDomainFree should be used to free the resources after the
 * returned domain object is no longer needed.
 *
 * Returns the new domain object if the migration was successful,
 *   or NULL in case of error.  Note that the new domain object
 *   exists in the scope of the destination connection (dconn).
 */
virDomainPtr
virDomainMigrate(virDomainPtr domain,
                 virConnectPtr dconn,
                 unsigned long flags,
                 const char *dname,
                 const char *uri,
                 unsigned long bandwidth)
{
    virDomainPtr ddomain = NULL;
    int rc_src;
    int rc_dst;
    int rc;

    VIR_DOMAIN_DEBUG(domain,
                     "dconn=%p, flags=0x%lx, dname=%s, uri=%s, bandwidth=%lu",
                     dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, NULL);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    /* Now checkout the destination */
    virCheckConnectGoto(dconn, error);
    virCheckReadOnlyGoto(dconn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_NON_SHARED_DISK,
                             VIR_MIGRATE_NON_SHARED_INC,
                             error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_TUNNELLED,
                             VIR_MIGRATE_PARALLEL,
                             error);

    if (flags & VIR_MIGRATE_OFFLINE) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the source host"));
            goto error;
        }

        rc = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the destination host"));
            goto error;
        }
    }

    if (flags & VIR_MIGRATE_PEER2PEER) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_P2P);
        if (rc < 0)
            goto error;
        if (rc) {
            g_autofree char *dstURI = NULL;
            if (uri == NULL) {
                dstURI = virConnectGetURI(dconn);
                if (!dstURI)
                    return NULL;
            }

            VIR_DEBUG("Using peer2peer migration");
            if (virDomainMigrateUnmanaged(domain, NULL, flags, dname,
                                          uri ? uri : dstURI, NULL, bandwidth) < 0)
                goto error;

            ddomain = virDomainLookupByName(dconn, dname ? dname : domain->name);
        } else {
            /* This driver does not support peer to peer migration */
            virReportUnsupportedError();
            goto error;
        }
    } else {
        /* Change protection requires support only on source side, and
         * is only needed in v3 migration, which automatically re-adds
         * the flag for just the source side.  We mask it out for
         * non-peer2peer to allow migration from newer source to an
         * older destination that rejects the flag.  */
        if (flags & VIR_MIGRATE_CHANGE_PROTECTION) {
            rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION);
            if (rc <= 0) {
                if (rc == 0)
                    virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                                   _("cannot enforce change protection"));
                goto error;
            }
        }
        flags &= ~VIR_MIGRATE_CHANGE_PROTECTION;
        if (flags & VIR_MIGRATE_TUNNELLED) {
            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                           _("cannot perform tunnelled migration without using peer2peer flag"));
            goto error;
        }

        /* Check that migration is supported by both drivers. */
        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V3);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V3);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 3");
            ddomain = virDomainMigrateVersion3(domain, dconn, NULL,
                                               flags, dname, uri, bandwidth);
            goto done;
        }

        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V2);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V2);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 2");
            ddomain = virDomainMigrateVersion2(domain, dconn, flags,
                                               dname, uri, bandwidth);
            goto done;
        }

        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V1);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V1);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 1");
            ddomain = virDomainMigrateVersion1(domain, dconn, flags,
                                               dname, uri, bandwidth);
            goto done;
        }

        /* This driver does not support any migration method */
        virReportUnsupportedError();
        goto error;
    }

 done:
    if (ddomain == NULL)
        goto error;

    return ddomain;

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainMigrate2:
 * @domain: a domain object
 * @dconn: destination host (a connection object)
 * @flags: bitwise-OR of virDomainMigrateFlags
 * @dxml: (optional) XML config for launching guest on target
 * @dname: (optional) rename domain to this at destination
 * @uri: (optional) dest hostname/URI as seen from the source host
 * @bandwidth: (optional) specify migration bandwidth limit in MiB/s
 *
 * Migrate the domain object from its current host to the destination
 * host given by dconn (a connection to the destination host).
 *
 * This function is similar to virDomainMigrate3, but it only supports a fixed
 * set of parameters: @dxml corresponds to VIR_MIGRATE_PARAM_DEST_XML, @dname
 * is VIR_MIGRATE_PARAM_DEST_NAME, @uri is VIR_MIGRATE_PARAM_URI, and
 * @bandwidth is VIR_MIGRATE_PARAM_BANDWIDTH.
 *
 * virDomainFree should be used to free the resources after the
 * returned domain object is no longer needed.
 *
 * Returns the new domain object if the migration was successful,
 *   or NULL in case of error.  Note that the new domain object
 *   exists in the scope of the destination connection (dconn).
 */
virDomainPtr
virDomainMigrate2(virDomainPtr domain,
                  virConnectPtr dconn,
                  const char *dxml,
                  unsigned long flags,
                  const char *dname,
                  const char *uri,
                  unsigned long bandwidth)
{
    virDomainPtr ddomain = NULL;
    int rc_src;
    int rc_dst;
    int rc;

    VIR_DOMAIN_DEBUG(domain,
                     "dconn=%p, flags=0x%lx, dname=%s, uri=%s, bandwidth=%lu",
                     dconn, flags, NULLSTR(dname), NULLSTR(uri), bandwidth);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, NULL);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    /* Now checkout the destination */
    virCheckConnectGoto(dconn, error);
    virCheckReadOnlyGoto(dconn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_NON_SHARED_DISK,
                             VIR_MIGRATE_NON_SHARED_INC,
                             error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_TUNNELLED,
                             VIR_MIGRATE_PARALLEL,
                             error);

    if (flags & VIR_MIGRATE_OFFLINE) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the source host"));
            goto error;
        }
        rc = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the destination host"));
            goto error;
        }
    }

    if (flags & VIR_MIGRATE_PEER2PEER) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_P2P);
        if (rc < 0)
            goto error;
        if (rc) {
            g_autofree char *dstURI = virConnectGetURI(dconn);
            if (!dstURI)
                return NULL;

            VIR_DEBUG("Using peer2peer migration");
            if (virDomainMigrateUnmanaged(domain, dxml, flags, dname,
                                          dstURI, uri, bandwidth) < 0)
                goto error;

            ddomain = virDomainLookupByName(dconn, dname ? dname : domain->name);
        } else {
            /* This driver does not support peer to peer migration */
            virReportUnsupportedError();
            goto error;
        }
    } else {
        /* Change protection requires support only on source side, and
         * is only needed in v3 migration, which automatically re-adds
         * the flag for just the source side.  We mask it out for
         * non-peer2peer to allow migration from newer source to an
         * older destination that rejects the flag.  */
        if (flags & VIR_MIGRATE_CHANGE_PROTECTION) {
            rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION);
            if (rc <= 0) {
                if (rc == 0)
                    virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                                   _("cannot enforce change protection"));
                goto error;
            }
        }
        flags &= ~VIR_MIGRATE_CHANGE_PROTECTION;
        if (flags & VIR_MIGRATE_TUNNELLED) {
            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                           _("cannot perform tunnelled migration without using peer2peer flag"));
            goto error;
        }

        /* Check that migration is supported by both drivers. */
        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V3);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V3);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 3");
            ddomain = virDomainMigrateVersion3(domain, dconn, dxml,
                                               flags, dname, uri, bandwidth);
            goto done;
        }

        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V2);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V2);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 2");
            if (dxml) {
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("Unable to change target guest XML during migration"));
                goto error;
            }
            ddomain = virDomainMigrateVersion2(domain, dconn, flags,
                                               dname, uri, bandwidth);
            goto done;
        }

        rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                          VIR_DRV_FEATURE_MIGRATION_V1);
        if (rc_src < 0)
            goto error;
        rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                          VIR_DRV_FEATURE_MIGRATION_V1);
        if (rc_dst < 0)
            goto error;
        if (rc_src && rc_dst) {
            VIR_DEBUG("Using migration protocol 1");
            if (dxml) {
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("Unable to change target guest XML during migration"));
                goto error;
            }
            ddomain = virDomainMigrateVersion1(domain, dconn, flags,
                                               dname, uri, bandwidth);
            goto done;
        }

        /* This driver does not support any migration method */
        virReportUnsupportedError();
        goto error;
    }

 done:
    if (ddomain == NULL)
        goto error;

    return ddomain;

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainMigrate3:
 * @domain: a domain object
 * @dconn: destination host (a connection object)
 * @params: (optional) migration parameters
 * @nparams: (optional) number of migration parameters in @params
 * @flags: bitwise-OR of virDomainMigrateFlags
 *
 * Migrate the domain object from its current host to the destination host
 * given by dconn (a connection to the destination host).
 *
 * See VIR_MIGRATE_PARAM_* and virDomainMigrateFlags for detailed description
 * of accepted migration parameters and flags.
 *
 * See virDomainMigrateFlags documentation for description of individual flags.
 *
 * VIR_MIGRATE_TUNNELLED and VIR_MIGRATE_PEER2PEER are not supported by this
 * API, use virDomainMigrateToURI3 instead.
 *
 * There are many limitations on migration imposed by the underlying
 * technology - for example it may not be possible to migrate between
 * different processors even with the same architecture, or between
 * different types of hypervisor.
 *
 * virDomainFree should be used to free the resources after the
 * returned domain object is no longer needed.
 *
 * Returns the new domain object if the migration was successful,
 *   or NULL in case of error.  Note that the new domain object
 *   exists in the scope of the destination connection (dconn).
 */
virDomainPtr
virDomainMigrate3(virDomainPtr domain,
                  virConnectPtr dconn,
                  virTypedParameterPtr params,
                  unsigned int nparams,
                  unsigned int flags)
{
    virDomainPtr ddomain = NULL;
    const char *compatParams[] = { VIR_MIGRATE_PARAM_URI,
                                   VIR_MIGRATE_PARAM_DEST_NAME,
                                   VIR_MIGRATE_PARAM_DEST_XML,
                                   VIR_MIGRATE_PARAM_BANDWIDTH };
    const char *uri = NULL;
    const char *dname = NULL;
    const char *dxml = NULL;
    unsigned long long bandwidth = 0;
    int rc_src;
    int rc_dst;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "dconn=%p, params=%p, nparms=%u flags=0x%x",
                     dconn, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, NULL);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    /* Now checkout the destination */
    virCheckReadOnlyGoto(dconn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_NON_SHARED_DISK,
                             VIR_MIGRATE_NON_SHARED_INC,
                             error);

    if (flags & VIR_MIGRATE_PEER2PEER) {
        virReportInvalidArg(flags, "%s",
                            _("use virDomainMigrateToURI3 for peer-to-peer "
                              "migration"));
        goto error;
    }
    if (flags & VIR_MIGRATE_TUNNELLED) {
        virReportInvalidArg(flags, "%s",
                            _("cannot perform tunnelled migration "
                              "without using peer2peer flag"));
        goto error;
    }

    if (flags & VIR_MIGRATE_OFFLINE) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the source host"));
            goto error;
        }

        rc = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the destination host"));
            goto error;
        }
    }

    /* Change protection requires support only on source side, and
     * is only needed in v3 migration, which automatically re-adds
     * the flag for just the source side.  We mask it out to allow
     * migration from newer source to an older destination that
     * rejects the flag.  */
    if (flags & VIR_MIGRATE_CHANGE_PROTECTION) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATE_CHANGE_PROTECTION);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("cannot enforce change protection"));
            goto error;
        }
    }
    flags &= ~VIR_MIGRATE_CHANGE_PROTECTION;

    /* Prefer extensible API but fall back to older migration APIs if params
     * only contains parameters which were supported by the older API. */
    rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_PARAMS);
    if (rc_src < 0)
        goto error;
    rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_PARAMS);
    if (rc_dst < 0)
        goto error;
    if (rc_src && rc_dst) {
        VIR_DEBUG("Using migration protocol 3 with extensible parameters");
        ddomain = virDomainMigrateVersion3Params(domain, dconn, params,
                                                 nparams, flags);
        goto done;
    }

    if (!virTypedParamsCheck(params, nparams, compatParams,
                             G_N_ELEMENTS(compatParams))) {
        virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                       _("Migration APIs with extensible parameters are not "
                         "supported but extended parameters were passed"));
        goto error;
    }

    if (virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_URI, &uri) < 0 ||
        virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_DEST_NAME, &dname) < 0 ||
        virTypedParamsGetString(params, nparams,
                                VIR_MIGRATE_PARAM_DEST_XML, &dxml) < 0 ||
        virTypedParamsGetULLong(params, nparams,
                                VIR_MIGRATE_PARAM_BANDWIDTH, &bandwidth) < 0) {
        goto error;
    }

    rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_V3);
    if (rc_src < 0)
        goto error;
    rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_V3);
    if (rc_dst < 0)
        goto error;
    if (rc_src && rc_dst) {
        VIR_DEBUG("Using migration protocol 3");
        ddomain = virDomainMigrateVersion3(domain, dconn, dxml, flags,
                                           dname, uri, bandwidth);
        goto done;
    }

    rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_V2);
    if (rc_src < 0)
        goto error;
    rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_V2);
    if (rc_dst < 0)
        goto error;
    if (rc_src && rc_dst) {
        VIR_DEBUG("Using migration protocol 2");
        if (dxml) {
            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                           _("Unable to change target guest XML during "
                             "migration"));
            goto error;
        }
        ddomain = virDomainMigrateVersion2(domain, dconn, flags,
                                           dname, uri, bandwidth);
        goto done;
    }

    rc_src = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_V1);
    if (rc_src < 0)
        goto error;
    rc_dst = VIR_DRV_SUPPORTS_FEATURE(dconn->driver, dconn,
                                      VIR_DRV_FEATURE_MIGRATION_V1);
    if (rc_dst < 0)
        goto error;
    if (rc_src && rc_dst) {
        VIR_DEBUG("Using migration protocol 1");
        if (dxml) {
            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                           _("Unable to change target guest XML during "
                             "migration"));
            goto error;
        }
        ddomain = virDomainMigrateVersion1(domain, dconn, flags,
                                           dname, uri, bandwidth);
        goto done;
    }

    /* This driver does not support any migration method */
    virReportUnsupportedError();
    goto error;

 done:
    if (ddomain == NULL)
        goto error;

    return ddomain;

 error:
    virDispatchError(domain->conn);
    return NULL;
}


static
int virDomainMigrateUnmanagedCheckCompat(virDomainPtr domain,
                                         unsigned int flags)
{
    int rc;

    VIR_EXCLUSIVE_FLAGS_RET(VIR_MIGRATE_NON_SHARED_DISK,
                            VIR_MIGRATE_NON_SHARED_INC,
                            -1);

    if (flags & VIR_MIGRATE_OFFLINE) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_OFFLINE);

        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("offline migration is not supported by "
                                 "the source host"));
            return -1;
        }
    }

    if (flags & VIR_MIGRATE_PEER2PEER) {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_P2P);

        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("p2p migration is not supported by "
                                 "the source host"));
            return -1;
        }
    } else {
        rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                      VIR_DRV_FEATURE_MIGRATION_DIRECT);
        if (rc <= 0) {
            if (rc == 0)
                virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                               _("direct migration is not supported by "
                                 "the source host"));
            return -1;
        }
    }

    return 0;
}


/**
 * virDomainMigrateToURI:
 * @domain: a domain object
 * @duri: mandatory URI for the destination host
 * @flags: bitwise-OR of virDomainMigrateFlags
 * @dname: (optional) rename domain to this at destination
 * @bandwidth: (optional) specify migration bandwidth limit in MiB/s
 *
 * Migrate the domain object from its current host to the destination
 * host given by duri.
 *
 * This function is similar to virDomainMigrateToURI3, but it only supports a
 * fixed set of parameters: @dname corresponds to VIR_MIGRATE_PARAM_DEST_NAME,
 * and @bandwidth corresponds to VIR_MIGRATE_PARAM_BANDWIDTH.
 *
 * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag.
 *
 * If the VIR_MIGRATE_PEER2PEER flag IS set, the @duri parameter must be a
 * valid libvirt connection URI, by which the source libvirt driver can connect
 * to the destination libvirt. In other words, @duri corresponds to @dconnuri
 * of virDomainMigrateToURI3.
 *
 * If the VIR_MIGRATE_PEER2PEER flag is NOT set, the @duri parameter takes a
 * hypervisor specific URI used to initiate the migration. In this case @duri
 * corresponds to VIR_MIGRATE_PARAM_URI of virDomainMigrateToURI3.
 *
 * Returns 0 if the migration succeeded, -1 upon error.
 */
int
virDomainMigrateToURI(virDomainPtr domain,
                      const char *duri,
                      unsigned long flags,
                      const char *dname,
                      unsigned long bandwidth)
{
    const char *dconnuri = NULL;
    const char *miguri = NULL;

    VIR_DOMAIN_DEBUG(domain, "duri=%p, flags=0x%lx, dname=%s, bandwidth=%lu",
                     NULLSTR(duri), flags, NULLSTR(dname), bandwidth);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);
    virCheckNonNullArgGoto(duri, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_TUNNELLED,
                             VIR_MIGRATE_PARALLEL,
                             error);

    if (virDomainMigrateUnmanagedCheckCompat(domain, flags) < 0)
        goto error;

    if (flags & VIR_MIGRATE_PEER2PEER)
        dconnuri = duri;
    else
        miguri = duri;

    if (virDomainMigrateUnmanaged(domain, NULL, flags,
                                  dname, dconnuri, miguri, bandwidth) < 0)
        goto error;

    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainMigrateToURI2:
 * @domain: a domain object
 * @dconnuri: (optional) URI for target libvirtd if @flags includes VIR_MIGRATE_PEER2PEER
 * @miguri: (optional) URI for invoking the migration, not if @flags includs VIR_MIGRATE_TUNNELLED
 * @dxml: (optional) XML config for launching guest on target
 * @flags: bitwise-OR of virDomainMigrateFlags
 * @dname: (optional) rename domain to this at destination
 * @bandwidth: (optional) specify migration bandwidth limit in MiB/s
 *
 * Migrate the domain object from its current host to the destination
 * host given by @dconnuri.
 *
 * This function is similar to virDomainMigrateToURI3, but it only supports a
 * fixed set of parameters: @miguri corresponds to VIR_MIGRATE_PARAM_URI, @dxml
 * is VIR_MIGRATE_PARAM_DEST_XML, @dname is VIR_MIGRATE_PARAM_DEST_NAME, and
 * @bandwidth corresponds to VIR_MIGRATE_PARAM_BANDWIDTH.
 *
 * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag.
 *
 * If the VIR_MIGRATE_PEER2PEER flag IS set, the @dconnuri parameter must be a
 * valid libvirt connection URI, by which the source libvirt driver can connect
 * to the destination libvirt. In other words, @dconnuri has the same semantics
 * as in virDomainMigrateToURI3.
 *
 * If the VIR_MIGRATE_PEER2PEER flag is NOT set, the @dconnuri must be NULL
 * and the @miguri parameter takes a hypervisor specific URI used to initiate
 * the migration. In this case @miguri corresponds to VIR_MIGRATE_PARAM_URI of
 * virDomainMigrateToURI3.
 *
 * Returns 0 if the migration succeeded, -1 upon error.
 */
int
virDomainMigrateToURI2(virDomainPtr domain,
                       const char *dconnuri,
                       const char *miguri,
                       const char *dxml,
                       unsigned long flags,
                       const char *dname,
                       unsigned long bandwidth)
{
    VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, miguri=%s, dxml=%s, "
                     "flags=0x%lx, dname=%s, bandwidth=%lu",
                     NULLSTR(dconnuri), NULLSTR(miguri), NULLSTR(dxml),
                     flags, NULLSTR(dname), bandwidth);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_TUNNELLED,
                             VIR_MIGRATE_PARALLEL,
                             error);

    if (virDomainMigrateUnmanagedCheckCompat(domain, flags) < 0)
        goto error;

    if (flags & VIR_MIGRATE_PEER2PEER)
        virCheckNonNullArgGoto(dconnuri, error);
    else
        dconnuri = NULL;

    if (virDomainMigrateUnmanaged(domain, dxml, flags,
                                  dname, dconnuri, miguri, bandwidth) < 0)
        goto error;

    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainMigrateToURI3:
 * @domain: a domain object
 * @dconnuri: (optional) URI for target libvirtd if @flags includes VIR_MIGRATE_PEER2PEER
 * @params: (optional) migration parameters
 * @nparams: (optional) number of migration parameters in @params
 * @flags: bitwise-OR of virDomainMigrateFlags
 *
 * Migrate the domain object from its current host to the destination host
 * given by URI.
 *
 * See VIR_MIGRATE_PARAM_* and virDomainMigrateFlags for detailed description
 * of accepted migration parameters and flags.
 *
 * The operation of this API hinges on the VIR_MIGRATE_PEER2PEER flag.
 *
 * If the VIR_MIGRATE_PEER2PEER flag is set, the @dconnuri parameter must be a
 * valid libvirt connection URI, by which the source libvirt daemon can connect
 * to the destination libvirt.
 *
 * If the VIR_MIGRATE_PEER2PEER flag is NOT set, then @dconnuri must be NULL
 * and VIR_MIGRATE_PARAM_URI migration parameter must be filled in with
 * hypervisor specific URI used to initiate the migration. The uri_transports
 * element of the hypervisor capabilities XML includes supported URI schemes.
 * This is called "direct" migration. Not all hypervisors support this mode of
 * migration, so if the VIR_MIGRATE_PEER2PEER flag is not set, then it may be
 * necessary to use the alternative virDomainMigrate3 API providing an explicit
 * virConnectPtr for the destination host.
 *
 * There are many limitations on migration imposed by the underlying
 * technology - for example it may not be possible to migrate between
 * different processors even with the same architecture, or between
 * different types of hypervisor.
 *
 * Returns 0 if the migration succeeded, -1 upon error.
 */
int
virDomainMigrateToURI3(virDomainPtr domain,
                       const char *dconnuri,
                       virTypedParameterPtr params,
                       unsigned int nparams,
                       unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, params=%p, nparms=%u flags=0x%x",
                     NULLSTR(dconnuri), params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    /* First checkout the source */
    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MIGRATE_TUNNELLED,
                             VIR_MIGRATE_PARALLEL,
                             error);

    if (virDomainMigrateUnmanagedCheckCompat(domain, flags) < 0)
        goto error;

    if (flags & VIR_MIGRATE_PEER2PEER)
        virCheckNonNullArgGoto(dconnuri, error);
    else
        dconnuri = NULL;

    if (virDomainMigrateUnmanagedParams(domain, dconnuri,
                                        params, nparams, flags) < 0)
        goto error;

    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepare(virConnectPtr dconn,
                        char **cookie,
                        int *cookielen,
                        const char *uri_in,
                        char **uri_out,
                        unsigned long flags,
                        const char *dname,
                        unsigned long bandwidth)
{
    VIR_DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p, "
              "flags=0x%lx, dname=%s, bandwidth=%lu", dconn, cookie, cookielen,
              NULLSTR(uri_in), uri_out, flags, NULLSTR(dname), bandwidth);

    virResetLastError();

    virCheckConnectReturn(dconn, -1);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigratePrepare) {
        int ret;
        ret = dconn->driver->domainMigratePrepare(dconn, cookie, cookielen,
                                                  uri_in, uri_out,
                                                  flags, dname, bandwidth);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePerform(virDomainPtr domain,
                        const char *cookie,
                        int cookielen,
                        const char *uri,
                        unsigned long flags,
                        const char *dname,
                        unsigned long bandwidth)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cookie=%p, cookielen=%d, uri=%s, flags=0x%lx, "
                     "dname=%s, bandwidth=%lu", cookie, cookielen, uri, flags,
                     NULLSTR(dname), bandwidth);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigratePerform) {
        int ret;
        ret = conn->driver->domainMigratePerform(domain, cookie, cookielen,
                                                 uri,
                                                 flags, dname, bandwidth);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
virDomainPtr
virDomainMigrateFinish(virConnectPtr dconn,
                       const char *dname,
                       const char *cookie,
                       int cookielen,
                       const char *uri,
                       unsigned long flags)
{
    VIR_DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, "
              "flags=0x%lx", dconn, NULLSTR(dname), cookie, cookielen,
              NULLSTR(uri), flags);

    virResetLastError();

    virCheckConnectReturn(dconn, NULL);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigrateFinish) {
        virDomainPtr ret;
        ret = dconn->driver->domainMigrateFinish(dconn, dname,
                                                 cookie, cookielen,
                                                 uri, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepare2(virConnectPtr dconn,
                         char **cookie,
                         int *cookielen,
                         const char *uri_in,
                         char **uri_out,
                         unsigned long flags,
                         const char *dname,
                         unsigned long bandwidth,
                         const char *dom_xml)
{
    VIR_DEBUG("dconn=%p, cookie=%p, cookielen=%p, uri_in=%s, uri_out=%p,"
              "flags=0x%lx, dname=%s, bandwidth=%lu, dom_xml=%s", dconn,
              cookie, cookielen, NULLSTR(uri_in), uri_out, flags, NULLSTR(dname),
              bandwidth, NULLSTR(dom_xml));

    virResetLastError();

    virCheckConnectReturn(dconn, -1);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigratePrepare2) {
        int ret;
        ret = dconn->driver->domainMigratePrepare2(dconn, cookie, cookielen,
                                                   uri_in, uri_out,
                                                   flags, dname, bandwidth,
                                                   dom_xml);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
virDomainPtr
virDomainMigrateFinish2(virConnectPtr dconn,
                        const char *dname,
                        const char *cookie,
                        int cookielen,
                        const char *uri,
                        unsigned long flags,
                        int retcode)
{
    VIR_DEBUG("dconn=%p, dname=%s, cookie=%p, cookielen=%d, uri=%s, "
              "flags=0x%lx, retcode=%d", dconn, NULLSTR(dname), cookie,
              cookielen, NULLSTR(uri), flags, retcode);

    virResetLastError();

    virCheckConnectReturn(dconn, NULL);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigrateFinish2) {
        virDomainPtr ret;
        ret = dconn->driver->domainMigrateFinish2(dconn, dname,
                                                  cookie, cookielen,
                                                  uri, flags,
                                                  retcode);
        if (!ret && !retcode)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepareTunnel(virConnectPtr conn,
                              virStreamPtr st,
                              unsigned long flags,
                              const char *dname,
                              unsigned long bandwidth,
                              const char *dom_xml)
{
    VIR_DEBUG("conn=%p, stream=%p, flags=0x%lx, dname=%s, "
              "bandwidth=%lu, dom_xml=%s", conn, st, flags,
              NULLSTR(dname), bandwidth, NULLSTR(dom_xml));

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn != st->conn) {
        virReportInvalidArg(conn, "%s",
                            _("conn must match stream connection"));
        goto error;
    }

    if (conn->driver->domainMigratePrepareTunnel) {
        int rv = conn->driver->domainMigratePrepareTunnel(conn, st,
                                                          flags, dname,
                                                          bandwidth, dom_xml);
        if (rv < 0)
            goto error;
        return rv;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
char *
virDomainMigrateBegin3(virDomainPtr domain,
                       const char *xmlin,
                       char **cookieout,
                       int *cookieoutlen,
                       unsigned long flags,
                       const char *dname,
                       unsigned long bandwidth)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xmlin=%s cookieout=%p, cookieoutlen=%p, "
                     "flags=0x%lx, dname=%s, bandwidth=%lu",
                     NULLSTR(xmlin), cookieout, cookieoutlen, flags,
                     NULLSTR(dname), bandwidth);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateBegin3) {
        char *xml;
        xml = conn->driver->domainMigrateBegin3(domain, xmlin,
                                                cookieout, cookieoutlen,
                                                flags, dname, bandwidth);
        VIR_DEBUG("xml %s", NULLSTR(xml));
        if (!xml)
            goto error;
        return xml;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepare3(virConnectPtr dconn,
                         const char *cookiein,
                         int cookieinlen,
                         char **cookieout,
                         int *cookieoutlen,
                         const char *uri_in,
                         char **uri_out,
                         unsigned long flags,
                         const char *dname,
                         unsigned long bandwidth,
                         const char *dom_xml)
{
    VIR_DEBUG("dconn=%p, cookiein=%p, cookieinlen=%d, cookieout=%p, "
              "cookieoutlen=%p, uri_in=%s, uri_out=%p, flags=0x%lx, dname=%s, "
              "bandwidth=%lu, dom_xml=%s",
              dconn, cookiein, cookieinlen, cookieout, cookieoutlen, NULLSTR(uri_in),
              uri_out, flags, NULLSTR(dname), bandwidth, NULLSTR(dom_xml));

    virResetLastError();

    virCheckConnectReturn(dconn, -1);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigratePrepare3) {
        int ret;
        ret = dconn->driver->domainMigratePrepare3(dconn,
                                                   cookiein, cookieinlen,
                                                   cookieout, cookieoutlen,
                                                   uri_in, uri_out,
                                                   flags, dname, bandwidth,
                                                   dom_xml);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepareTunnel3(virConnectPtr conn,
                               virStreamPtr st,
                               const char *cookiein,
                               int cookieinlen,
                               char **cookieout,
                               int *cookieoutlen,
                               unsigned long flags,
                               const char *dname,
                               unsigned long bandwidth,
                               const char *dom_xml)
{
    VIR_DEBUG("conn=%p, stream=%p, cookiein=%p, cookieinlen=%d, cookieout=%p, "
              "cookieoutlen=%p, flags=0x%lx, dname=%s, bandwidth=%lu, "
              "dom_xml=%s",
              conn, st, cookiein, cookieinlen, cookieout, cookieoutlen, flags,
              NULLSTR(dname), bandwidth, NULLSTR(dom_xml));

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn != st->conn) {
        virReportInvalidArg(conn, "%s",
                            _("conn must match stream connection"));
        goto error;
    }

    if (conn->driver->domainMigratePrepareTunnel3) {
        int rv = conn->driver->domainMigratePrepareTunnel3(conn, st,
                                                           cookiein, cookieinlen,
                                                           cookieout, cookieoutlen,
                                                           flags, dname,
                                                           bandwidth, dom_xml);
        if (rv < 0)
            goto error;
        return rv;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePerform3(virDomainPtr domain,
                         const char *xmlin,
                         const char *cookiein,
                         int cookieinlen,
                         char **cookieout,
                         int *cookieoutlen,
                         const char *dconnuri,
                         const char *uri,
                         unsigned long flags,
                         const char *dname,
                         unsigned long bandwidth)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xmlin=%s cookiein=%p, cookieinlen=%d, "
                     "cookieout=%p, cookieoutlen=%p, dconnuri=%s, "
                     "uri=%s, flags=0x%lx, dname=%s, bandwidth=%lu",
                     NULLSTR(xmlin), cookiein, cookieinlen,
                     cookieout, cookieoutlen, NULLSTR(dconnuri),
                     NULLSTR(uri), flags, NULLSTR(dname), bandwidth);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigratePerform3) {
        int ret;
        ret = conn->driver->domainMigratePerform3(domain, xmlin,
                                                  cookiein, cookieinlen,
                                                  cookieout, cookieoutlen,
                                                  dconnuri, uri,
                                                  flags, dname, bandwidth);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
virDomainPtr
virDomainMigrateFinish3(virConnectPtr dconn,
                        const char *dname,
                        const char *cookiein,
                        int cookieinlen,
                        char **cookieout,
                        int *cookieoutlen,
                        const char *dconnuri,
                        const char *uri,
                        unsigned long flags,
                        int cancelled)
{
    VIR_DEBUG("dconn=%p, dname=%s, cookiein=%p, cookieinlen=%d, cookieout=%p,"
              "cookieoutlen=%p, dconnuri=%s, uri=%s, flags=0x%lx, retcode=%d",
              dconn, NULLSTR(dname), cookiein, cookieinlen, cookieout,
              cookieoutlen, NULLSTR(dconnuri), NULLSTR(uri), flags, cancelled);

    virResetLastError();

    virCheckConnectReturn(dconn, NULL);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigrateFinish3) {
        virDomainPtr ret;
        ret = dconn->driver->domainMigrateFinish3(dconn, dname,
                                                  cookiein, cookieinlen,
                                                  cookieout, cookieoutlen,
                                                  dconnuri, uri, flags,
                                                  cancelled);
        if (!ret && !cancelled)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigrateConfirm3(virDomainPtr domain,
                         const char *cookiein,
                         int cookieinlen,
                         unsigned long flags,
                         int cancelled)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain,
                     "cookiein=%p, cookieinlen=%d, flags=0x%lx, cancelled=%d",
                     cookiein, cookieinlen, flags, cancelled);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateConfirm3) {
        int ret;
        ret = conn->driver->domainMigrateConfirm3(domain,
                                                  cookiein, cookieinlen,
                                                  flags, cancelled);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
char *
virDomainMigrateBegin3Params(virDomainPtr domain,
                             virTypedParameterPtr params,
                             int nparams,
                             char **cookieout,
                             int *cookieoutlen,
                             unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, "
                     "cookieout=%p, cookieoutlen=%p, flags=0x%x",
                     params, nparams, cookieout, cookieoutlen, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateBegin3Params) {
        char *xml;
        xml = conn->driver->domainMigrateBegin3Params(domain, params, nparams,
                                                      cookieout, cookieoutlen,
                                                      flags);
        VIR_DEBUG("xml %s", NULLSTR(xml));
        if (!xml)
            goto error;
        return xml;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepare3Params(virConnectPtr dconn,
                               virTypedParameterPtr params,
                               int nparams,
                               const char *cookiein,
                               int cookieinlen,
                               char **cookieout,
                               int *cookieoutlen,
                               char **uri_out,
                               unsigned int flags)
{
    VIR_DEBUG("dconn=%p, params=%p, nparams=%d, cookiein=%p, cookieinlen=%d, "
              "cookieout=%p, cookieoutlen=%p, uri_out=%p, flags=0x%x",
              dconn, params, nparams, cookiein, cookieinlen,
              cookieout, cookieoutlen, uri_out, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckConnectReturn(dconn, -1);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigratePrepare3Params) {
        int ret;
        ret = dconn->driver->domainMigratePrepare3Params(dconn, params, nparams,
                                                         cookiein, cookieinlen,
                                                         cookieout, cookieoutlen,
                                                         uri_out, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePrepareTunnel3Params(virConnectPtr conn,
                                     virStreamPtr st,
                                     virTypedParameterPtr params,
                                     int nparams,
                                     const char *cookiein,
                                     int cookieinlen,
                                     char **cookieout,
                                     int *cookieoutlen,
                                     unsigned int flags)
{
    VIR_DEBUG("conn=%p, stream=%p, params=%p, nparams=%d, cookiein=%p, "
              "cookieinlen=%d, cookieout=%p, cookieoutlen=%p, flags=0x%x",
              conn, st, params, nparams, cookiein, cookieinlen,
              cookieout, cookieoutlen, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn != st->conn) {
        virReportInvalidArg(conn, "%s",
                            _("conn must match stream connection"));
        goto error;
    }

    if (conn->driver->domainMigratePrepareTunnel3Params) {
        int rv;
        rv = conn->driver->domainMigratePrepareTunnel3Params(
                conn, st, params, nparams, cookiein, cookieinlen,
                cookieout, cookieoutlen, flags);
        if (rv < 0)
            goto error;
        return rv;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigratePerform3Params(virDomainPtr domain,
                               const char *dconnuri,
                               virTypedParameterPtr params,
                               int nparams,
                               const char *cookiein,
                               int cookieinlen,
                               char **cookieout,
                               int *cookieoutlen,
                               unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "dconnuri=%s, params=%p, nparams=%d, cookiein=%p, "
                     "cookieinlen=%d, cookieout=%p, cookieoutlen=%p, flags=0x%x",
                     NULLSTR(dconnuri), params, nparams, cookiein,
                     cookieinlen, cookieout, cookieoutlen, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigratePerform3Params) {
        int ret;
        ret = conn->driver->domainMigratePerform3Params(
                domain, dconnuri, params, nparams, cookiein, cookieinlen,
                cookieout, cookieoutlen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
virDomainPtr
virDomainMigrateFinish3Params(virConnectPtr dconn,
                              virTypedParameterPtr params,
                              int nparams,
                              const char *cookiein,
                              int cookieinlen,
                              char **cookieout,
                              int *cookieoutlen,
                              unsigned int flags,
                              int cancelled)
{
    VIR_DEBUG("dconn=%p, params=%p, nparams=%d, cookiein=%p, cookieinlen=%d, "
              "cookieout=%p, cookieoutlen=%p, flags=0x%x, cancelled=%d",
              dconn, params, nparams, cookiein, cookieinlen, cookieout,
              cookieoutlen, flags, cancelled);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckConnectReturn(dconn, NULL);
    virCheckReadOnlyGoto(dconn->flags, error);

    if (dconn->driver->domainMigrateFinish3Params) {
        virDomainPtr ret;
        ret = dconn->driver->domainMigrateFinish3Params(
                dconn, params, nparams, cookiein, cookieinlen,
                cookieout, cookieoutlen, flags, cancelled);
        if (!ret && !cancelled)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dconn);
    return NULL;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of migration in the remote case.
 */
int
virDomainMigrateConfirm3Params(virDomainPtr domain,
                               virTypedParameterPtr params,
                               int nparams,
                               const char *cookiein,
                               int cookieinlen,
                               unsigned int flags,
                               int cancelled)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, cookiein=%p, "
                     "cookieinlen=%d, flags=0x%x, cancelled=%d",
                     params, nparams, cookiein, cookieinlen, flags, cancelled);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateConfirm3Params) {
        int ret;
        ret = conn->driver->domainMigrateConfirm3Params(
                domain, params, nparams,
                cookiein, cookieinlen, flags, cancelled);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetSchedulerType:
 * @domain: pointer to domain object
 * @nparams: pointer to number of scheduler parameters, can be NULL
 *           (return value)
 *
 * Get the scheduler type and the number of scheduler parameters.
 *
 * Returns NULL in case of error. The caller must free the returned string.
 */
char *
virDomainGetSchedulerType(virDomainPtr domain, int *nparams)
{
    virConnectPtr conn;
    char *schedtype;

    VIR_DOMAIN_DEBUG(domain, "nparams=%p", nparams);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    if (conn->driver->domainGetSchedulerType) {
        schedtype = conn->driver->domainGetSchedulerType(domain, nparams);
        if (!schedtype)
            goto error;
        return schedtype;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainGetSchedulerParameters:
 * @domain: pointer to domain object
 * @params: pointer to scheduler parameter objects
 *          (return value)
 * @nparams: pointer to number of scheduler parameter objects
 *          (this value should generally be as large as the returned value
 *           nparams of virDomainGetSchedulerType()); input and output
 *
 * Get all scheduler parameters.  On input, @nparams gives the size of the
 * @params array; on output, @nparams gives how many slots were filled
 * with parameter information, which might be less but will not exceed
 * the input value.  @nparams cannot be 0.
 *
 * It is hypervisor specific whether this returns the live or
 * persistent state; for more control, use
 * virDomainGetSchedulerParametersFlags().
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetSchedulerParameters(virDomainPtr domain,
                                virTypedParameterPtr params, int *nparams)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p", params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);
    virCheckPositiveArgGoto(*nparams, error);

    conn = domain->conn;

    if (conn->driver->domainGetSchedulerParameters) {
        int ret;
        ret = conn->driver->domainGetSchedulerParameters(domain, params, nparams);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetSchedulerParametersFlags:
 * @domain: pointer to domain object
 * @params: pointer to scheduler parameter object
 *          (return value)
 * @nparams: pointer to number of scheduler parameter
 *          (this value should be same than the returned value
 *           nparams of virDomainGetSchedulerType()); input and output
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all scheduler parameters.  On input, @nparams gives the size of the
 * @params array; on output, @nparams gives how many slots were filled
 * with parameter information, which might be less but will not exceed
 * the input value.  @nparams cannot be 0.
 *
 * The value of @flags can be exactly VIR_DOMAIN_AFFECT_CURRENT,
 * VIR_DOMAIN_AFFECT_LIVE, or VIR_DOMAIN_AFFECT_CONFIG.
 *
 * Here is a sample code snippet:
 *
 *   char *ret = virDomainGetSchedulerType(dom, &nparams);
 *   if (ret && nparams != 0) {
 *       if ((params = malloc(sizeof(*params) * nparams)) == NULL)
 *           goto error;
 *       memset(params, 0, sizeof(*params) * nparams);
 *       if (virDomainGetSchedulerParametersFlags(dom, params, &nparams, 0))
 *           goto error;
 *   }
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetSchedulerParametersFlags(virDomainPtr domain,
                                     virTypedParameterPtr params, int *nparams,
                                     unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p, flags=0x%x",
                     params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);
    virCheckPositiveArgGoto(*nparams, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    conn = domain->conn;

    if (conn->driver->domainGetSchedulerParametersFlags) {
        int ret;
        ret = conn->driver->domainGetSchedulerParametersFlags(domain, params,
                                                              nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetSchedulerParameters:
 * @domain: pointer to domain object
 * @params: pointer to scheduler parameter objects
 * @nparams: number of scheduler parameter objects
 *          (this value can be the same or less than the returned value
 *           nparams of virDomainGetSchedulerType)
 *
 * Change all or a subset or the scheduler parameters.  It is
 * hypervisor-specific whether this sets live, persistent, or both
 * settings; for more control, use
 * virDomainSetSchedulerParametersFlags.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetSchedulerParameters(virDomainPtr domain,
                                virTypedParameterPtr params, int nparams)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d", params, nparams);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNegativeArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetSchedulerParameters) {
        int ret;
        ret = conn->driver->domainSetSchedulerParameters(domain, params, nparams);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetSchedulerParametersFlags:
 * @domain: pointer to domain object
 * @params: pointer to scheduler parameter objects
 * @nparams: number of scheduler parameter objects
 *          (this value can be the same or less than the returned value
 *           nparams of virDomainGetSchedulerType)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change a subset or all scheduler parameters.  The value of @flags
 * should be either VIR_DOMAIN_AFFECT_CURRENT, or a bitwise-or of
 * values from VIR_DOMAIN_AFFECT_LIVE and
 * VIR_DOMAIN_AFFECT_CURRENT, although hypervisors vary in which
 * flags are supported.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetSchedulerParametersFlags(virDomainPtr domain,
                                     virTypedParameterPtr params,
                                     int nparams,
                                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d, flags=0x%x",
                     params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNegativeArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetSchedulerParametersFlags) {
        int ret;
        ret = conn->driver->domainSetSchedulerParametersFlags(domain,
                                                              params,
                                                              nparams,
                                                              flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainBlockStats:
 * @dom: pointer to the domain object
 * @disk: path to the block device, or device shorthand
 * @stats: block device stats (returned)
 * @size: size of stats structure
 *
 * This function returns block device (disk) stats for block
 * devices attached to the domain.
 *
 * The @disk parameter is either the device target shorthand (the
 * <target dev='...'/> sub-element, such as "vda"), or (since 0.9.8)
 * an unambiguous source name of the block device (the <source
 * file='...'/> sub-element, such as "/path/to/image").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk. Some drivers might also
 * accept the empty string for the @disk parameter, and then yield
 * summary stats for the entire domain.
 *
 * Domains may have more than one block device.  To get stats for
 * each you should make multiple calls to this function.
 *
 * Individual fields within the stats structure may be returned
 * as -1, which indicates that the hypervisor does not support
 * that particular statistic.
 *
 * Returns: 0 in case of success or -1 in case of failure.
 */
int
virDomainBlockStats(virDomainPtr dom, const char *disk,
                    virDomainBlockStatsPtr stats, size_t size)
{
    virConnectPtr conn;
    virDomainBlockStatsStruct stats2 = { -1, -1, -1, -1, -1 };

    VIR_DOMAIN_DEBUG(dom, "disk=%s, stats=%p, size=%zi", disk, stats, size);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckNonNullArgGoto(disk, error);
    virCheckNonNullArgGoto(stats, error);
    if (size > sizeof(stats2)) {
        virReportInvalidArg(size,
                            _("size must not exceed %zu"),
                            sizeof(stats2));
        goto error;
    }
    conn = dom->conn;

    if (conn->driver->domainBlockStats) {
        if (conn->driver->domainBlockStats(dom, disk, &stats2) == -1)
            goto error;

        memcpy(stats, &stats2, size);
        return 0;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockStatsFlags:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @params: pointer to block stats parameter object
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of block stats; input and output
 * @flags: bitwise-OR of virTypedParameterFlags
 *
 * This function is to get block stats parameters for block
 * devices attached to the domain.
 *
 * The @disk parameter is either the device target shorthand (the
 * <target dev='...'/> sub-element, such as "vda"), or (since 0.9.8)
 * an unambiguous source name of the block device (the <source
 * file='...'/> sub-element, such as "/path/to/image").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk. Some drivers might also
 * accept the empty string for the @disk parameter, and then yield
 * summary stats for the entire domain.
 *
 * Domains may have more than one block device.  To get stats for
 * each you should make multiple calls to this function.
 *
 * On input, @nparams gives the size of the @params array; on output,
 * @nparams gives how many slots were filled with parameter
 * information, which might be less but will not exceed the input
 * value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0 on
 * input will cause @nparams on output to contain the number of parameters
 * supported by the hypervisor. (Note that block devices of different types
 * might support different parameters, so it might be necessary to compute
 * @nparams for each block device). The caller should then allocate @params
 * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the API
 * again. See virDomainGetMemoryParameters() for more details.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainBlockStatsFlags(virDomainPtr dom,
                         const char *disk,
                         virTypedParameterPtr params,
                         int *nparams,
                         unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, params=%p, nparams=%d, flags=0x%x",
                     disk, params, nparams ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckNonNullArgGoto(disk, error);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0)
        virCheckNonNullArgGoto(params, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(dom->conn->driver, dom->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;
    conn = dom->conn;

    if (conn->driver->domainBlockStatsFlags) {
        int ret;
        ret = conn->driver->domainBlockStatsFlags(dom, disk, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainInterfaceStats:
 * @dom: pointer to the domain object
 * @device: the interface name or MAC address
 * @stats: network interface stats (returned)
 * @size: size of stats structure
 *
 * This function returns network interface stats for interfaces
 * attached to the domain.
 *
 * The @device parameter is the network interface either by name or MAC
 * address.
 *
 * Domains may have more than one network interface.  To get stats for
 * each you should make multiple calls to this function.
 *
 * Individual fields within the stats structure may be returned
 * as -1, which indicates that the hypervisor does not support
 * that particular statistic.
 *
 * The returned stats are from domain's point of view.
 *
 * Returns: 0 in case of success or -1 in case of failure.
 */
int
virDomainInterfaceStats(virDomainPtr dom, const char *device,
                        virDomainInterfaceStatsPtr stats, size_t size)
{
    virConnectPtr conn;
    virDomainInterfaceStatsStruct stats2 = { -1, -1, -1, -1,
                                             -1, -1, -1, -1 };

    VIR_DOMAIN_DEBUG(dom, "device=%s, stats=%p, size=%zi",
                     device, stats, size);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckNonNullArgGoto(device, error);
    virCheckNonNullArgGoto(stats, error);
    if (size > sizeof(stats2)) {
        virReportInvalidArg(size,
                            _("size must not exceed %zu"),
                            sizeof(stats2));
        goto error;
    }

    conn = dom->conn;

    if (conn->driver->domainInterfaceStats) {
        if (conn->driver->domainInterfaceStats(dom, device, &stats2) == -1)
            goto error;

        memcpy(stats, &stats2, size);
        return 0;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainSetInterfaceParameters:
 * @domain: pointer to domain object
 * @device: the interface name or mac address
 * @params: pointer to interface parameter objects
 * @nparams: number of interface parameter (this value can be the same or
 *          less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change a subset or all parameters of interface; currently this
 * includes bandwidth parameters.  The value of @flags should be
 * either VIR_DOMAIN_AFFECT_CURRENT, or a bitwise-or of values
 * VIR_DOMAIN_AFFECT_LIVE and VIR_DOMAIN_AFFECT_CONFIG, although
 * hypervisors vary in which flags are supported.
 *
 * This function may require privileged access to the hypervisor.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetInterfaceParameters(virDomainPtr domain,
                                const char *device,
                                virTypedParameterPtr params,
                                int nparams, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "device=%s, params=%p, nparams=%d, flags=0x%x",
                     device, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckPositiveArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetInterfaceParameters) {
        int ret;
        ret = conn->driver->domainSetInterfaceParameters(domain, device,
                                                         params, nparams,
                                                         flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetInterfaceParameters:
 * @domain: pointer to domain object
 * @device: the interface name or mac address
 * @params: pointer to interface parameter objects
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of interface parameter; input and output
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all interface parameters. On input, @nparams gives the size of
 * the @params array; on output, @nparams gives how many slots were
 * filled with parameter information, which might be less but will not
 * exceed the input value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0 on
 * input will cause @nparams on output to contain the number of parameters
 * supported by the hypervisor. The caller should then allocate @params
 * array, i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the
 * API again. See virDomainGetMemoryParameters() for an equivalent usage
 * example.
 *
 * This function may require privileged access to the hypervisor. This function
 * expects the caller to allocate the @params.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetInterfaceParameters(virDomainPtr domain,
                                const char *device,
                                virTypedParameterPtr params,
                                int *nparams, unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "device=%s, params=%p, nparams=%d, flags=0x%x",
                     device, params, (nparams) ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0)
        virCheckNonNullArgGoto(params, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    conn = domain->conn;

    if (conn->driver->domainGetInterfaceParameters) {
        int ret;
        ret = conn->driver->domainGetInterfaceParameters(domain, device,
                                                         params, nparams,
                                                         flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainMemoryStats:
 * @dom: pointer to the domain object
 * @stats: nr_stats-sized array of stat structures (returned)
 * @nr_stats: number of memory statistics requested
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This function provides memory statistics for the domain.
 *
 * Up to 'nr_stats' elements of 'stats' will be populated with memory statistics
 * from the domain.  Only statistics supported by the domain, the driver, and
 * this version of libvirt will be returned.
 *
 * Memory Statistics:
 *
 * VIR_DOMAIN_MEMORY_STAT_SWAP_IN:
 *     The total amount of data read from swap space (in kb).
 * VIR_DOMAIN_MEMORY_STAT_SWAP_OUT:
 *     The total amount of memory written out to swap space (in kb).
 * VIR_DOMAIN_MEMORY_STAT_MAJOR_FAULT:
 *     The number of page faults that required disk IO to service.
 * VIR_DOMAIN_MEMORY_STAT_MINOR_FAULT:
 *     The number of page faults serviced without disk IO.
 * VIR_DOMAIN_MEMORY_STAT_UNUSED:
 *     The amount of memory which is not being used for any purpose (in kb).
 * VIR_DOMAIN_MEMORY_STAT_AVAILABLE:
 *     The total amount of memory available to the domain's OS (in kb).
 * VIR_DOMAIN_MEMORY_STAT_USABLE:
 *     How much the balloon can be inflated without pushing the guest system
 *     to swap, corresponds to 'Available' in /proc/meminfo
 * VIR_DOMAIN_MEMORY_STAT_ACTUAL_BALLOON:
 *     Current balloon value (in kb).
 * VIR_DOMAIN_MEMORY_STAT_LAST_UPDATE
 *     Timestamp of the last statistic
 * VIR_DOMAIN_MEMORY_STAT_DISK_CACHES
 *     Memory that can be reclaimed without additional I/O, typically disk
 *     caches (in kb).
 * VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGALLOC
 *     The number of successful huge page allocations from inside the domain
 * VIR_DOMAIN_MEMORY_STAT_HUGETLB_PGFAIL
 *     The number of failed huge page allocations from inside the domain
 *
 * Returns: The number of stats provided or -1 in case of failure.
 */
int
virDomainMemoryStats(virDomainPtr dom, virDomainMemoryStatPtr stats,
                     unsigned int nr_stats, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "stats=%p, nr_stats=%u, flags=0x%x",
                     stats, nr_stats, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    if (!stats || nr_stats == 0)
        return 0;

    if (nr_stats > VIR_DOMAIN_MEMORY_STAT_NR)
        nr_stats = VIR_DOMAIN_MEMORY_STAT_NR;

    conn = dom->conn;
    if (conn->driver->domainMemoryStats) {
        int ret = conn->driver->domainMemoryStats(dom, stats, nr_stats, flags);
        if (ret == -1)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockPeek:
 * @dom: pointer to the domain object
 * @disk: path to the block device, or device shorthand
 * @offset: offset within block device
 * @size: size to read
 * @buffer: return buffer (must be at least size bytes)
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This function allows you to read the contents of a domain's
 * disk device.
 *
 * Typical uses for this are to determine if the domain has
 * written a Master Boot Record (indicating that the domain
 * has completed installation), or to try to work out the state
 * of the domain's filesystems.
 *
 * (Note that in the local case you might try to open the
 * block device or file directly, but that won't work in the
 * remote case, nor if you don't have sufficient permission.
 * Hence the need for this call).
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * 'offset' and 'size' represent an area which must lie entirely
 * within the device or file.  'size' may be 0 to test if the
 * call would succeed.
 *
 * 'buffer' is the return buffer and must be at least 'size' bytes.
 *
 * NB. The remote driver imposes a 64K byte limit on 'size'.
 * For your program to be able to work reliably over a remote
 * connection you should split large requests to <= 65536 bytes.
 * However, with 0.9.13 this RPC limit has been raised to 1M byte.
 * Starting with version 1.0.6 the RPC limit has been raised again.
 * Now large requests up to 16M byte are supported.
 *
 * Returns: 0 in case of success or -1 in case of failure.
 */
int
virDomainBlockPeek(virDomainPtr dom,
                   const char *disk,
                   unsigned long long offset,
                   size_t size,
                   void *buffer,
                   unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, offset=%lld, size=%zi, buffer=%p, flags=0x%x",
                     disk, offset, size, buffer, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonEmptyStringArgGoto(disk, error);

    /* Allow size == 0 as an access test. */
    if (size > 0)
        virCheckNonNullArgGoto(buffer, error);

    if (conn->driver->domainBlockPeek) {
        int ret;
        ret = conn->driver->domainBlockPeek(dom, disk, offset, size,
                                            buffer, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockResize:
 * @dom: pointer to the domain object
 * @disk: path to the block image, or shorthand
 * @size: new size of the block image, see below for unit
 * @flags: bitwise-OR of virDomainBlockResizeFlags
 *
 * Resize a block device of domain while the domain is running.  If
 * @flags is 0, then @size is in kibibytes (blocks of 1024 bytes);
 * since 0.9.11, if @flags includes VIR_DOMAIN_BLOCK_RESIZE_BYTES,
 * @size is in bytes instead.  @size is taken directly as the new
 * size.  Depending on the file format, the hypervisor may round up
 * to the next alignment boundary.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * Note that this call may fail if the underlying virtualization hypervisor
 * does not support it; this call requires privileged access to the
 * hypervisor.
 *
 * Returns: 0 in case of success or -1 in case of failure.
 */
int
virDomainBlockResize(virDomainPtr dom,
                     const char *disk,
                     unsigned long long size,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, size=%llu, flags=0x%x", disk, size, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (conn->driver->domainBlockResize) {
        int ret;
        ret = conn->driver->domainBlockResize(dom, disk, size, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainMemoryPeek:
 * @dom: pointer to the domain object
 * @start: start of memory to peek
 * @size: size of memory to peek
 * @buffer: return buffer (must be at least size bytes)
 * @flags: bitwise-OR of virDomainMemoryFlags
 *
 * This function allows you to read the contents of a domain's
 * memory.
 *
 * The memory which is read is controlled by the 'start', 'size'
 * and 'flags' parameters.
 *
 * If 'flags' is VIR_MEMORY_VIRTUAL then the 'start' and 'size'
 * parameters are interpreted as virtual memory addresses for
 * whichever task happens to be running on the domain at the
 * moment.  Although this sounds haphazard it is in fact what
 * you want in order to read Linux kernel state, because it
 * ensures that pointers in the kernel image can be interpreted
 * coherently.
 *
 * 'buffer' is the return buffer and must be at least 'size' bytes.
 * 'size' may be 0 to test if the call would succeed.
 *
 * NB. The remote driver imposes a 64K byte limit on 'size'.
 * For your program to be able to work reliably over a remote
 * connection you should split large requests to <= 65536 bytes.
 * However, with 0.9.13 this RPC limit has been raised to 1M byte.
 * Starting with version 1.0.6 the RPC limit has been raised again.
 * Now large requests up to 16M byte are supported.
 *
 * Returns: 0 in case of success or -1 in case of failure.
 */
int
virDomainMemoryPeek(virDomainPtr dom,
                    unsigned long long start,
                    size_t size,
                    void *buffer,
                    unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "start=%lld, size=%zi, buffer=%p, flags=0x%x",
                     start, size, buffer, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    /* Note on access to physical memory: A VIR_MEMORY_PHYSICAL flag is
     * a possibility.  However it isn't really useful unless the caller
     * can also access registers, particularly CR3 on x86 in order to
     * get the Page Table Directory.  Since registers are different on
     * every architecture, that would imply another call to get the
     * machine registers.
     *
     * The QEMU driver handles VIR_MEMORY_VIRTUAL, mapping it
     * to the qemu 'memsave' command which does the virtual to physical
     * mapping inside qemu.
     *
     * The QEMU driver also handles VIR_MEMORY_PHYSICAL, mapping it
     * to the qemu 'pmemsave' command.
     *
     * At time of writing there is no Xen driver.  However the Xen
     * hypervisor only lets you map physical pages from other domains,
     * and so the Xen driver would have to do the virtual to physical
     * mapping by chasing 2, 3 or 4-level page tables from the PTD.
     * There is example code in libxc (xc_translate_foreign_address)
     * which does this, although we cannot copy this code directly
     * because of incompatible licensing.
     */

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_MEMORY_VIRTUAL, VIR_MEMORY_PHYSICAL, error);

    /* Allow size == 0 as an access test. */
    if (size > 0)
        virCheckNonNullArgGoto(buffer, error);

    if (conn->driver->domainMemoryPeek) {
        int ret;
        ret = conn->driver->domainMemoryPeek(dom, start, size,
                                             buffer, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetBlockInfo:
 * @domain: a domain object
 * @disk: path to the block device, or device shorthand
 * @info: pointer to a virDomainBlockInfo structure allocated by the user
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Extract information about a domain's block device.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * For QEMU domains, the allocation and physical virDomainBlockInfo
 * values returned will generally be the same, except when using a
 * non raw, block backing device, such as qcow2 for an active domain.
 * When the persistent domain is not active, QEMU will return the
 * default which is the same value for allocation and physical.
 *
 * Active QEMU domains can return an allocation value which is more
 * representative of the currently used blocks by the device compared
 * to the physical size of the device. Applications can use/monitor
 * the allocation value with the understanding that if the domain
 * becomes inactive during an attempt to get the value, the default
 * values will be returned. Thus, the application should check
 * after the call for the domain being inactive if the values are
 * the same. Optionally, the application could be watching for a
 * shutdown event and then ignore any values received afterwards.
 * This can be an issue when a domain is being migrated and the
 * exact timing of the domain being made inactive and check of
 * the allocation value results the default being returned. For
 * a transient domain in the similar situation, this call will return
 * -1 and an error message indicating the "domain is not running".
 *
 * The following is some pseudo code illustrating the call sequence:
 *
 *   ...
 *   virDomainPtr dom;
 *   virDomainBlockInfo info;
 *   char *device;
 *   ...
 *   // Either get a list of all domains or a specific domain
 *   // via a virDomainLookupBy*() call.
 *   //
 *   // It's also required to fill in the device pointer, but that's
 *   // specific to the implementation. For the purposes of this example
 *   // a qcow2 backed device name string would need to be provided.
 *   ...
 *   // If the following call is made on a persistent domain with a
 *   // qcow2 block backed block device, then it's possible the returned
 *   // allocation equals the physical value. In that case, the domain
 *   // that may have been active prior to calling has become inactive,
 *   // such as is the case during a domain migration. Thus once we
 *   // get data returned, check for active domain when the values are
 *   // the same.
 *   if (virDomainGetBlockInfo(dom, device, &info, 0) < 0)
 *       goto failure;
 *   if (info.allocation == info.physical) {
 *       // If the domain is no longer active,
 *       // then the defaults are being returned.
 *       if (!virDomainIsActive())
 *               goto ignore_return;
 *   }
 *   // Do something with the allocation and physical values
 *   ...
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetBlockInfo(virDomainPtr domain, const char *disk,
                      virDomainBlockInfoPtr info, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "info=%p, flags=0x%x", info, flags);

    virResetLastError();

    if (info)
        memset(info, 0, sizeof(*info));

    virCheckDomainReturn(domain, -1);
    virCheckNonEmptyStringArgGoto(disk, error);
    virCheckNonNullArgGoto(info, error);

    conn = domain->conn;

    if (conn->driver->domainGetBlockInfo) {
        int ret;
        ret = conn->driver->domainGetBlockInfo(domain, disk, info, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainDefineXML:
 * @conn: pointer to the hypervisor connection
 * @xml: the XML description for the domain, preferably in UTF-8
 *
 * Define a domain, but does not start it.
 * This definition is persistent, until explicitly undefined with
 * virDomainUndefine(). A previous definition for this domain would be
 * overridden if it already exists.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns NULL in case of error, a pointer to the domain otherwise
 */
virDomainPtr
virDomainDefineXML(virConnectPtr conn, const char *xml)
{
    VIR_DEBUG("conn=%p, xml=%s", conn, NULLSTR(xml));

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(xml, error);

    if (conn->driver->domainDefineXML) {
        virDomainPtr ret;
        ret = conn->driver->domainDefineXML(conn, xml);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainDefineXMLFlags:
 * @conn: pointer to the hypervisor connection
 * @xml: the XML description for the domain, preferably in UTF-8
 * @flags: bitwise OR of the virDomainDefineFlags constants
 *
 * Defines a domain, but does not start it.
 * This definition is persistent, until explicitly undefined with
 * virDomainUndefine(). A previous definition for this domain would be
 * overridden if it already exists.
 *
 * virDomainFree should be used to free the resources after the
 * domain object is no longer needed.
 *
 * Returns NULL in case of error, a pointer to the domain otherwise
 */
virDomainPtr
virDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
    VIR_DEBUG("conn=%p, xml=%s flags=0x%x", conn, NULLSTR(xml), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(xml, error);

    if (conn->driver->domainDefineXMLFlags) {
        virDomainPtr ret;
        ret = conn->driver->domainDefineXMLFlags(conn, xml, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainUndefine:
 * @domain: pointer to a defined domain
 *
 * Undefine a domain. If the domain is running, it's converted to
 * transient domain, without stopping it. If the domain is inactive,
 * the domain configuration is removed.
 *
 * If the domain has a managed save image (see
 * virDomainHasManagedSaveImage()), or if it is inactive and has any
 * snapshot metadata (see virDomainSnapshotNum()) or checkpoint
 * metadata (see virDomainListAllCheckpoints()), then the undefine
 * will fail. See virDomainUndefineFlags() for more control.
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
virDomainUndefine(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainUndefine) {
        int ret;
        ret = conn->driver->domainUndefine(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainUndefineFlags:
 * @domain: pointer to a defined domain
 * @flags: bitwise-OR of supported virDomainUndefineFlagsValues
 *
 * Undefine a domain. If the domain is running, it's converted to
 * transient domain, without stopping it. If the domain is inactive,
 * the domain configuration is removed.
 *
 * If the domain has a managed save image (see virDomainHasManagedSaveImage()),
 * then including VIR_DOMAIN_UNDEFINE_MANAGED_SAVE in @flags will also remove
 * that file, and omitting the flag will cause the undefine process to fail.
 *
 * If the domain is inactive and has any snapshot metadata (see
 * virDomainSnapshotNum()), then including
 * VIR_DOMAIN_UNDEFINE_SNAPSHOTS_METADATA in @flags will also remove
 * that metadata.  Omitting the flag will cause the undefine of an
 * inactive domain with snapshots to fail.  Active domains will retain
 * snapshot metadata until the (now-transient) domain halts,
 * regardless of whether this flag is present.  On hypervisors that
 * support snapshots, but where snapshots do not use libvirt metadata,
 * this flag has no effect.
 *
 * If the domain is inactive and has any checkpoint metadata (see
 * virDomainListAllCheckpoints()), then including
 * VIR_DOMAIN_UNDEFINE_CHECKPOINTS_METADATA in @flags will also remove
 * that metadata. Omitting the flag will cause the undefine of an
 * inactive domain with checkpoints to fail. Active domains will
 * retain checkpoint metadata until the (now-transient) domain halts,
 * regardless of whether this flag is present. On hypervisors that
 * support checkpoints, but where checkpoints do not use libvirt
 * metadata, this flag has no effect.
 *
 * If the domain has any nvram specified, the undefine process will fail
 * unless VIR_DOMAIN_UNDEFINE_KEEP_NVRAM is specified, or if
 * VIR_DOMAIN_UNDEFINE_NVRAM is specified to remove the nvram file.
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
virDomainUndefineFlags(virDomainPtr domain,
                       unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainUndefineFlags) {
        int ret;
        ret = conn->driver->domainUndefineFlags(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virConnectNumOfDefinedDomains:
 * @conn: pointer to the hypervisor connection
 *
 * Provides the number of defined but inactive domains.
 *
 * Returns the number of domain found or -1 in case of error
 */
int
virConnectNumOfDefinedDomains(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    if (conn->driver->connectNumOfDefinedDomains) {
        int ret;
        ret = conn->driver->connectNumOfDefinedDomains(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectListDefinedDomains:
 * @conn: pointer to the hypervisor connection
 * @names: pointer to an array to store the names
 * @maxnames: size of the array
 *
 * list the defined but inactive domains, stores the pointers to the names
 * in @names
 *
 * For active domains, see virConnectListDomains().  For more control over
 * the results, see virConnectListAllDomains().
 *
 * Returns the number of names provided in the array or -1 in case of error.
 * Note that this command is inherently racy; a domain can be defined between
 * a call to virConnectNumOfDefinedDomains() and this call; you are only
 * guaranteed that all currently defined domains were listed if the return
 * is less than @maxids.  The client must call free() on each returned name.
 */
int
virConnectListDefinedDomains(virConnectPtr conn, char **const names,
                             int maxnames)
{
    VIR_DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArrayArgGoto(names, maxnames, error);
    virCheckNonNegativeArgGoto(maxnames, error);

    if (conn->driver->connectListDefinedDomains) {
        int ret;
        ret = conn->driver->connectListDefinedDomains(conn, names, maxnames);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectListAllDomains:
 * @conn: Pointer to the hypervisor connection.
 * @domains: Pointer to a variable to store the array containing domain objects
 *           or NULL if the list is not required (just returns number of guests).
 * @flags: bitwise-OR of virConnectListAllDomainsFlags
 *
 * Collect a possibly-filtered list of all domains, and return an allocated
 * array of information for each.  This API solves the race inherent in
 * virConnectListDomains() and virConnectListDefinedDomains().
 *
 * Normally, all domains are returned; however, @flags can be used to
 * filter the results for a smaller list of targeted domains.  The valid
 * flags are divided into groups, where each group contains bits that
 * describe mutually exclusive attributes of a domain, and where all bits
 * within a group describe all possible domains.  Some hypervisors might
 * reject explicit bits from a group where the hypervisor cannot make a
 * distinction (for example, not all hypervisors can tell whether domains
 * have snapshots).  For a group supported by a given hypervisor, the
 * behavior when no bits of a group are set is identical to the behavior
 * when all bits in that group are set.  When setting bits from more than
 * one group, it is possible to select an impossible combination (such
 * as an inactive transient domain), in that case a hypervisor may return
 * either 0 or an error.
 *
 * The first group of @flags is VIR_CONNECT_LIST_DOMAINS_ACTIVE (online
 * domains) and VIR_CONNECT_LIST_DOMAINS_INACTIVE (offline domains).
 *
 * The next group of @flags is VIR_CONNECT_LIST_DOMAINS_PERSISTENT (defined
 * domains) and VIR_CONNECT_LIST_DOMAINS_TRANSIENT (running but not defined).
 *
 * The next group of @flags covers various domain states:
 * VIR_CONNECT_LIST_DOMAINS_RUNNING, VIR_CONNECT_LIST_DOMAINS_PAUSED,
 * VIR_CONNECT_LIST_DOMAINS_SHUTOFF, and a catch-all for all other states
 * (such as crashed, this catch-all covers the possibility of adding new
 * states).
 *
 * The remaining groups cover boolean attributes commonly asked about
 * domains; they include VIR_CONNECT_LIST_DOMAINS_MANAGEDSAVE and
 * VIR_CONNECT_LIST_DOMAINS_NO_MANAGEDSAVE, for filtering based on whether
 * a managed save image exists; VIR_CONNECT_LIST_DOMAINS_AUTOSTART and
 * VIR_CONNECT_LIST_DOMAINS_NO_AUTOSTART, for filtering based on autostart;
 * VIR_CONNECT_LIST_DOMAINS_HAS_SNAPSHOT and
 * VIR_CONNECT_LIST_DOMAINS_NO_SNAPSHOT, for filtering based on whether
 * a domain has snapshots; VIR_CONNECT_LIST_DOMAINS_HAS_CHECKPOINT and
 * VIR_CONNECT_LIST_DOMAINS_NO_CHECKPOINT, for filtering based on whether
 * a domain has checkpoints.
 *
 * Example of usage:
 *
 *   virDomainPtr *domains;
 *   size_t i;
 *   int ret;
 *   unsigned int flags = VIR_CONNECT_LIST_DOMAINS_RUNNING |
 *                        VIR_CONNECT_LIST_DOMAINS_PERSISTENT;
 *   ret = virConnectListAllDomains(conn, &domains, flags);
 *   if (ret < 0)
 *       error();
 *   for (i = 0; i < ret; i++) {
 *        do_something_with_domain(domains[i]);
 *        //here or in a separate loop if needed
 *        virDomainFree(domains[i]);
 *   }
 *   free(domains);
 *
 * Returns the number of domains found or -1 and sets domains to NULL in case of
 * error.  On success, the array stored into @domains is guaranteed to have an
 * extra allocated element set to NULL but not included in the return count, to
 * make iteration easier. The caller is responsible for calling virDomainFree()
 * on each array element, then calling free() on @domains.
 */
int
virConnectListAllDomains(virConnectPtr conn,
                         virDomainPtr **domains,
                         unsigned int flags)
{
    VIR_DEBUG("conn=%p, domains=%p, flags=0x%x", conn, domains, flags);

    virResetLastError();

    if (domains)
        *domains = NULL;

    virCheckConnectReturn(conn, -1);

    if (conn->driver->connectListAllDomains) {
        int ret;
        ret = conn->driver->connectListAllDomains(conn, domains, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainCreate:
 * @domain: pointer to a defined domain
 *
 * Launch a defined domain. If the call succeeds the domain moves from the
 * defined to the running domains pools.  The domain will be paused only
 * if restoring from managed state created from a paused domain.  For more
 * control, see virDomainCreateWithFlags().
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
virDomainCreate(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainCreate) {
        int ret;
        ret = conn->driver->domainCreate(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainCreateWithFlags:
 * @domain: pointer to a defined domain
 * @flags: bitwise-OR of supported virDomainCreateFlags
 *
 * Launch a defined domain. If the call succeeds the domain moves from the
 * defined to the running domains pools.
 *
 * If the VIR_DOMAIN_START_PAUSED flag is set, or if the guest domain
 * has a managed save image that requested paused state (see
 * virDomainManagedSave()) the guest domain will be started, but its
 * CPUs will remain paused. The CPUs can later be manually started
 * using virDomainResume().  In all other cases, the guest domain will
 * be running.
 *
 * If the VIR_DOMAIN_START_AUTODESTROY flag is set, the guest
 * domain will be automatically destroyed when the virConnectPtr
 * object is finally released. This will also happen if the
 * client application crashes / loses its connection to the
 * libvirtd daemon. Any domains marked for auto destroy will
 * block attempts at migration. Hypervisors may also block save-to-file,
 * or snapshots.
 *
 * If the VIR_DOMAIN_START_BYPASS_CACHE flag is set, and there is a
 * managed save file for this domain (created by virDomainManagedSave()),
 * then libvirt will attempt to bypass the file system cache while restoring
 * the file, or fail if it cannot do so for the given system; this can allow
 * less pressure on file system cache, but also risks slowing loads from NFS.
 *
 * If the VIR_DOMAIN_START_FORCE_BOOT flag is set, then any managed save
 * file for this domain is discarded, and the domain boots from scratch.
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
virDomainCreateWithFlags(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainCreateWithFlags) {
        int ret;
        ret = conn->driver->domainCreateWithFlags(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainCreateWithFiles:
 * @domain: pointer to a defined domain
 * @nfiles: number of file descriptors passed
 * @files: list of file descriptors passed
 * @flags: bitwise-OR of supported virDomainCreateFlags
 *
 * Launch a defined domain. If the call succeeds the domain moves from the
 * defined to the running domains pools.
 *
 * @files provides an array of file descriptors which will be
 * made available to the 'init' process of the guest. The file
 * handles exposed to the guest will be renumbered to start
 * from 3 (ie immediately following stderr). This is only
 * supported for guests which use container based virtualization
 * technology.
 *
 * If the VIR_DOMAIN_START_PAUSED flag is set, or if the guest domain
 * has a managed save image that requested paused state (see
 * virDomainManagedSave()) the guest domain will be started, but its
 * CPUs will remain paused. The CPUs can later be manually started
 * using virDomainResume().  In all other cases, the guest domain will
 * be running.
 *
 * If the VIR_DOMAIN_START_AUTODESTROY flag is set, the guest
 * domain will be automatically destroyed when the virConnectPtr
 * object is finally released. This will also happen if the
 * client application crashes / loses its connection to the
 * libvirtd daemon. Any domains marked for auto destroy will
 * block attempts at migration, save-to-file, or snapshots.
 *
 * If the VIR_DOMAIN_START_BYPASS_CACHE flag is set, and there is a
 * managed save file for this domain (created by virDomainManagedSave()),
 * then libvirt will attempt to bypass the file system cache while restoring
 * the file, or fail if it cannot do so for the given system; this can allow
 * less pressure on file system cache, but also risks slowing loads from NFS.
 *
 * If the VIR_DOMAIN_START_FORCE_BOOT flag is set, then any managed save
 * file for this domain is discarded, and the domain boots from scratch.
 *
 * Returns 0 in case of success, -1 in case of error
 */
int
virDomainCreateWithFiles(virDomainPtr domain, unsigned int nfiles,
                         int *files, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "nfiles=%u, files=%p, flags=0x%x",
                     nfiles, files, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainCreateWithFiles) {
        int ret;
        ret = conn->driver->domainCreateWithFiles(domain,
                                                  nfiles, files,
                                                  flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetAutostart:
 * @domain: a domain object
 * @autostart: the value returned
 *
 * Provides a boolean value indicating whether the domain
 * configured to be automatically started when the host
 * machine boots.
 *
 * Returns -1 in case of error, 0 in case of success
 */
int
virDomainGetAutostart(virDomainPtr domain,
                      int *autostart)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "autostart=%p", autostart);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(autostart, error);

    conn = domain->conn;

    if (conn->driver->domainGetAutostart) {
        int ret;
        ret = conn->driver->domainGetAutostart(domain, autostart);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetAutostart:
 * @domain: a domain object
 * @autostart: whether the domain should be automatically started 0 or 1
 *
 * Configure the domain to be automatically started
 * when the host machine boots.
 *
 * Returns -1 in case of error, 0 in case of success
 */
int
virDomainSetAutostart(virDomainPtr domain,
                      int autostart)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "autostart=%d", autostart);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainSetAutostart) {
        int ret;
        ret = conn->driver->domainSetAutostart(domain, autostart);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainInjectNMI:
 * @domain: pointer to domain object, or NULL for Domain0
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Send NMI to the guest
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainInjectNMI(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;
    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainInjectNMI) {
        int ret;
        ret = conn->driver->domainInjectNMI(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSendKey:
 * @domain:    pointer to domain object, or NULL for Domain0
 * @codeset:   the code set of keycodes, from virKeycodeSet
 * @holdtime:  the duration (in milliseconds) that the keys will be held
 * @keycodes:  array of keycodes
 * @nkeycodes: number of keycodes, up to VIR_DOMAIN_SEND_KEY_MAX_KEYS
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Send key(s) to the guest.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSendKey(virDomainPtr domain,
                 unsigned int codeset,
                 unsigned int holdtime,
                 unsigned int *keycodes,
                 int nkeycodes,
                 unsigned int flags)
{
    virConnectPtr conn;
    VIR_DOMAIN_DEBUG(domain, "codeset=%u, holdtime=%u, nkeycodes=%u, flags=0x%x",
                     codeset, holdtime, nkeycodes, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(keycodes, error);
    virCheckPositiveArgGoto(nkeycodes, error);

    if (codeset >= VIR_KEYCODE_SET_LAST) {
        virReportInvalidArg(codeset,
                            _("Unsupported codeset '%d'"),
                            codeset);
        goto error;
    }

    if (nkeycodes > VIR_DOMAIN_SEND_KEY_MAX_KEYS) {
        virReportInvalidArg(nkeycodes,
                            _("nkeycodes must be <= %d"),
                            VIR_DOMAIN_SEND_KEY_MAX_KEYS);
        goto error;
    }

    if (conn->driver->domainSendKey) {
        int ret;
        ret = conn->driver->domainSendKey(domain, codeset, holdtime,
                                          keycodes, nkeycodes, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSendProcessSignal:
 * @domain: pointer to domain object
 * @pid_value: a positive integer process ID, or negative integer process group ID
 * @signum: a signal from the virDomainProcessSignal enum
 * @flags: currently unused, pass 0
 *
 * Send a signal to the designated process in the guest
 *
 * The signal numbers must be taken from the virDomainProcessSignal
 * enum. These will be translated to the corresponding signal
 * number for the guest OS, by the guest agent delivering the
 * signal. If there is no mapping from virDomainProcessSignal to
 * the native OS signals, this API will report an error.
 *
 * If @pid_value is an integer greater than zero, it is
 * treated as a process ID. If @pid_value is an integer
 * less than zero, it is treated as a process group ID.
 * All the @pid_value numbers are from the container/guest
 * namespace. The value zero is not valid.
 *
 * Not all hypervisors will support sending signals to
 * arbitrary processes or process groups. If this API is
 * implemented the minimum requirement is to be able to
 * use @pid_value == 1 (i.e. kill init). No other value is
 * required to be supported.
 *
 * If the @signum is VIR_DOMAIN_PROCESS_SIGNAL_NOP then this
 * API will simply report whether the process is running in
 * the container/guest.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSendProcessSignal(virDomainPtr domain,
                           long long pid_value,
                           unsigned int signum,
                           unsigned int flags)
{
    virConnectPtr conn;
    VIR_DOMAIN_DEBUG(domain, "pid=%lld, signum=%u flags=0x%x",
                     pid_value, signum, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonZeroArgGoto(pid_value, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainSendProcessSignal) {
        int ret;
        ret = conn->driver->domainSendProcessSignal(domain,
                                                    pid_value,
                                                    signum,
                                                    flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetVcpus:
 * @domain: pointer to domain object, or NULL for Domain0
 * @nvcpus: the new number of virtual CPUs for this domain
 *
 * Dynamically change the number of virtual CPUs used by the domain.
 * Note that this call may fail if the underlying virtualization hypervisor
 * does not support it or if growing the number is arbitrarily limited.
 * This function may require privileged access to the hypervisor.
 *
 * Note that if this call is executed before the guest has finished booting,
 * the guest may fail to process the change.
 *
 * This command only changes the runtime configuration of the domain,
 * so can only be called on an active domain.  It is hypervisor-dependent
 * whether it also affects persistent configuration; for more control,
 * use virDomainSetVcpusFlags().
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSetVcpus(virDomainPtr domain, unsigned int nvcpus)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "nvcpus=%u", nvcpus);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonZeroArgGoto(nvcpus, error);

    if (conn->driver->domainSetVcpus) {
        int ret;
        ret = conn->driver->domainSetVcpus(domain, nvcpus);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetVcpusFlags:
 * @domain: pointer to domain object, or NULL for Domain0
 * @nvcpus: the new number of virtual CPUs for this domain, must be at least 1
 * @flags: bitwise-OR of virDomainVcpuFlags
 *
 * Dynamically change the number of virtual CPUs used by the domain.
 * Note that this call may fail if the underlying virtualization hypervisor
 * does not support it or if growing the number is arbitrarily limited.
 * This function may require privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE to affect a running
 * domain (which may fail if domain is not active), or
 * VIR_DOMAIN_AFFECT_CONFIG to affect the next boot via the XML
 * description of the domain.  Both flags may be set.
 * If neither flag is specified (that is, @flags is VIR_DOMAIN_AFFECT_CURRENT),
 * then an inactive domain modifies persistent setup, while an active domain
 * is hypervisor-dependent on whether just live or both live and persistent
 * state is changed.
 *
 * Note that if this call is executed before the guest has finished booting,
 * the guest may fail to process the change.
 *
 * If @flags includes VIR_DOMAIN_VCPU_MAXIMUM, then
 * VIR_DOMAIN_AFFECT_LIVE must be clear, and only the maximum virtual
 * CPU limit is altered; generally, this value must be less than or
 * equal to virConnectGetMaxVcpus().  Otherwise, this call affects the
 * current virtual CPU limit, which must be less than or equal to the
 * maximum limit. Note that hypervisors may not allow changing the maximum
 * vcpu count if processor topology is specified.
 *
 * If @flags includes VIR_DOMAIN_VCPU_GUEST, then the state of processors is
 * modified inside the guest instead of the hypervisor. This flag can only
 * be used with live guests and is incompatible with VIR_DOMAIN_VCPU_MAXIMUM.
 * The usage of this flag may require a guest agent configured.
 *
 * Not all hypervisors can support all flag combinations.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSetVcpusFlags(virDomainPtr domain, unsigned int nvcpus,
                       unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "nvcpus=%u, flags=0x%x", nvcpus, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    VIR_REQUIRE_FLAG_GOTO(VIR_DOMAIN_VCPU_MAXIMUM,
                          VIR_DOMAIN_AFFECT_CONFIG,
                          error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_VCPU_GUEST,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    virCheckNonZeroArgGoto(nvcpus, error);

    conn = domain->conn;

    if (conn->driver->domainSetVcpusFlags) {
        int ret;
        ret = conn->driver->domainSetVcpusFlags(domain, nvcpus, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetVcpusFlags:
 * @domain: pointer to domain object, or NULL for Domain0
 * @flags: bitwise-OR of virDomainVcpuFlags
 *
 * Query the number of virtual CPUs used by the domain.  Note that
 * this call may fail if the underlying virtualization hypervisor does
 * not support it.  This function may require privileged access to the
 * hypervisor.
 *
 * If @flags includes VIR_DOMAIN_AFFECT_LIVE, this will query a
 * running domain (which will fail if domain is not active); if
 * it includes VIR_DOMAIN_AFFECT_CONFIG, this will query the XML
 * description of the domain.  It is an error to set both flags.
 * If neither flag is set (that is, VIR_DOMAIN_AFFECT_CURRENT),
 * then the configuration queried depends on whether the domain
 * is currently running.
 *
 * If @flags includes VIR_DOMAIN_VCPU_MAXIMUM, then the maximum
 * virtual CPU limit is queried.  Otherwise, this call queries the
 * current virtual CPU count.
 *
 * If @flags includes VIR_DOMAIN_VCPU_GUEST, then the state of the processors
 * is queried in the guest instead of the hypervisor. This flag is only usable
 * on live domains. Guest agent may be needed for this flag to be available.
 *
 * Returns the number of vCPUs in case of success, -1 in case of failure.
 */
int
virDomainGetVcpusFlags(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    if (flags & VIR_DOMAIN_VCPU_GUEST)
        virCheckReadOnlyGoto(conn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    if (conn->driver->domainGetVcpusFlags) {
        int ret;
        ret = conn->driver->domainGetVcpusFlags(domain, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainPinVcpu:
 * @domain: pointer to domain object, or NULL for Domain0
 * @vcpu: virtual CPU number
 * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN)
 *      Each bit set to 1 means that corresponding CPU is usable.
 *      Bytes are stored in little-endian order: CPU0-7, 8-15...
 *      In each byte, lowest CPU number is least significant bit.
 * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in
 *      underlying virtualization system (Xen...).
 *      If maplen < size, missing bytes are set to zero.
 *      If maplen > size, failure code is returned.
 *
 * Dynamically change the real CPUs which can be allocated to a virtual CPU.
 * This function may require privileged access to the hypervisor.
 *
 * This command only changes the runtime configuration of the domain,
 * so can only be called on an active domain.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainPinVcpu(virDomainPtr domain, unsigned int vcpu,
                 unsigned char *cpumap, int maplen)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "vcpu=%u, cpumap=%p, maplen=%d",
                     vcpu, cpumap, maplen);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(cpumap, error);
    virCheckPositiveArgGoto(maplen, error);

    if (conn->driver->domainPinVcpu) {
        int ret;
        ret = conn->driver->domainPinVcpu(domain, vcpu, cpumap, maplen);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainPinVcpuFlags:
 * @domain: pointer to domain object, or NULL for Domain0
 * @vcpu: virtual CPU number
 * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN)
 *      Each bit set to 1 means that corresponding CPU is usable.
 *      Bytes are stored in little-endian order: CPU0-7, 8-15...
 *      In each byte, lowest CPU number is least significant bit.
 * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in
 *      underlying virtualization system (Xen...).
 *      If maplen < size, missing bytes are set to zero.
 *      If maplen > size, failure code is returned.
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Dynamically change the real CPUs which can be allocated to a virtual CPU.
 * This function may require privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set.
 * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain
 * and may fail if domain is not alive.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified (that is,
 * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies
 * persistent setup, while an active domain is hypervisor-dependent on whether
 * just live or both live and persistent state is changed.
 * Not all hypervisors can support all flag combinations.
 *
 * See also virDomainGetVcpuPinInfo for querying this information.
 *
 * Returns 0 in case of success, -1 in case of failure.
 *
 */
int
virDomainPinVcpuFlags(virDomainPtr domain, unsigned int vcpu,
                      unsigned char *cpumap, int maplen, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "vcpu=%u, cpumap=%p, maplen=%d, flags=0x%x",
                     vcpu, cpumap, maplen, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(cpumap, error);
    virCheckPositiveArgGoto(maplen, error);

    if (conn->driver->domainPinVcpuFlags) {
        int ret;
        ret = conn->driver->domainPinVcpuFlags(domain, vcpu, cpumap, maplen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetVcpuPinInfo:
 * @domain: pointer to domain object, or NULL for Domain0
 * @ncpumaps: the number of cpumap (listed first to match virDomainGetVcpus)
 * @cpumaps: pointer to a bit map of real CPUs for all vcpus of this
 *     domain (in 8-bit bytes) (OUT)
 *     It's assumed there is <ncpumaps> cpumap in cpumaps array.
 *     The memory allocated to cpumaps must be (ncpumaps * maplen) bytes
 *     (ie: calloc(ncpumaps, maplen)).
 *     One cpumap inside cpumaps has the format described in
 *     virDomainPinVcpu() API.
 *     Must not be NULL.
 * @maplen: the number of bytes in one cpumap, from 1 up to size of CPU map.
 *     Must be positive.
 * @flags: bitwise-OR of virDomainModificationImpact
 *     Must not be VIR_DOMAIN_AFFECT_LIVE and
 *     VIR_DOMAIN_AFFECT_CONFIG concurrently.
 *
 * Query the CPU affinity setting of all virtual CPUs of domain, store it
 * in cpumaps.
 *
 * Returns the number of virtual CPUs in case of success,
 * -1 in case of failure.
 */
int
virDomainGetVcpuPinInfo(virDomainPtr domain, int ncpumaps,
                        unsigned char *cpumaps, int maplen, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "ncpumaps=%d, cpumaps=%p, maplen=%d, flags=0x%x",
                     ncpumaps, cpumaps, maplen, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArrayArgGoto(cpumaps, ncpumaps, error);
    virCheckPositiveArgGoto(ncpumaps, error);
    virCheckPositiveArgGoto(maplen, error);

    if (VIR_INT_MULTIPLY_OVERFLOW(ncpumaps, maplen)) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %d * %d"),
                       ncpumaps, maplen);
        goto error;
    }

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    if (conn->driver->domainGetVcpuPinInfo) {
        int ret;
        ret = conn->driver->domainGetVcpuPinInfo(domain, ncpumaps,
                                                 cpumaps, maplen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainPinEmulator:
 * @domain: pointer to domain object, or NULL for Domain0
 * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN)
 *      Each bit set to 1 means that corresponding CPU is usable.
 *      Bytes are stored in little-endian order: CPU0-7, 8-15...
 *      In each byte, lowest CPU number is least significant bit.
 * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in
 *      underlying virtualization system (Xen...).
 *      If maplen < size, missing bytes are set to zero.
 *      If maplen > size, failure code is returned.
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Dynamically change the real CPUs which can be allocated to all emulator
 * threads. This function may require privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set.
 * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain
 * and may fail if domain is not alive.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified (that is,
 * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies
 * persistent setup, while an active domain is hypervisor-dependent on whether
 * just live or both live and persistent state is changed.
 * Not all hypervisors can support all flag combinations.
 *
 * See also virDomainGetEmulatorPinInfo for querying this information.
 *
 * Returns 0 in case of success, -1 in case of failure.
 *
 */
int
virDomainPinEmulator(virDomainPtr domain, unsigned char *cpumap,
                     int maplen, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cpumap=%p, maplen=%d, flags=0x%x",
                     cpumap, maplen, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    virCheckNonNullArgGoto(cpumap, error);
    virCheckPositiveArgGoto(maplen, error);

    if (conn->driver->domainPinEmulator) {
        int ret;
        ret = conn->driver->domainPinEmulator(domain, cpumap, maplen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetEmulatorPinInfo:
 * @domain: pointer to domain object, or NULL for Domain0
 * @cpumap: pointer to a bit map of real CPUs for all emulator threads of
 *     this domain (in 8-bit bytes) (OUT)
 *     There is only one cpumap for all emulator threads.
 *     Must not be NULL.
 * @maplen: the number of bytes in one cpumap, from 1 up to size of CPU map.
 *     Must be positive.
 * @flags: bitwise-OR of virDomainModificationImpact
 *     Must not be VIR_DOMAIN_AFFECT_LIVE and
 *     VIR_DOMAIN_AFFECT_CONFIG concurrently.
 *
 * Query the CPU affinity setting of all emulator threads of domain, store
 * it in cpumap.
 *
 * Returns 1 in case of success,
 * 0 in case of no emulator threads are pined to pcpus,
 * -1 in case of failure.
 */
int
virDomainGetEmulatorPinInfo(virDomainPtr domain, unsigned char *cpumap,
                            int maplen, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cpumap=%p, maplen=%d, flags=0x%x",
                     cpumap, maplen, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virCheckNonNullArgGoto(cpumap, error);
    virCheckPositiveArgGoto(maplen, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    conn = domain->conn;

    if (conn->driver->domainGetEmulatorPinInfo) {
        int ret;
        ret = conn->driver->domainGetEmulatorPinInfo(domain, cpumap,
                                                     maplen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetVcpus:
 * @domain: pointer to domain object, or NULL for Domain0
 * @info: pointer to an array of virVcpuInfo structures (OUT)
 * @maxinfo: number of structures in info array
 * @cpumaps: pointer to a bit map of real CPUs for all vcpus of this
 *      domain (in 8-bit bytes) (OUT)
 *      If cpumaps is NULL, then no cpumap information is returned by the API.
 *      It's assumed there is <maxinfo> cpumap in cpumaps array.
 *      The memory allocated to cpumaps must be (maxinfo * maplen) bytes
 *      (ie: calloc(maxinfo, maplen)).
 *      One cpumap inside cpumaps has the format described in
 *      virDomainPinVcpu() API.
 * @maplen: number of bytes in one cpumap, from 1 up to size of CPU map in
 *      underlying virtualization system (Xen...).
 *      Must be zero when cpumaps is NULL and positive when it is non-NULL.
 *
 * Extract information about virtual CPUs of domain, store it in info array
 * and also in cpumaps if this pointer isn't NULL.  This call may fail
 * on an inactive domain.
 *
 * See also virDomainGetVcpuPinInfo for querying just cpumaps, including on
 * an inactive domain.
 *
 * Returns the number of info filled in case of success, -1 in case of failure.
 */
int
virDomainGetVcpus(virDomainPtr domain, virVcpuInfoPtr info, int maxinfo,
                  unsigned char *cpumaps, int maplen)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "info=%p, maxinfo=%d, cpumaps=%p, maplen=%d",
                     info, maxinfo, cpumaps, maplen);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(info, error);
    virCheckPositiveArgGoto(maxinfo, error);

    /* Ensure that domainGetVcpus (aka remoteDomainGetVcpus) does not
       try to memcpy anything into a NULL pointer.  */
    if (cpumaps)
        virCheckPositiveArgGoto(maplen, error);
    else
        virCheckZeroArgGoto(maplen, error);

    if (cpumaps && VIR_INT_MULTIPLY_OVERFLOW(maxinfo, maplen)) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %d * %d"),
                       maxinfo, maplen);
        goto error;
    }

    conn = domain->conn;

    if (conn->driver->domainGetVcpus) {
        int ret;
        ret = conn->driver->domainGetVcpus(domain, info, maxinfo,
                                           cpumaps, maplen);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetMaxVcpus:
 * @domain: pointer to domain object
 *
 * Provides the maximum number of virtual CPUs supported for
 * the guest VM. If the guest is inactive, this is basically
 * the same as virConnectGetMaxVcpus(). If the guest is running
 * this will reflect the maximum number of virtual CPUs the
 * guest was booted with.  For more details, see virDomainGetVcpusFlags().
 *
 * Returns the maximum of virtual CPU or -1 in case of error.
 */
int
virDomainGetMaxVcpus(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    if (conn->driver->domainGetMaxVcpus) {
        int ret;
        ret = conn->driver->domainGetMaxVcpus(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetIOThreadInfo:
 * @dom: a domain object
 * @info: pointer to an array of virDomainIOThreadInfo structures (OUT)
 * @flags: bitwise-OR of virDomainModificationImpact
 *     Must not be VIR_DOMAIN_AFFECT_LIVE and
 *     VIR_DOMAIN_AFFECT_CONFIG concurrently.
 *
 * Fetch IOThreads of an active domain including the cpumap information to
 * determine on which CPU the IOThread has affinity to run.
 *
 * Returns the number of IOThreads or -1 in case of error.
 * On success, the array of information is stored into @info. The caller is
 * responsible for calling virDomainIOThreadInfoFree() on each array element,
 * then calling free() on @info. On error, @info is set to NULL.
 */
int
virDomainGetIOThreadInfo(virDomainPtr dom,
                         virDomainIOThreadInfoPtr **info,
                         unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "info=%p flags=0x%x", info, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckNonNullArgGoto(info, error);
    *info = NULL;

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    if (dom->conn->driver->domainGetIOThreadInfo) {
        int ret;
        ret = dom->conn->driver->domainGetIOThreadInfo(dom, info, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainIOThreadInfoFree:
 * @info: pointer to a virDomainIOThreadInfo object
 *
 * Frees the memory used by @info.
 */
void
virDomainIOThreadInfoFree(virDomainIOThreadInfoPtr info)
{
    if (!info)
        return;

    VIR_FREE(info->cpumap);
    VIR_FREE(info);
}


/**
 * virDomainPinIOThread:
 * @domain: a domain object
 * @iothread_id: the IOThread ID to set the CPU affinity
 * @cpumap: pointer to a bit map of real CPUs (in 8-bit bytes) (IN)
 *      Each bit set to 1 means that corresponding CPU is usable.
 *      Bytes are stored in little-endian order: CPU0-7, 8-15...
 *      In each byte, lowest CPU number is least significant bit.
 * @maplen: number of bytes in cpumap, from 1 up to size of CPU map in
 *      underlying virtualization system (Xen...).
 *      If maplen < size, missing bytes are set to zero.
 *      If maplen > size, failure code is returned.
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Dynamically change the real CPUs which can be allocated to an IOThread.
 * This function may require privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set.
 * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain
 * and may fail if domain is not alive.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified (that is,
 * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies
 * persistent setup, while an active domain is hypervisor-dependent on whether
 * just live or both live and persistent state is changed.
 * Not all hypervisors can support all flag combinations.
 *
 * See also virDomainGetIOThreadInfo for querying this information.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainPinIOThread(virDomainPtr domain,
                     unsigned int iothread_id,
                     unsigned char *cpumap,
                     int maplen,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "iothread_id=%u, cpumap=%p, maplen=%d",
                     iothread_id, cpumap, maplen);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(cpumap, error);
    virCheckPositiveArgGoto(maplen, error);

    if (conn->driver->domainPinIOThread) {
        int ret;
        ret = conn->driver->domainPinIOThread(domain, iothread_id,
                                              cpumap, maplen, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainAddIOThread:
 * @domain: a domain object
 * @iothread_id: the specific IOThread ID value to add
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Dynamically add an IOThread to the domain. It is left up to the
 * underlying virtual hypervisor to determine the valid range for an
 * @iothread_id and determining whether the @iothread_id already exists.
 *
 * Note that this call can fail if the underlying virtualization hypervisor
 * does not support it or if growing the number is arbitrarily limited.
 * This function requires privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set.
 * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain
 * and may fail if domain is not alive.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified (that is,
 * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies
 * persistent setup, while an active domain is hypervisor-dependent on whether
 * just live or both live and persistent state is changed.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainAddIOThread(virDomainPtr domain,
                     unsigned int iothread_id,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "iothread_id=%u, flags=0x%x",
                     iothread_id, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    conn = domain->conn;

    if (conn->driver->domainAddIOThread) {
        int ret;
        ret = conn->driver->domainAddIOThread(domain, iothread_id, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainDelIOThread:
 * @domain: a domain object
 * @iothread_id: the specific IOThread ID value to delete
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Dynamically delete an IOThread from the domain. The @iothread_id to be
 * deleted must not have a resource associated with it and can be any of
 * the currently valid IOThread ID's.
 *
 * Note that this call can fail if the underlying virtualization hypervisor
 * does not support it or if reducing the number is arbitrarily limited.
 * This function requires privileged access to the hypervisor.
 *
 * @flags may include VIR_DOMAIN_AFFECT_LIVE or VIR_DOMAIN_AFFECT_CONFIG.
 * Both flags may be set.
 * If VIR_DOMAIN_AFFECT_LIVE is set, the change affects a running domain
 * and may fail if domain is not alive.
 * If VIR_DOMAIN_AFFECT_CONFIG is set, the change affects persistent state,
 * and will fail for transient domains. If neither flag is specified (that is,
 * @flags is VIR_DOMAIN_AFFECT_CURRENT), then an inactive domain modifies
 * persistent setup, while an active domain is hypervisor-dependent on whether
 * just live or both live and persistent state is changed.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainDelIOThread(virDomainPtr domain,
                     unsigned int iothread_id,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "iothread_id=%u, flags=0x%x", iothread_id, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);
    virCheckNonZeroArgGoto(iothread_id, error);

    conn = domain->conn;

    if (conn->driver->domainDelIOThread) {
        int ret;
        ret = conn->driver->domainDelIOThread(domain, iothread_id, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetIOThreadParams:
 * @domain: a domain object
 * @iothread_id: the specific IOThread ID value to add
 * @params: pointer to IOThread parameter objects
 * @nparams: number of IOThread parameters
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Dynamically set IOThread parameters to the domain. It is left up to
 * the underlying virtual hypervisor to determine the valid range for an
 * @iothread_id, determining whether the @iothread_id already exists, and
 * determining the validity of the provided param values.
 *
 * See VIR_DOMAIN_IOTHREAD_* for detailed description of accepted IOThread
 * parameters.
 *
 * Since the purpose of this API is to dynamically modify the IOThread
 * @flags should only include the VIR_DOMAIN_AFFECT_CURRENT and/or
 * VIR_DOMAIN_AFFECT_LIVE virDomainMemoryModFlags. Setting other flags
 * may cause errors from the hypervisor.
 *
 * Note that this call can fail if the underlying virtualization hypervisor
 * does not support it or does not support setting the provided values.
 *
 * This function requires privileged access to the hypervisor.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainSetIOThreadParams(virDomainPtr domain,
                           unsigned int iothread_id,
                           virTypedParameterPtr params,
                           int nparams,
                           unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "iothread_id=%u, params=%p, nparams=%d, flags=0x%x",
                     iothread_id, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckPositiveArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetIOThreadParams) {
        int ret;
        ret = conn->driver->domainSetIOThreadParams(domain, iothread_id,
                                                    params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetSecurityLabel:
 * @domain: a domain object
 * @seclabel: pointer to a virSecurityLabel structure
 *
 * Extract security label of an active domain. The 'label' field
 * in the @seclabel argument will be initialized to the empty
 * string if the domain is not running under a security model.
 *
 * Returns 0 in case of success, -1 in case of failure
 */
int
virDomainGetSecurityLabel(virDomainPtr domain, virSecurityLabelPtr seclabel)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "seclabel=%p", seclabel);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(seclabel, error);

    if (conn->driver->domainGetSecurityLabel) {
        int ret;
        ret = conn->driver->domainGetSecurityLabel(domain, seclabel);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetSecurityLabelList:
 * @domain: a domain object
 * @seclabels: will be auto-allocated and filled with domains' security labels.
 * Caller must free memory on return.
 *
 * Extract the security labels of an active domain. The 'label' field
 * in the @seclabels argument will be initialized to the empty
 * string if the domain is not running under a security model.
 *
 * Returns number of elements in @seclabels on success, -1 in case of failure.
 */
int
virDomainGetSecurityLabelList(virDomainPtr domain,
                              virSecurityLabelPtr* seclabels)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "seclabels=%p", seclabels);

    virResetLastError();

    virCheckDomainReturn(domain, -1);

    virCheckNonNullArgGoto(seclabels, error);

    conn = domain->conn;

    if (conn->driver->domainGetSecurityLabelList) {
        int ret;
        ret = conn->driver->domainGetSecurityLabelList(domain, seclabels);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetMetadata:
 * @domain: a domain object
 * @type: type of metadata, from virDomainMetadataType
 * @metadata: new metadata text
 * @key: XML namespace key, or NULL
 * @uri: XML namespace URI, or NULL
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Sets the appropriate domain element given by @type to the
 * value of @metadata.  A @type of VIR_DOMAIN_METADATA_DESCRIPTION
 * is free-form text; VIR_DOMAIN_METADATA_TITLE is free-form, but no
 * newlines are permitted, and should be short (although the length is
 * not enforced). For these two options @key and @uri are irrelevant and
 * must be set to NULL.
 *
 * For type VIR_DOMAIN_METADATA_ELEMENT @metadata  must be well-formed
 * XML belonging to namespace defined by @uri with local name @key.
 *
 * Passing NULL for @metadata says to remove that element from the
 * domain XML (passing the empty string leaves the element present).
 *
 * The resulting metadata will be present in virDomainGetXMLDesc(),
 * as well as quick access through virDomainGetMetadata().
 *
 * @flags controls whether the live domain, persistent configuration,
 * or both will be modified.
 *
 * Returns 0 on success, -1 in case of failure.
 */
int
virDomainSetMetadata(virDomainPtr domain,
                     int type,
                     const char *metadata,
                     const char *key,
                     const char *uri,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain,
                     "type=%d, metadata='%s', key='%s', uri='%s', flags=0x%x",
                     type, NULLSTR(metadata), NULLSTR(key), NULLSTR(uri),
                     flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    switch (type) {
    case VIR_DOMAIN_METADATA_TITLE:
        if (metadata && strchr(metadata, '\n')) {
            virReportInvalidArg(metadata, "%s",
                                _("metadata title can't contain "
                                  "newlines"));
            goto error;
        }
        G_GNUC_FALLTHROUGH;
    case VIR_DOMAIN_METADATA_DESCRIPTION:
        virCheckNullArgGoto(uri, error);
        virCheckNullArgGoto(key, error);
        break;
    case VIR_DOMAIN_METADATA_ELEMENT:
        virCheckNonNullArgGoto(uri, error);
        if (metadata)
            virCheckNonNullArgGoto(key, error);
        break;
    default:
        /* For future expansion */
        break;
    }

    if (conn->driver->domainSetMetadata) {
        int ret;
        ret = conn->driver->domainSetMetadata(domain, type, metadata, key, uri,
                                              flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetMetadata:
 * @domain: a domain object
 * @type: type of metadata, from virDomainMetadataType
 * @uri: XML namespace identifier
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Retrieves the appropriate domain element given by @type.
 * If VIR_DOMAIN_METADATA_ELEMENT is requested parameter @uri
 * must be set to the name of the namespace the requested elements
 * belong to, otherwise must be NULL.
 *
 * If an element of the domain XML is not present, the resulting
 * error will be VIR_ERR_NO_DOMAIN_METADATA.  This method forms
 * a shortcut for seeing information from virDomainSetMetadata()
 * without having to go through virDomainGetXMLDesc().
 *
 * @flags controls whether the live domain or persistent
 * configuration will be queried.
 *
 * Returns the metadata string on success (caller must free),
 * or NULL in case of failure.
 */
char *
virDomainGetMetadata(virDomainPtr domain,
                     int type,
                     const char *uri,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "type=%d, uri='%s', flags=0x%x",
                     type, NULLSTR(uri), flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    switch (type) {
    case VIR_DOMAIN_METADATA_TITLE:
    case VIR_DOMAIN_METADATA_DESCRIPTION:
        virCheckNullArgGoto(uri, error);
        break;
    case VIR_DOMAIN_METADATA_ELEMENT:
        virCheckNonNullArgGoto(uri, error);
        break;
    default:
        /* For future expansion */
        break;
    }

    conn = domain->conn;

    if (conn->driver->domainGetMetadata) {
        char *ret;
        if (!(ret = conn->driver->domainGetMetadata(domain, type, uri, flags)))
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainAttachDevice:
 * @domain: pointer to domain object
 * @xml: pointer to XML description of one device
 *
 * Create a virtual device attachment to backend.  This function,
 * having hotplug semantics, is only allowed on an active domain.
 *
 * For compatibility, this method can also be used to change the media
 * in an existing CDROM/Floppy device, however, applications are
 * recommended to use the virDomainUpdateDeviceFlag method instead.
 *
 * Be aware that hotplug changes might not persist across a domain going
 * into S4 state (also known as hibernation) unless you also modify the
 * persistent domain definition.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainAttachDevice(virDomainPtr domain, const char *xml)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xml=%s", xml);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(xml, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAttachDevice) {
       int ret;
       ret = conn->driver->domainAttachDevice(domain, xml);
       if (ret < 0)
          goto error;
       return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainAttachDeviceFlags:
 * @domain: pointer to domain object
 * @xml: pointer to XML description of one device
 * @flags: bitwise-OR of virDomainDeviceModifyFlags
 *
 * Attach a virtual device to a domain, using the flags parameter
 * to control how the device is attached.  VIR_DOMAIN_AFFECT_CURRENT
 * specifies that the device allocation is made based on current domain
 * state.  VIR_DOMAIN_AFFECT_LIVE specifies that the device shall be
 * allocated to the active domain instance only and is not added to the
 * persisted domain configuration.  VIR_DOMAIN_AFFECT_CONFIG
 * specifies that the device shall be allocated to the persisted domain
 * configuration only.  Note that the target hypervisor must return an
 * error if unable to satisfy flags.  E.g. the hypervisor driver will
 * return failure if LIVE is specified but it only supports modifying the
 * persisted device allocation.
 *
 * For compatibility, this method can also be used to change the media
 * in an existing CDROM/Floppy device, however, applications are
 * recommended to use the virDomainUpdateDeviceFlag method instead.
 *
 * Be aware that hotplug changes might not persist across a domain going
 * into S4 state (also known as hibernation) unless you also modify the
 * persistent domain definition.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainAttachDeviceFlags(virDomainPtr domain,
                           const char *xml, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xml=%s, flags=0x%x", xml, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(xml, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAttachDeviceFlags) {
        int ret;
        ret = conn->driver->domainAttachDeviceFlags(domain, xml, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainDetachDevice:
 * @domain: pointer to domain object
 * @xml: pointer to XML description of one device
 *
 * This is an equivalent of virDomainDetachDeviceFlags() when called with
 * @flags parameter set to VIR_DOMAIN_AFFECT_LIVE.
 *
 * See virDomainDetachDeviceFlags() for more details.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainDetachDevice(virDomainPtr domain, const char *xml)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xml=%s", xml);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(xml, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainDetachDevice) {
        int ret;
        ret = conn->driver->domainDetachDevice(domain, xml);
         if (ret < 0)
             goto error;
         return ret;
     }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainDetachDeviceFlags:
 * @domain: pointer to domain object
 * @xml: pointer to XML description of one device
 * @flags: bitwise-OR of virDomainDeviceModifyFlags
 *
 * Detach a virtual device from a domain, using the flags parameter
 * to control how the device is detached.  VIR_DOMAIN_AFFECT_CURRENT
 * specifies that the device allocation is removed based on current domain
 * state.  VIR_DOMAIN_AFFECT_LIVE specifies that the device shall be
 * deallocated from the active domain instance only and is not from the
 * persisted domain configuration.  VIR_DOMAIN_AFFECT_CONFIG
 * specifies that the device shall be deallocated from the persisted domain
 * configuration only.  Note that the target hypervisor must return an
 * error if unable to satisfy flags.  E.g. the hypervisor driver will
 * return failure if LIVE is specified but it only supports removing the
 * persisted device allocation.
 *
 * Some hypervisors may prevent this operation if there is a current
 * block job running operation on the device being detached; in that case,
 * use virDomainBlockJobAbort() to stop the block job first.
 *
 * Beware that depending on the hypervisor and device type, detaching a device
 * from a running domain may be asynchronous. That is, calling
 * virDomainDetachDeviceFlags may just request device removal while the device
 * is actually removed later (in cooperation with a guest OS). Previously,
 * this fact was ignored and the device could have been removed from domain
 * configuration before it was actually removed by the hypervisor causing
 * various failures on subsequent operations. To check whether the device was
 * successfully removed, either recheck domain configuration using
 * virDomainGetXMLDesc() or add a handler for the VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED
 * event. In case the device is already gone when virDomainDetachDeviceFlags
 * returns, the event is delivered before this API call ends. To help existing
 * clients work better in most cases, this API will try to transform an
 * asynchronous device removal that finishes shortly after the request into
 * a synchronous removal. In other words, this API may wait a bit for the
 * removal to complete in case it was not synchronous.
 *
 * Be aware that hotplug changes might not persist across a domain going
 * into S4 state (also known as hibernation) unless you also modify the
 * persistent domain definition.
 *
 * The supplied XML description of the device should be as specific
 * as its definition in the domain XML. The set of attributes used
 * to match the device are internal to the drivers. Using a partial definition,
 * or attempting to detach a device that is not present in the domain XML,
 * but shares some specific attributes with one that is present,
 * may lead to unexpected results.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainDetachDeviceFlags(virDomainPtr domain,
                           const char *xml, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xml=%s, flags=0x%x", xml, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(xml, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainDetachDeviceFlags) {
        int ret;
        ret = conn->driver->domainDetachDeviceFlags(domain, xml, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainUpdateDeviceFlags:
 * @domain: pointer to domain object
 * @xml: pointer to XML description of one device
 * @flags: bitwise-OR of virDomainDeviceModifyFlags
 *
 * Change a virtual device on a domain, using the flags parameter
 * to control how the device is changed.  VIR_DOMAIN_AFFECT_CURRENT
 * specifies that the device change is made based on current domain
 * state.  VIR_DOMAIN_AFFECT_LIVE specifies that the device shall be
 * changed on the active domain instance only and is not added to the
 * persisted domain configuration. VIR_DOMAIN_AFFECT_CONFIG
 * specifies that the device shall be changed on the persisted domain
 * configuration only.  Note that the target hypervisor must return an
 * error if unable to satisfy flags.  E.g. the hypervisor driver will
 * return failure if LIVE is specified but it only supports modifying the
 * persisted device allocation.
 *
 * This method is used for actions such changing CDROM/Floppy device
 * media, altering the graphics configuration such as password,
 * reconfiguring the NIC device backend connectivity, etc.
 *
 * The supplied XML description of the device should contain all
 * the information that is found in the corresponding domain XML.
 * Leaving out any piece of information may be treated as a
 * request for its removal, which may be denied. For instance,
 * when users want to change CDROM media only for live XML, they
 * must provide live disk XML as found in the corresponding live
 * domain XML with only the disk path changed.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainUpdateDeviceFlags(virDomainPtr domain,
                           const char *xml, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "xml=%s, flags=0x%x", xml, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(xml, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainUpdateDeviceFlags) {
        int ret;
        ret = conn->driver->domainUpdateDeviceFlags(domain, xml, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainDetachDeviceAlias:
 * @domain: pointer to a domain object
 * @alias: device alias
 * @flags: bitwise-OR of virDomainDeviceModifyFlags
 *
 * Detach a virtual device from a domain, using the alias to
 * specify the device. The value of @flags should be either
 * VIR_DOMAIN_AFFECT_CURRENT, or a bitwise-or of values from
 * VIR_DOMAIN_AFFECT_LIVE and VIR_DOMAIN_AFFECT_CURRENT, although
 * hypervisors vary in which flags are supported.
 *
 * In contrast to virDomainDetachDeviceFlags() this API is
 * asynchronous - it returns immediately after sending the detach
 * request to the hypervisor. It's caller's responsibility to
 * wait for VIR_DOMAIN_EVENT_ID_DEVICE_REMOVED event to signal
 * actual device removal or for
 * VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED to signal rejected
 * device removal.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virDomainDetachDeviceAlias(virDomainPtr domain,
                           const char *alias,
                           unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "alias=%s, flags=0x%x", alias, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(alias, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainDetachDeviceAlias) {
        int ret;
        ret = conn->driver->domainDetachDeviceAlias(domain, alias, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virConnectDomainEventRegister:
 * @conn: pointer to the connection
 * @cb: callback to the function handling domain events
 * @opaque: opaque data to pass on to the callback
 * @freecb: optional function to deallocate opaque when not used anymore
 *
 * Adds a callback to receive notifications of domain lifecycle events
 * occurring on a connection.  This function requires that an event loop
 * has been previously registered with virEventRegisterImpl() or
 * virEventRegisterDefaultImpl().
 *
 * Use of this method is no longer recommended. Instead applications
 * should try virConnectDomainEventRegisterAny() which has a more flexible
 * API contract.
 *
 * The virDomainPtr object handle passed into the callback upon delivery
 * of an event is only valid for the duration of execution of the callback.
 * If the callback wishes to keep the domain object after the callback returns,
 * it shall take a reference to it, by calling virDomainRef.
 * The reference can be released once the object is no longer required
 * by calling virDomainFree.
 *
 * Returns 0 on success, -1 on failure.  Older versions of some hypervisors
 * sometimes returned a positive number on success, but without any reliable
 * semantics on what that number represents.
 */
int
virConnectDomainEventRegister(virConnectPtr conn,
                              virConnectDomainEventCallback cb,
                              void *opaque,
                              virFreeCallback freecb)
{
    VIR_DEBUG("conn=%p, cb=%p, opaque=%p, freecb=%p", conn, cb, opaque, freecb);
    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(cb, error);

    if (conn->driver && conn->driver->connectDomainEventRegister) {
        int ret;
        ret = conn->driver->connectDomainEventRegister(conn, cb, opaque, freecb);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectDomainEventDeregister:
 * @conn: pointer to the connection
 * @cb: callback to the function handling domain events
 *
 * Removes a callback previously registered with the
 * virConnectDomainEventRegister() function.
 *
 * Use of this method is no longer recommended. Instead applications
 * should try virConnectDomainEventDeregisterAny() which has a more flexible
 * API contract
 *
 * Returns 0 on success, -1 on failure.  Older versions of some hypervisors
 * sometimes returned a positive number on success, but without any reliable
 * semantics on what that number represents.
 */
int
virConnectDomainEventDeregister(virConnectPtr conn,
                                virConnectDomainEventCallback cb)
{
    VIR_DEBUG("conn=%p, cb=%p", conn, cb);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(cb, error);

    if (conn->driver && conn->driver->connectDomainEventDeregister) {
        int ret;
        ret = conn->driver->connectDomainEventDeregister(conn, cb);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainIsActive:
 * @dom: pointer to the domain object
 *
 * Determine if the domain is currently running
 *
 * Returns 1 if running, 0 if inactive, -1 on error
 */
int
virDomainIsActive(virDomainPtr dom)
{
    VIR_DEBUG("dom=%p", dom);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    if (dom->conn->driver->domainIsActive) {
        int ret;
        ret = dom->conn->driver->domainIsActive(dom);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainIsPersistent:
 * @dom: pointer to the domain object
 *
 * Determine if the domain has a persistent configuration
 * which means it will still exist after shutting down
 *
 * Returns 1 if persistent, 0 if transient, -1 on error
 */
int
virDomainIsPersistent(virDomainPtr dom)
{
    VIR_DOMAIN_DEBUG(dom);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    if (dom->conn->driver->domainIsPersistent) {
        int ret;
        ret = dom->conn->driver->domainIsPersistent(dom);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainRename:
 * @dom: pointer to the domain object
 * @new_name: new domain name
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Rename a domain. New domain name is specified in the second
 * argument. Depending on each driver implementation it may be
 * required that domain is in a specific state.
 *
 * There might be some attributes and/or elements in domain XML that if no
 * value provided at XML defining time, libvirt will derive their value from
 * the domain name. These are not updated by this API. Users are strongly
 * advised to change these after the rename was successful.
 *
 * Returns 0 if successfully renamed, -1 on error
 */
int
virDomainRename(virDomainPtr dom,
                const char *new_name,
                unsigned int flags)
{
    VIR_DEBUG("dom=%p, new_name=%s", dom, NULLSTR(new_name));

    virResetLastError();
    virCheckDomainReturn(dom, -1);
    virCheckNonEmptyStringArgGoto(new_name, error);
    virCheckReadOnlyGoto(dom->conn->flags, error);

    if (dom->conn->driver->domainRename) {
        int ret = dom->conn->driver->domainRename(dom, new_name, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainIsUpdated:
 * @dom: pointer to the domain object
 *
 * Determine if the domain has been updated.
 *
 * Returns 1 if updated, 0 if not, -1 on error
 */
int
virDomainIsUpdated(virDomainPtr dom)
{
    VIR_DOMAIN_DEBUG(dom);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    if (dom->conn->driver->domainIsUpdated) {
        int ret;
        ret = dom->conn->driver->domainIsUpdated(dom);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetJobInfo:
 * @domain: a domain object
 * @info: pointer to a virDomainJobInfo structure allocated by the user
 *
 * Extract information about progress of a background job on a domain.
 * Will return an error if the domain is not active.
 *
 * This function returns a limited amount of information in comparison
 * to virDomainGetJobStats().
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetJobInfo(virDomainPtr domain, virDomainJobInfoPtr info)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "info=%p", info);

    virResetLastError();

    if (info)
        memset(info, 0, sizeof(*info));

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(info, error);

    conn = domain->conn;

    if (conn->driver->domainGetJobInfo) {
        int ret;
        ret = conn->driver->domainGetJobInfo(domain, info);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetJobStats:
 * @domain: a domain object
 * @type: where to store the job type (one of virDomainJobType)
 * @params: where to store job statistics
 * @nparams: number of items in @params
 * @flags: bitwise-OR of virDomainGetJobStatsFlags
 *
 * Extract information about progress of a background job on a domain.
 * Will return an error if the domain is not active. The function returns
 * a superset of progress information provided by virDomainGetJobInfo.
 * Possible fields returned in @params are defined by VIR_DOMAIN_JOB_*
 * macros and new fields will likely be introduced in the future so callers
 * may receive fields that they do not understand in case they talk to a
 * newer server.
 *
 * When @flags contains VIR_DOMAIN_JOB_STATS_COMPLETED, the function will
 * return statistics about a recently completed job. Specifically, this
 * flag may be used to query statistics of a completed incoming pre-copy
 * migration (statistics for post-copy migration are only available on the
 * source host). Statistics of a completed job are automatically destroyed
 * once read (unless the VIR_DOMAIN_JOB_STATS_COMPLETED_KEEP is used as well)
 * or when libvirtd is restarted. Note that time information
 * returned for completed migrations may be completely irrelevant unless both
 * source and destination hosts have synchronized time (i.e., NTP daemon is
 * running on both of them). The statistics of a completed job can also be
 * obtained by listening to a VIR_DOMAIN_EVENT_ID_JOB_COMPLETED event (on the
 * source host in case of a migration job).
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainGetJobStats(virDomainPtr domain,
                     int *type,
                     virTypedParameterPtr *params,
                     int *nparams,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "type=%p, params=%p, nparams=%p, flags=0x%x",
                     type, params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(type, error);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);
    VIR_REQUIRE_FLAG_GOTO(VIR_DOMAIN_JOB_STATS_KEEP_COMPLETED,
                          VIR_DOMAIN_JOB_STATS_COMPLETED,
                          error);

    conn = domain->conn;

    if (conn->driver->domainGetJobStats) {
        int ret;
        ret = conn->driver->domainGetJobStats(domain, type, params,
                                              nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainAbortJob:
 * @domain: a domain object
 *
 * Requests that the current background job be aborted at the
 * soonest opportunity. In case the job is a migration in a post-copy mode,
 * virDomainAbortJob will report an error (see virDomainMigrateStartPostCopy
 * for more details).
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainAbortJob(virDomainPtr domain)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAbortJob) {
        int ret;
        ret = conn->driver->domainAbortJob(domain);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateSetMaxDowntime:
 * @domain: a domain object
 * @downtime: maximum tolerable downtime for live migration, in milliseconds
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Sets maximum tolerable time for which the domain is allowed to be paused
 * at the end of live migration. It's supposed to be called while the domain is
 * being live-migrated as a reaction to migration progress.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateSetMaxDowntime(virDomainPtr domain,
                               unsigned long long downtime,
                               unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "downtime=%llu, flags=0x%x", downtime, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateSetMaxDowntime) {
        if (conn->driver->domainMigrateSetMaxDowntime(domain, downtime, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateGetMaxDowntime:
 * @domain: a domain object
 * @downtime: return value of the maximum tolerable downtime for live
 *            migration, in milliseconds
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Gets current maximum tolerable time for which the domain may be paused
 * at the end of live migration.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateGetMaxDowntime(virDomainPtr domain,
                               unsigned long long *downtime,
                               unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "downtime = %p, flags=0x%x", downtime, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(downtime, error);

    if (conn->driver->domainMigrateGetMaxDowntime) {
        if (conn->driver->domainMigrateGetMaxDowntime(domain, downtime, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateGetCompressionCache:
 * @domain: a domain object
 * @cacheSize: return value of current size of the cache (in bytes)
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Gets current size of the cache (in bytes) used for compressing repeatedly
 * transferred memory pages during live migration.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateGetCompressionCache(virDomainPtr domain,
                                    unsigned long long *cacheSize,
                                    unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cacheSize=%p, flags=0x%x", cacheSize, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(cacheSize, error);

    if (conn->driver->domainMigrateGetCompressionCache) {
        if (conn->driver->domainMigrateGetCompressionCache(domain, cacheSize,
                                                           flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateSetCompressionCache:
 * @domain: a domain object
 * @cacheSize: size of the cache (in bytes) used for compression
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Sets size of the cache (in bytes) used for compressing repeatedly
 * transferred memory pages during live migration. It's supposed to be called
 * while the domain is being live-migrated as a reaction to migration progress
 * and increasing number of compression cache misses obtained from
 * virDomainGetJobStats.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateSetCompressionCache(virDomainPtr domain,
                                    unsigned long long cacheSize,
                                    unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cacheSize=%llu, flags=0x%x", cacheSize, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateSetCompressionCache) {
        if (conn->driver->domainMigrateSetCompressionCache(domain, cacheSize,
                                                           flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateSetMaxSpeed:
 * @domain: a domain object
 * @bandwidth: migration bandwidth limit in MiB/s
 * @flags: bitwise-OR of virDomainMigrateMaxSpeedFlags
 *
 * The maximum bandwidth (in MiB/s) that will be used to do migration
 * can be specified with the bandwidth parameter. Not all hypervisors
 * will support a bandwidth cap. When VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY
 * is set in @flags, this API sets the maximum bandwidth for the post-copy
 * phase of the migration.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateSetMaxSpeed(virDomainPtr domain,
                            unsigned long bandwidth,
                            unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "bandwidth=%lu, flags=0x%x", bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateSetMaxSpeed) {
        if (conn->driver->domainMigrateSetMaxSpeed(domain, bandwidth, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateGetMaxSpeed:
 * @domain: a domain object
 * @bandwidth: return value of current migration bandwidth limit in MiB/s
 * @flags: bitwise-OR of virDomainMigrateMaxSpeedFlags
 *
 * Get the current maximum bandwidth (in MiB/s) that will be used if the
 * domain is migrated.  Not all hypervisors will support a bandwidth limit.
 * When VIR_DOMAIN_MIGRATE_MAX_SPEED_POSTCOPY is set in @flags, this API
 * gets the current maximum bandwidth for the post-copy phase of the
 * migration.
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateGetMaxSpeed(virDomainPtr domain,
                            unsigned long *bandwidth,
                            unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "bandwidth = %p, flags=0x%x", bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(bandwidth, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateGetMaxSpeed) {
        if (conn->driver->domainMigrateGetMaxSpeed(domain, bandwidth, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainMigrateStartPostCopy:
 * @domain: a domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Starts post-copy migration. This function has to be called while
 * migration (initiated with VIR_MIGRATE_POSTCOPY flag) is in progress.
 *
 * Traditional pre-copy migration iteratively walks through guest memory
 * pages and migrates those that changed since the previous iteration. The
 * iterative phase stops when the number of dirty pages is low enough so that
 * the virtual CPUs can be paused, all dirty pages transferred to the
 * destination, where the virtual CPUs are unpaused, and all this can happen
 * within a predefined downtime period. It's clear that this process may never
 * converge if downtime is too short and/or the guest keeps changing a lot of
 * memory pages.
 *
 * When migration is switched to post-copy mode, the virtual CPUs are paused
 * immediately, only a minimum set of pages is transferred, and the CPUs are
 * unpaused on destination. The source keeps sending all remaining memory pages
 * to the destination while the guest is already running there. Whenever the
 * guest tries to read a memory page which has not been migrated yet, the
 * hypervisor has to tell the source to transfer that page in a priority
 * channel. To minimize such page faults, it is a good idea to run at least one
 * iteration of pre-copy migration before switching to post-copy.
 *
 * Post-copy migration is guaranteed to converge since each page is transferred
 * at most once no matter how fast it changes. On the other hand once the
 * guest is running on the destination host, the migration can no longer be
 * rolled back because none of the hosts has complete state. If this happens,
 * libvirt will leave the domain paused on both hosts with
 * VIR_DOMAIN_PAUSED_POSTCOPY_FAILED reason. It's up to the upper layer to
 * decide what to do in such case. Because of this, libvirt will refuse to
 * cancel post-copy migration via virDomainAbortJob.
 *
 * The following domain life cycle events are emitted during post-copy
 * migration:
 *  VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY (on the source) -- migration entered
 *      post-copy mode.
 *  VIR_DOMAIN_EVENT_RESUMED_POSTCOPY (on the destination) -- the guest is
 *      running on the destination host while some of its memory pages still
 *      remain on the source host; neither the source nor the destination host
 *      contain a complete guest state from this point until migration
 *      finishes.
 *  VIR_DOMAIN_EVENT_RESUMED_MIGRATED (on the destination),
 *  VIR_DOMAIN_EVENT_STOPPED_MIGRATED (on the source) -- migration finished
 *      successfully and the destination host holds a complete guest state.
 *  VIR_DOMAIN_EVENT_SUSPENDED_POSTCOPY_FAILED (on the destination) -- emitted
 *      when migration fails in post-copy mode and it's unclear whether any
 *      of the hosts has a complete guest state.
 *
 * The progress of a post-copy migration can be monitored normally using
 * virDomainGetJobStats on the source host. Fetching statistics of a completed
 * post-copy migration can also be done on the source host (by calling
 * virDomainGetJobStats or listening to VIR_DOMAIN_EVENT_ID_JOB_COMPLETED
 * event, but (in contrast to pre-copy migration) the statistics are not
 * available on the destination host. Thus, VIR_DOMAIN_EVENT_ID_JOB_COMPLETED
 * event is the only way of getting statistics of a completed post-copy
 * migration of a transient domain (because the domain is removed after
 * migration and there's no domain to run virDomainGetJobStats on).
 *
 * Returns 0 in case of success, -1 otherwise.
 */
int
virDomainMigrateStartPostCopy(virDomainPtr domain,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainMigrateStartPostCopy) {
        if (conn->driver->domainMigrateStartPostCopy(domain, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectDomainEventRegisterAny:
 * @conn: pointer to the connection
 * @dom: pointer to the domain
 * @eventID: the event type to receive
 * @cb: callback to the function handling domain events
 * @opaque: opaque data to pass on to the callback
 * @freecb: optional function to deallocate opaque when not used anymore
 *
 * Adds a callback to receive notifications of arbitrary domain events
 * occurring on a domain.  This function requires that an event loop
 * has been previously registered with virEventRegisterImpl() or
 * virEventRegisterDefaultImpl().
 *
 * If @dom is NULL, then events will be monitored for any domain. If @dom
 * is non-NULL, then only the specific domain will be monitored.
 *
 * Most types of event have a callback providing a custom set of parameters
 * for the event. When registering an event, it is thus necessary to use
 * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer
 * to match the signature of this method.
 *
 * The virDomainPtr object handle passed into the callback upon delivery
 * of an event is only valid for the duration of execution of the callback.
 * If the callback wishes to keep the domain object after the callback returns,
 * it shall take a reference to it, by calling virDomainRef().
 * The reference can be released once the object is no longer required
 * by calling virDomainFree().
 *
 * The return value from this method is a positive integer identifier
 * for the callback. To unregister a callback, this callback ID should
 * be passed to the virConnectDomainEventDeregisterAny() method.
 *
 * Returns a callback identifier on success, -1 on failure.
 */
int
virConnectDomainEventRegisterAny(virConnectPtr conn,
                                 virDomainPtr dom,
                                 int eventID,
                                 virConnectDomainEventGenericCallback cb,
                                 void *opaque,
                                 virFreeCallback freecb)
{
    VIR_DOMAIN_DEBUG(dom, "conn=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p",
                     conn, eventID, cb, opaque, freecb);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    if (dom) {
        virCheckDomainGoto(dom, error);
        if (dom->conn != conn) {
            virReportInvalidArg(dom,
                                _("domain '%s' must match connection"),
                                dom->name);
            goto error;
        }
    }
    virCheckNonNullArgGoto(cb, error);
    virCheckNonNegativeArgGoto(eventID, error);
    if (eventID >= VIR_DOMAIN_EVENT_ID_LAST) {
        virReportInvalidArg(eventID,
                            _("eventID must be less than %d"),
                            VIR_DOMAIN_EVENT_ID_LAST);
        goto error;
    }

    if (conn->driver && conn->driver->connectDomainEventRegisterAny) {
        int ret;
        ret = conn->driver->connectDomainEventRegisterAny(conn, dom, eventID, cb, opaque, freecb);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virConnectDomainEventDeregisterAny:
 * @conn: pointer to the connection
 * @callbackID: the callback identifier
 *
 * Removes an event callback. The callbackID parameter should be the
 * value obtained from a previous virConnectDomainEventRegisterAny() method.
 *
 * Returns 0 on success, -1 on failure.  Older versions of some hypervisors
 * sometimes returned a positive number on success, but without any reliable
 * semantics on what that number represents. */
int
virConnectDomainEventDeregisterAny(virConnectPtr conn,
                                   int callbackID)
{
    VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNegativeArgGoto(callbackID, error);

    if (conn->driver && conn->driver->connectDomainEventDeregisterAny) {
        int ret;
        ret = conn->driver->connectDomainEventDeregisterAny(conn, callbackID);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainManagedSave:
 * @dom: pointer to the domain
 * @flags: bitwise-OR of virDomainSaveRestoreFlags
 *
 * This method will suspend a domain and save its memory contents to
 * a file on disk. After the call, if successful, the domain is not
 * listed as running anymore.
 * The difference from virDomainSave() is that libvirt is keeping track of
 * the saved state itself, and will reuse it once the domain is being
 * restarted (automatically or via an explicit libvirt call).
 * As a result any running domain is sure to not have a managed saved image.
 * This also implies that managed save only works on persistent domains,
 * since the domain must still exist in order to use virDomainCreate() to
 * restart it.
 *
 * If @flags includes VIR_DOMAIN_SAVE_BYPASS_CACHE, then libvirt will
 * attempt to bypass the file system cache while creating the file, or
 * fail if it cannot do so for the given system; this can allow less
 * pressure on file system cache, but also risks slowing saves to NFS.
 *
 * Normally, the managed saved state will remember whether the domain
 * was running or paused, and start will resume to the same state.
 * Specifying VIR_DOMAIN_SAVE_RUNNING or VIR_DOMAIN_SAVE_PAUSED in
 * @flags will override the default saved into the file.  These two
 * flags are mutually exclusive.
 *
 * Returns 0 in case of success or -1 in case of failure
 */
int
virDomainManagedSave(virDomainPtr dom, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SAVE_RUNNING,
                             VIR_DOMAIN_SAVE_PAUSED,
                             error);

    if (conn->driver->domainManagedSave) {
        int ret;

        ret = conn->driver->domainManagedSave(dom, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainHasManagedSaveImage:
 * @dom: pointer to the domain
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Check if a domain has a managed save image as created by
 * virDomainManagedSave(). Note that any running domain should not have
 * such an image, as it should have been removed on restart.
 *
 * Returns 0 if no image is present, 1 if an image is present, and
 *         -1 in case of error
 */
int
virDomainHasManagedSaveImage(virDomainPtr dom, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    if (conn->driver->domainHasManagedSaveImage) {
        int ret;

        ret = conn->driver->domainHasManagedSaveImage(dom, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainManagedSaveRemove:
 * @dom: pointer to the domain
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Remove any managed save image for this domain.
 *
 * Returns 0 in case of success, and -1 in case of error
 */
int
virDomainManagedSaveRemove(virDomainPtr dom, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainManagedSaveRemove) {
        int ret;

        ret = conn->driver->domainManagedSaveRemove(dom, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainManagedSaveGetXMLDesc:
 * @domain: a domain object
 * @flags: bitwise-OR of supported virDomainSaveImageXMLFlags
 *
 * This method will extract the XML description of the managed save
 * state file of a domain.
 *
 * No security-sensitive data will be included unless @flags contains
 * VIR_DOMAIN_SAVE_IMAGE_XML_SECURE; this flag is rejected on read-only
 * connections.
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case of
 * error.  The caller must free() the returned value.
 */
char *
virDomainManagedSaveGetXMLDesc(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    if ((conn->flags & VIR_CONNECT_RO) &&
        (flags & VIR_DOMAIN_SAVE_IMAGE_XML_SECURE)) {
        virReportError(VIR_ERR_OPERATION_DENIED, "%s",
                       _("virDomainManagedSaveGetXMLDesc with secure flag"));
        goto error;
    }

    if (conn->driver->domainManagedSaveGetXMLDesc) {
        char *ret;
        ret = conn->driver->domainManagedSaveGetXMLDesc(domain, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainManagedSaveDefineXML:
 * @domain: a domain object
 * @dxml: XML config for adjusting guest xml used on restore
 * @flags: bitwise-OR of virDomainSaveRestoreFlags
 *
 * This updates the definition of a domain stored in a saved state
 * file.
 *
 * @dxml can be used to alter host-specific portions of the domain XML
 * that will be used on the next start of the domain. For example, it is
 * possible to alter the backing filename that is associated with a
 * disk device.
 *
 * Normally, the saved state file will remember whether the domain was
 * running or paused, and restore defaults to the same state.
 * Specifying VIR_DOMAIN_SAVE_RUNNING or VIR_DOMAIN_SAVE_PAUSED in
 * @flags will override the default saved into the file; omitting both
 * leaves the file's default unchanged.  These two flags are mutually
 * exclusive.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virDomainManagedSaveDefineXML(virDomainPtr domain, const char *dxml,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SAVE_RUNNING,
                             VIR_DOMAIN_SAVE_PAUSED,
                             error);

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainManagedSaveDefineXML) {
        int ret;
        ret = conn->driver->domainManagedSaveDefineXML(domain, dxml, flags);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainOpenConsole:
 * @dom: a domain object
 * @dev_name: the console, serial or parallel port device alias, or NULL
 * @st: a stream to associate with the console
 * @flags: bitwise-OR of virDomainConsoleFlags
 *
 * This opens the backend associated with a console, serial or
 * parallel port device on a guest, if the backend is supported.
 * If the @dev_name is omitted, then the first console or serial
 * device is opened. The console is associated with the passed
 * in @st stream, which should have been opened in non-blocking
 * mode for bi-directional I/O.
 *
 * By default, when @flags is 0, the open will fail if libvirt
 * detects that the console is already in use by another client;
 * passing VIR_DOMAIN_CONSOLE_FORCE will cause libvirt to forcefully
 * remove the other client prior to opening this console.
 *
 * If flag VIR_DOMAIN_CONSOLE_SAFE the console is opened only in the
 * case where the hypervisor driver supports safe (mutually exclusive)
 * console handling.
 *
 * Older servers did not support either flag, and also did not forbid
 * simultaneous clients on a console, with potentially confusing results.
 * When passing @flags of 0 in order to support a wider range of server
 * versions, it is up to the client to ensure mutual exclusion.
 *
 * Returns 0 if the console was opened, -1 on error
 */
int
virDomainOpenConsole(virDomainPtr dom,
                     const char *dev_name,
                     virStreamPtr st,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "dev_name=%s, st=%p, flags=0x%x",
                     NULLSTR(dev_name), st, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckStreamGoto(st, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn != st->conn) {
        virReportInvalidArg(st,
                            _("stream must match connection of domain '%s'"),
                            dom->name);
        goto error;
    }

    if (conn->driver->domainOpenConsole) {
        int ret;
        ret = conn->driver->domainOpenConsole(dom, dev_name, st, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainOpenChannel:
 * @dom: a domain object
 * @name: the channel name, or NULL
 * @st: a stream to associate with the channel
 * @flags: bitwise-OR of virDomainChannelFlags
 *
 * This opens the host interface associated with a channel device on a
 * guest, if the host interface is supported.  If @name is given, it
 * can match either the device alias (e.g. "channel0"), or the virtio
 * target name (e.g. "org.qemu.guest_agent.0").  If @name is omitted,
 * then the first channel is opened. The channel is associated with
 * the passed in @st stream, which should have been opened in
 * non-blocking mode for bi-directional I/O.
 *
 * By default, when @flags is 0, the open will fail if libvirt detects
 * that the channel is already in use by another client; passing
 * VIR_DOMAIN_CHANNEL_FORCE will cause libvirt to forcefully remove the
 * other client prior to opening this channel.
 *
 * Returns 0 if the channel was opened, -1 on error
 */
int
virDomainOpenChannel(virDomainPtr dom,
                     const char *name,
                     virStreamPtr st,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "name=%s, st=%p, flags=0x%x",
                     NULLSTR(name), st, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckStreamGoto(st, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn != st->conn) {
        virReportInvalidArg(st,
                            _("stream must match connection of domain '%s'"),
                            dom->name);
        goto error;
    }

    if (conn->driver->domainOpenChannel) {
        int ret;
        ret = conn->driver->domainOpenChannel(dom, name, st, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainGetPerfEvents:
 * @domain: a domain object
 * @params: where to store perf events setting
 * @nparams: number of items in @params
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all Linux perf events setting. Possible fields returned in
 * @params are defined by VIR_PERF_EVENT_* macros and new fields
 * will likely be introduced in the future.
 *
 * Linux perf events are performance analyzing tool in Linux.
 *
 * Returns -1 in case of failure, 0 in case of success.
 */
int virDomainGetPerfEvents(virDomainPtr domain,
                           virTypedParameterPtr *params,
                           int *nparams,
                           unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p flags=0x%x",
                     params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);

    conn = domain->conn;

    if (conn->driver->domainGetPerfEvents) {
        int ret;
        ret = conn->driver->domainGetPerfEvents(domain, params,
                                                nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetPerfEvents:
 * @domain: a domain object
 * @params: pointer to perf events parameter object
 * @nparams: number of perf event parameters (this value can be the same
 *           less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Enable or disable the particular list of Linux perf events you
 * care about. The @params argument should contain any subset of
 * VIR_PERF_EVENT_ macros.
 *
 * Linux perf events are performance analyzing tool in Linux.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int virDomainSetPerfEvents(virDomainPtr domain,
                           virTypedParameterPtr params,
                           int nparams,
                           unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%d flags=0x%x",
                     params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(params, error);
    virCheckPositiveArgGoto(nparams, error);

    if (virTypedParameterValidateSet(conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetPerfEvents) {
        int ret;
        ret = conn->driver->domainSetPerfEvents(domain, params,
                                                nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainBlockJobAbort:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @flags: bitwise-OR of virDomainBlockJobAbortFlags
 *
 * Cancel the active block job on the given disk.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * If the current block job for @disk is VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, then
 * by default, this function performs a synchronous operation and the caller
 * may assume that the operation has completed when 0 is returned.  However,
 * BlockJob operations may take a long time to cancel, and during this time
 * further domain interactions may be unresponsive.  To avoid this problem,
 * pass VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC in the @flags argument to enable
 * asynchronous behavior, returning as soon as possible.  When the job has
 * been canceled, a BlockJob event will be emitted, with status
 * VIR_DOMAIN_BLOCK_JOB_CANCELED (even if the ABORT_ASYNC flag was not
 * used); it is also possible to poll virDomainBlockJobInfo() to see if
 * the job cancellation is still pending.  This type of job can be restarted
 * to pick up from where it left off.
 *
 * If the current block job for @disk is VIR_DOMAIN_BLOCK_JOB_TYPE_COPY, then
 * the default is to abort the mirroring and revert to the source disk;
 * likewise, if the current job is VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT,
 * the default is to abort without changing the active layer of @disk.
 * Adding @flags of VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT causes this call to
 * fail with VIR_ERR_BLOCK_COPY_ACTIVE if the copy or commit is not yet
 * ready; otherwise it will swap the disk over to the new active image
 * to end the mirroring or active commit.  An event will be issued when the
 * job is ended, and it is possible to use VIR_DOMAIN_BLOCK_JOB_ABORT_ASYNC
 * to control whether this command waits for the completion of the job.
 * Restarting a copy or active commit job requires starting over from the
 * beginning of the first phase.
 *
 * Returns -1 in case of failure, 0 when successful.
 */
int
virDomainBlockJobAbort(virDomainPtr dom, const char *disk,
                       unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, flags=0x%x", disk, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (conn->driver->domainBlockJobAbort) {
        int ret;
        ret = conn->driver->domainBlockJobAbort(dom, disk, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetBlockJobInfo:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @info: pointer to a virDomainBlockJobInfo structure
 * @flags: bitwise-OR of virDomainBlockJobInfoFlags
 *
 * Request block job information for the given disk.  If an operation is active
 * @info will be updated with the current progress.  The units used for the
 * bandwidth field of @info depends on @flags.  If @flags includes
 * VIR_DOMAIN_BLOCK_JOB_INFO_BANDWIDTH_BYTES, bandwidth is in bytes/second
 * (although this mode can risk failure due to overflow, depending on both
 * client and server word size); otherwise, the value is rounded up to MiB/s.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * In cases when libvirt can't determine actual progress of the block job from
 * the underlying hypervisor due to corner cases such as the job wasn't yet
 * fully initialized, or finalized and thus the progress can't be queried,
 * libvirt reports 'cur = 0, end = 1'.
 *
 * For jobs requiring finalizing via qemuDomainBlockJobAbort() with
 * VIR_DOMAIN_BLOCK_JOB_ABORT_PIVOT flag which reached synchronised phase, but
 * were empty, or the progress can't be determined libvirt returns
 * 'cur = 1, end = 1'.
 *
 * Users thus should not consider any data where 'end = 1' as absolute progress
 * value.
 *
 * Applications looking for a reliable and low-overhead way to determine whether
 * a block job already finished or reached synchronised phase should register a
 * handler for the VIR_DOMAIN_EVENT_ID_BLOCK_JOB_2 event instead of polling this
 * API.
 *
 * Note that the progress reported for blockjobs corresponding to a pull-mode
 * backup don't report progress of the backup but rather usage of temporary
 * space required for the backup.
 *
 * Returns -1 in case of failure, 0 when nothing found, 1 when info was found.
 */
int
virDomainGetBlockJobInfo(virDomainPtr dom, const char *disk,
                         virDomainBlockJobInfoPtr info, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, info=%p, flags=0x%x", disk, info, flags);

    virResetLastError();

    if (info)
        memset(info, 0, sizeof(*info));

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckNonNullArgGoto(disk, error);
    virCheckNonNullArgGoto(info, error);

    if (conn->driver->domainGetBlockJobInfo) {
        int ret;
        ret = conn->driver->domainGetBlockJobInfo(dom, disk, info, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockJobSetSpeed:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @bandwidth: specify bandwidth limit; flags determine the unit
 * @flags: bitwise-OR of virDomainBlockJobSetSpeedFlags
 *
 * Set the maximum allowable bandwidth that a block job may consume.  If
 * bandwidth is 0, the limit will revert to the hypervisor default of
 * unlimited.
 *
 * If @flags contains VIR_DOMAIN_BLOCK_JOB_SPEED_BANDWIDTH_BYTES, @bandwidth
 * is in bytes/second; otherwise, it is in MiB/second.  Values larger than
 * 2^52 bytes/sec may be rejected due to overflow considerations based on
 * the word size of both client and server, and values larger than 2^31
 * bytes/sec may cause overflow problems if later queried by
 * virDomainGetBlockJobInfo() without scaling.  Hypervisors may further
 * restrict the range of valid bandwidth values.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * Returns -1 in case of failure, 0 when successful.
 */
int
virDomainBlockJobSetSpeed(virDomainPtr dom, const char *disk,
                          unsigned long bandwidth, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, bandwidth=%lu, flags=0x%x",
                     disk, bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (conn->driver->domainBlockJobSetSpeed) {
        int ret;
        ret = conn->driver->domainBlockJobSetSpeed(dom, disk, bandwidth, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockPull:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @bandwidth: (optional) specify bandwidth limit; flags determine the unit
 * @flags: bitwise-OR of virDomainBlockPullFlags
 *
 * Populate a disk image with data from its backing image.  Once all data from
 * its backing image has been pulled, the disk no longer depends on a backing
 * image.  This function pulls data for the entire device in the background.
 * Progress of the operation can be checked with virDomainGetBlockJobInfo() and
 * the operation can be aborted with virDomainBlockJobAbort().  When finished,
 * an asynchronous event is raised to indicate the final status.  To move
 * data in the opposite direction, see virDomainBlockCommit().
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or (since 0.9.5) the device target shorthand
 * (the <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * The maximum bandwidth that will be used to do the copy can be
 * specified with the @bandwidth parameter.  If set to 0, there is no
 * limit.  If @flags includes VIR_DOMAIN_BLOCK_PULL_BANDWIDTH_BYTES,
 * @bandwidth is in bytes/second; otherwise, it is in MiB/second.
 * Values larger than 2^52 bytes/sec may be rejected due to overflow
 * considerations based on the word size of both client and server,
 * and values larger than 2^31 bytes/sec may cause overflow problems
 * if later queried by virDomainGetBlockJobInfo() without scaling.
 * Hypervisors may further restrict the range of valid bandwidth
 * values.  Some hypervisors do not support this feature and will
 * return an error if bandwidth is not 0; in this case, it might still
 * be possible for a later call to virDomainBlockJobSetSpeed() to
 * succeed.  The actual speed can be determined with
 * virDomainGetBlockJobInfo().
 *
 * This is shorthand for virDomainBlockRebase() with a NULL base.
 *
 * Returns 0 if the operation has started, -1 on failure.
 */
int
virDomainBlockPull(virDomainPtr dom, const char *disk,
                   unsigned long bandwidth, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, bandwidth=%lu, flags=0x%x",
                     disk, bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (conn->driver->domainBlockPull) {
        int ret;
        ret = conn->driver->domainBlockPull(dom, disk, bandwidth, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockRebase:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @base: path to backing file to keep, or device shorthand,
 *        or NULL for no backing file
 * @bandwidth: (optional) specify bandwidth limit; flags determine the unit
 * @flags: bitwise-OR of virDomainBlockRebaseFlags
 *
 * Populate a disk image with data from its backing image chain, and
 * setting the backing image to @base, or alternatively copy an entire
 * backing chain to a new file @base.
 *
 * When @flags is 0, this starts a pull, where @base must be the absolute
 * path of one of the backing images further up the chain, or NULL to
 * convert the disk image so that it has no backing image.  Once all
 * data from its backing image chain has been pulled, the disk no
 * longer depends on those intermediate backing images.  This function
 * pulls data for the entire device in the background.  Progress of
 * the operation can be checked with virDomainGetBlockJobInfo() with a
 * job type of VIR_DOMAIN_BLOCK_JOB_TYPE_PULL, and the operation can be
 * aborted with virDomainBlockJobAbort().  When finished, an asynchronous
 * event is raised to indicate the final status, and the job no longer
 * exists.  If the job is aborted, a new one can be started later to
 * resume from the same point.
 *
 * If @flags contains VIR_DOMAIN_BLOCK_REBASE_RELATIVE, the name recorded
 * into the active disk as the location for @base will be kept relative.
 * The operation will fail if libvirt can't infer the name.
 *
 * When @flags includes VIR_DOMAIN_BLOCK_REBASE_COPY, this starts a copy,
 * where @base must be the name of a new file to copy the chain to.  By
 * default, the copy will pull the entire source chain into the destination
 * file, but if @flags also contains VIR_DOMAIN_BLOCK_REBASE_SHALLOW, then
 * only the top of the source chain will be copied (the source and
 * destination have a common backing file).  By default, @base will be
 * created with the same file format as the source, but this can be altered
 * by adding VIR_DOMAIN_BLOCK_REBASE_COPY_RAW to force the copy to be raw
 * (does not make sense with the shallow flag unless the source is also raw),
 * or by using VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT to reuse an existing file
 * which was pre-created with the correct format and metadata and sufficient
 * size to hold the copy. In case the VIR_DOMAIN_BLOCK_REBASE_SHALLOW flag
 * is used the pre-created file has to exhibit the same guest visible contents
 * as the backing file of the original image. This allows a management app to
 * pre-create files with relative backing file names, rather than the default
 * of absolute backing file names; as a security precaution, you should
 * generally only use reuse_ext with the shallow flag and a non-raw
 * destination file.  By default, the copy destination will be treated as
 * type='file', but using VIR_DOMAIN_BLOCK_REBASE_COPY_DEV treats the
 * destination as type='block' (affecting how virDomainGetBlockInfo() will
 * report allocation after pivoting).
 *
 * A copy job has two parts; in the first phase, the @bandwidth parameter
 * affects how fast the source is pulled into the destination, and the job
 * can only be canceled by reverting to the source file; progress in this
 * phase can be tracked via the virDomainBlockJobInfo() command, with a
 * job type of VIR_DOMAIN_BLOCK_JOB_TYPE_COPY.  The job transitions to the
 * second phase when the job info states cur == end, and remains alive to
 * mirror all further changes to both source and destination.  The user
 * must call virDomainBlockJobAbort() to end the mirroring while choosing
 * whether to revert to source or pivot to the destination.  An event is
 * issued when the job ends, and depending on the hypervisor, an event may
 * also be issued when the job transitions from pulling to mirroring.  If
 * the job is aborted, a new job will have to start over from the beginning
 * of the first phase.
 *
 * Some hypervisors will restrict certain actions, such as virDomainSave()
 * or virDomainDetachDevice(), while a copy job is active; they may
 * also restrict a copy job to transient domains.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or the device target shorthand (the
 * <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * The @base parameter can be either a path to a file within the backing
 * chain, or the device target shorthand (the <target dev='...'/>
 * sub-element, such as "vda") followed by an index to the backing chain
 * enclosed in square brackets. Backing chain indexes can be found by
 * inspecting //disk//backingStore/@index in the domain XML. Thus, for
 * example, "vda[3]" refers to the backing store with index equal to "3"
 * in the chain of disk "vda".
 *
 * The maximum bandwidth that will be used to do the copy can be
 * specified with the @bandwidth parameter.  If set to 0, there is no
 * limit.  If @flags includes VIR_DOMAIN_BLOCK_REBASE_BANDWIDTH_BYTES,
 * @bandwidth is in bytes/second; otherwise, it is in MiB/second.
 * Values larger than 2^52 bytes/sec may be rejected due to overflow
 * considerations based on the word size of both client and server,
 * and values larger than 2^31 bytes/sec may cause overflow problems
 * if later queried by virDomainGetBlockJobInfo() without scaling.
 * Hypervisors may further restrict the range of valid bandwidth
 * values.  Some hypervisors do not support this feature and will
 * return an error if bandwidth is not 0; in this case, it might still
 * be possible for a later call to virDomainBlockJobSetSpeed() to
 * succeed.  The actual speed can be determined with
 * virDomainGetBlockJobInfo().
 *
 * When @base is NULL and @flags is 0, this is identical to
 * virDomainBlockPull().  When @flags contains VIR_DOMAIN_BLOCK_REBASE_COPY,
 * this command is shorthand for virDomainBlockCopy() where the destination
 * XML encodes @base as a <disk type='file'>, @bandwidth is properly scaled
 * and passed as a typed parameter, the shallow and reuse external flags
 * are preserved, and remaining flags control whether the XML encodes a
 * destination format of raw instead of leaving the destination identical
 * to the source format or probed from the reused file.
 *
 * Returns 0 if the operation has started, -1 on failure.
 */
int
virDomainBlockRebase(virDomainPtr dom, const char *disk,
                     const char *base, unsigned long bandwidth,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, base=%s, bandwidth=%lu, flags=0x%x",
                     disk, NULLSTR(base), bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (flags & VIR_DOMAIN_BLOCK_REBASE_COPY) {
        virCheckNonNullArgGoto(base, error);
    } else if (flags & (VIR_DOMAIN_BLOCK_REBASE_SHALLOW |
                        VIR_DOMAIN_BLOCK_REBASE_REUSE_EXT |
                        VIR_DOMAIN_BLOCK_REBASE_COPY_RAW |
                        VIR_DOMAIN_BLOCK_REBASE_COPY_DEV)) {
        virReportInvalidArg(flags, "%s",
                            _("use of flags requires a copy job"));
        goto error;
    }

    if (conn->driver->domainBlockRebase) {
        int ret;
        ret = conn->driver->domainBlockRebase(dom, disk, base, bandwidth,
                                              flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockCopy:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @destxml: XML description of the copy destination
 * @params: Pointer to block copy parameter objects, or NULL
 * @nparams: Number of block copy parameters (this value can be the same or
 *           less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainBlockCopyFlags
 *
 * Copy the guest-visible contents of a disk image to a new file described
 * by @destxml.  The destination XML has a top-level element of <disk>, and
 * resembles what is used when hot-plugging a disk via virDomainAttachDevice(),
 * except that only sub-elements related to describing the new host resource
 * are necessary (sub-elements related to the guest view, such as <target>,
 * are ignored).  It is strongly recommended to include a <driver type='...'/>
 * format designation for the destination, to avoid the potential of any
 * security problem that might be caused by probing a file for its format.
 *
 * This command starts a long-running copy.  By default, the copy will pull
 * the entire source chain into the destination file, but if @flags also
 * contains VIR_DOMAIN_BLOCK_COPY_SHALLOW, then only the top of the source
 * chain will be copied (the source and destination have a common backing
 * file).  The format of the destination file is controlled by the <driver>
 * sub-element of the XML.  The destination will be created unless the
 * VIR_DOMAIN_BLOCK_COPY_REUSE_EXT flag is present stating that the file
 * was pre-created with the correct format and metadata and sufficient
 * size to hold the copy. In case the VIR_DOMAIN_BLOCK_COPY_SHALLOW flag
 * is used the pre-created file has to exhibit the same guest visible contents
 * as the backing file of the original image. This allows a management app to
 * pre-create files with relative backing file names, rather than the default
 * of absolute backing file names.
 *
 * A copy job has two parts; in the first phase, the source is copied into
 * the destination, and the job can only be canceled by reverting to the
 * source file; progress in this phase can be tracked via the
 * virDomainBlockJobInfo() command, with a job type of
 * VIR_DOMAIN_BLOCK_JOB_TYPE_COPY.  The job transitions to the second
 * phase when the block job event with state VIR_DOMAIN_BLOCK_JOB_READY is
 * emitted for the given device. This information is also visible in the
 * live XML as 'ready="yes"' attribute of the corresponding <mirror> element.
 * All further changes are saved to both source and destination.  The user must
 * call virDomainBlockJobAbort() to end the mirroring while choosing
 * whether to revert to source or pivot to the destination.  An event is
 * issued when the job ends, and depending on the hypervisor, an event may
 * also be issued when the job transitions from pulling to mirroring.  If
 * the job is aborted, a new job will have to start over from the beginning
 * of the first phase.
 *
 * Some hypervisors will restrict certain actions, such as virDomainSave()
 * or virDomainDetachDevice(), while a copy job is active; they may
 * also restrict a copy job to transient domains.
 *
 * If @flags contains VIR_DOMAIN_BLOCK_COPY_TRANSIENT_JOB the job will not be
 * recoverable if the VM is turned off while job is active. This flag will
 * remove the restriction of copy jobs to transient domains. Note that this flag
 * is automatically implied if the VM is transient at the time it's started.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or the device target shorthand (the
 * <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * The @params and @nparams arguments can be used to set hypervisor-specific
 * tuning parameters, such as maximum bandwidth or granularity.  For a
 * parameter that the hypervisor understands, explicitly specifying 0
 * behaves the same as omitting the parameter, to use the hypervisor
 * default; however, omitting a parameter is less likely to fail.
 *
 * This command is a superset of the older virDomainBlockRebase() when used
 * with the VIR_DOMAIN_BLOCK_REBASE_COPY flag, and offers better control
 * over the destination format, the ability to copy to a destination that
 * is not a local file, and the possibility of additional tuning parameters.
 *
 * Returns 0 if the operation has started, -1 on failure.
 */
int
virDomainBlockCopy(virDomainPtr dom, const char *disk,
                   const char *destxml,
                   virTypedParameterPtr params,
                   int nparams,
                   unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom,
                     "disk=%s, destxml=%s, params=%p, nparams=%d, flags=0x%x",
                     disk, destxml, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);
    virCheckNonNullArgGoto(destxml, error);
    virCheckNonNegativeArgGoto(nparams, error);
    if (nparams)
        virCheckNonNullArgGoto(params, error);

    if (conn->driver->domainBlockCopy) {
        int ret;
        ret = conn->driver->domainBlockCopy(dom, disk, destxml,
                                            params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainBlockCommit:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @base: path to backing file to merge into, or device shorthand,
 *        or NULL for default
 * @top: path to file within backing chain that contains data to be merged,
 *       or device shorthand, or NULL to merge all possible data
 * @bandwidth: (optional) specify bandwidth limit; flags determine the unit
 * @flags: bitwise-OR of virDomainBlockCommitFlags
 *
 * Commit changes that were made to temporary top-level files within a disk
 * image backing file chain into a lower-level base file.  In other words,
 * take all the difference between @base and @top, and update @base to contain
 * that difference; after the commit, any portion of the chain that previously
 * depended on @top will now depend on @base, and all files after @base up
 * to and including @top will now be invalidated.  A typical use of this
 * command is to reduce the length of a backing file chain after taking an
 * external disk snapshot.  To move data in the opposite direction, see
 * virDomainBlockPull().
 *
 * This command starts a long-running commit block job, whose status may
 * be tracked by virDomainBlockJobInfo() with a job type of
 * VIR_DOMAIN_BLOCK_JOB_TYPE_COMMIT, and the operation can be aborted with
 * virDomainBlockJobAbort().  When finished, an asynchronous event is
 * raised to indicate the final status, and the job no longer exists.  If
 * the job is aborted, it is up to the hypervisor whether starting a new
 * job will resume from the same point, or start over.
 *
 * As a special case, if @top is the active image (or NULL), and @flags
 * includes VIR_DOMAIN_BLOCK_COMMIT_ACTIVE, the block job will have a type
 * of VIR_DOMAIN_BLOCK_JOB_TYPE_ACTIVE_COMMIT, and operates in two phases.
 * In the first phase, the contents are being committed into @base, and the
 * job can only be canceled.  The job transitions to the second phase when
 * the block job event with state VIR_DOMAIN_BLOCK_JOB_READY is
 * emitted for the given device. This information is also visible in the
 * live XML as 'ready="yes"' attribute of the corresponding <mirror> element.
 * Once in the second phase, the user must choose whether to cancel the job
 * (keeping @top as the active image, but now containing only the changes
 * since the time the job ended) or to pivot the job (adjusting to @base as
 * the active image, and invalidating @top).
 *
 * Be aware that this command may invalidate files even if it is aborted;
 * the user is cautioned against relying on the contents of invalidated
 * intermediate files such as @top (when @top is not the active image)
 * without manually rebasing those files to use a backing file of a
 * read-only copy of @base prior to the point where the commit operation
 * was started (and such a rebase cannot be safely done until the commit
 * has successfully completed).  However, the domain itself will not have
 * any issues; the active layer remains valid throughout the entire commit
 * operation.
 *
 * Some hypervisors may support a shortcut where if @flags contains
 * VIR_DOMAIN_BLOCK_COMMIT_DELETE, then this command will unlink all files
 * that were invalidated, after the commit successfully completes.
 *
 * If @flags contains VIR_DOMAIN_BLOCK_COMMIT_RELATIVE, the name recorded
 * into the overlay of the @top image (if there is such image) as the
 * path to the new backing file will be kept relative to other images.
 * The operation will fail if libvirt can't infer the name.
 *
 * By default, if @base is NULL, the commit target will be the bottom of
 * the backing chain; if @flags contains VIR_DOMAIN_BLOCK_COMMIT_SHALLOW,
 * then the immediate backing file of @top will be used instead.  If @top
 * is NULL, the active image at the top of the chain will be used.  Some
 * hypervisors place restrictions on how much can be committed, and might
 * fail if @base is not the immediate backing file of @top, or if @top is
 * the active layer in use by a running domain but @flags did not include
 * VIR_DOMAIN_BLOCK_COMMIT_ACTIVE, or if @top is not the top-most file;
 * restrictions may differ for online vs. offline domains.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or the device target shorthand (the
 * <target dev='...'/> sub-element, such as "vda").  Valid names
 * can be found by calling virDomainGetXMLDesc() and inspecting
 * elements within //domain/devices/disk.
 *
 * The @base and @top parameters can be either paths to files within the
 * backing chain, or the device target shorthand (the <target dev='...'/>
 * sub-element, such as "vda") followed by an index to the backing chain
 * enclosed in square brackets. Backing chain indexes can be found by
 * inspecting //disk//backingStore/@index in the domain XML. Thus, for
 * example, "vda[3]" refers to the backing store with index equal to "3"
 * in the chain of disk "vda".
 *
 * The maximum bandwidth that will be used to do the commit can be
 * specified with the @bandwidth parameter.  If set to 0, there is no
 * limit.  If @flags includes VIR_DOMAIN_BLOCK_COMMIT_BANDWIDTH_BYTES,
 * @bandwidth is in bytes/second; otherwise, it is in MiB/second.
 * Values larger than 2^52 bytes/sec may be rejected due to overflow
 * considerations based on the word size of both client and server,
 * and values larger than 2^31 bytes/sec may cause overflow problems
 * if later queried by virDomainGetBlockJobInfo() without scaling.
 * Hypervisors may further restrict the range of valid bandwidth
 * values.  Some hypervisors do not support this feature and will
 * return an error if bandwidth is not 0; in this case, it might still
 * be possible for a later call to virDomainBlockJobSetSpeed() to
 * succeed.  The actual speed can be determined with
 * virDomainGetBlockJobInfo().
 *
 * Returns 0 if the operation has started, -1 on failure.
 */
int
virDomainBlockCommit(virDomainPtr dom, const char *disk,
                     const char *base, const char *top,
                     unsigned long bandwidth, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, base=%s, top=%s, bandwidth=%lu, flags=0x%x",
                     disk, NULLSTR(base), NULLSTR(top), bandwidth, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);

    if (conn->driver->domainBlockCommit) {
        int ret;
        ret = conn->driver->domainBlockCommit(dom, disk, base, top, bandwidth,
                                              flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainOpenGraphics:
 * @dom: pointer to domain object
 * @idx: index of graphics config to open
 * @fd: file descriptor to attach graphics to
 * @flags: bitwise-OR of virDomainOpenGraphicsFlags
 *
 * This will attempt to connect the file descriptor @fd, to
 * the graphics backend of @dom. If @dom has multiple graphics
 * backends configured, then @idx will determine which one is
 * opened, starting from @idx 0.
 *
 * To disable any authentication, pass the VIR_DOMAIN_OPEN_GRAPHICS_SKIPAUTH
 * constant for @flags.
 *
 * The caller should use an anonymous socketpair to open
 * @fd before invocation.
 *
 * This method can only be used when connected to a local
 * libvirt hypervisor, over a UNIX domain socket. Attempts
 * to use this method over a TCP connection will always fail
 *
 * Returns 0 on success, -1 on failure
 */
int
virDomainOpenGraphics(virDomainPtr dom,
                      unsigned int idx,
                      int fd,
                      unsigned int flags)
{
    struct stat sb;
    int rc;

    VIR_DOMAIN_DEBUG(dom, "idx=%u, fd=%d, flags=0x%x",
                     idx, fd, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckNonNegativeArgGoto(fd, error);

    if (fstat(fd, &sb) < 0) {
        virReportSystemError(errno,
                             _("Unable to access file descriptor %d"), fd);
        goto error;
    }

#ifndef WIN32
    if (!S_ISSOCK(sb.st_mode)) {
        virReportInvalidArg(fd,
                            _("fd %d must be a socket"),
                            fd);
        goto error;
    }
#endif /* !WIN32 */

    virCheckReadOnlyGoto(dom->conn->flags, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(dom->conn->driver, dom->conn,
                                  VIR_DRV_FEATURE_FD_PASSING);
    if (rc <= 0) {
        if (rc == 0)
            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                           _("fd passing is not supported by this connection"));
        goto error;
    }

    if (dom->conn->driver->domainOpenGraphics) {
        int ret;
        ret = dom->conn->driver->domainOpenGraphics(dom, idx, fd, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainOpenGraphicsFD:
 * @dom: pointer to domain object
 * @idx: index of graphics config to open
 * @flags: bitwise-OR of virDomainOpenGraphicsFlags
 *
 * This will create a socket pair connected to the graphics backend of @dom.
 * One end of the socket will be returned on success, and the other end is
 * handed to the hypervisor.
 * If @dom has multiple graphics backends configured, then @idx will determine
 * which one is opened, starting from @idx 0.
 *
 * To disable any authentication, pass the VIR_DOMAIN_OPEN_GRAPHICS_SKIPAUTH
 * constant for @flags.
 *
 * This method can only be used when connected to a local
 * libvirt hypervisor, over a UNIX domain socket. Attempts
 * to use this method over a TCP connection will always fail.
 *
 * Returns an fd on success, -1 on failure
 */
int
virDomainOpenGraphicsFD(virDomainPtr dom,
                        unsigned int idx,
                        unsigned int flags)
{
    int rc;

    VIR_DOMAIN_DEBUG(dom, "idx=%u, flags=0x%x", idx, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    virCheckReadOnlyGoto(dom->conn->flags, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(dom->conn->driver, dom->conn,
                                  VIR_DRV_FEATURE_FD_PASSING);

    if (rc <= 0) {
        if (rc == 0)
            virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
                           _("fd passing is not supported by this connection"));
        goto error;
    }

    if (dom->conn->driver->domainOpenGraphicsFD) {
        int ret;
        ret = dom->conn->driver->domainOpenGraphicsFD(dom, idx, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainSetBlockIoTune:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @params: Pointer to blkio parameter objects
 * @nparams: Number of blkio parameters (this value can be the same or
 *           less than the number of parameters supported)
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Change all or a subset of the per-device block IO tunables.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or the device target shorthand (the <target
 * dev='...'/> sub-element, such as "xvda").  Valid names can be found
 * by calling virDomainGetXMLDesc() and inspecting elements
 * within //domain/devices/disk.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainSetBlockIoTune(virDomainPtr dom,
                        const char *disk,
                        virTypedParameterPtr params,
                        int nparams,
                        unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, params=%p, nparams=%d, flags=0x%x",
                     disk, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    conn = dom->conn;

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonNullArgGoto(disk, error);
    virCheckPositiveArgGoto(nparams, error);
    virCheckNonNullArgGoto(params, error);

    if (virTypedParameterValidateSet(dom->conn, params, nparams) < 0)
        goto error;

    if (conn->driver->domainSetBlockIoTune) {
        int ret;
        ret = conn->driver->domainSetBlockIoTune(dom, disk, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetBlockIoTune:
 * @dom: pointer to domain object
 * @disk: path to the block device, or device shorthand
 * @params: Pointer to blkio parameter object
 *          (return value, allocated by the caller)
 * @nparams: Pointer to number of blkio parameters
 * @flags: bitwise-OR of virDomainModificationImpact and virTypedParameterFlags
 *
 * Get all block IO tunable parameters for a given device.  On input,
 * @nparams gives the size of the @params array; on output, @nparams
 * gives how many slots were filled with parameter information, which
 * might be less but will not exceed the input value.
 *
 * As a special case, calling with @params as NULL and @nparams as 0
 * on input will cause @nparams on output to contain the number of
 * parameters supported by the hypervisor, either for the given @disk
 * (note that block devices of different types might support different
 * parameters), or if @disk is NULL, for all possible disks. The
 * caller should then allocate @params array,
 * i.e. (sizeof(@virTypedParameter) * @nparams) bytes and call the API
 * again.  See virDomainGetMemoryParameters() for more details.
 *
 * The @disk parameter is either an unambiguous source name of the
 * block device (the <source file='...'/> sub-element, such as
 * "/path/to/image"), or the device target shorthand (the <target
 * dev='...'/> sub-element, such as "xvda").  Valid names can be found
 * by calling virDomainGetXMLDesc() and inspecting elements
 * within //domain/devices/disk.  This parameter cannot be NULL
 * unless @nparams is 0 on input.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virDomainGetBlockIoTune(virDomainPtr dom,
                        const char *disk,
                        virTypedParameterPtr params,
                        int *nparams,
                        unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(dom, "disk=%s, params=%p, nparams=%d, flags=0x%x",
                     NULLSTR(disk), params, (nparams) ? *nparams : -1, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (*nparams != 0) {
        virCheckNonNullArgGoto(params, error);
        virCheckNonNullArgGoto(disk, error);
    }

    rc = VIR_DRV_SUPPORTS_FEATURE(dom->conn->driver, dom->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_AFFECT_LIVE,
                             VIR_DOMAIN_AFFECT_CONFIG,
                             error);

    conn = dom->conn;

    if (conn->driver->domainGetBlockIoTune) {
        int ret;
        ret = conn->driver->domainGetBlockIoTune(dom, disk, params, nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetCPUStats:
 * @domain: domain to query
 * @params: array to populate on output
 * @nparams: number of parameters per cpu
 * @start_cpu: which cpu to start with, or -1 for summary
 * @ncpus: how many cpus to query
 * @flags: bitwise-OR of virTypedParameterFlags
 *
 * Get statistics relating to CPU usage attributable to a single
 * domain (in contrast to the statistics returned by
 * virNodeGetCPUStats() for all processes on the host).  @dom
 * must be running (an inactive domain has no attributable cpu
 * usage).  On input, @params must contain at least @nparams * @ncpus
 * entries, allocated by the caller.
 *
 * If @start_cpu is -1, then @ncpus must be 1, and the returned
 * results reflect the statistics attributable to the entire
 * domain (such as user and system time for the process as a
 * whole).  Otherwise, @start_cpu represents which cpu to start
 * with, and @ncpus represents how many consecutive processors to
 * query, with statistics attributable per processor (such as
 * per-cpu usage).  If @ncpus is larger than the number of cpus
 * available to query, then the trailing part of the array will
 * be unpopulated.
 *
 * The remote driver imposes a limit of 128 @ncpus and 16 @nparams;
 * the number of parameters per cpu should not exceed 16, but if you
 * have a host with more than 128 CPUs, your program should split
 * the request into multiple calls.
 *
 * As special cases, if @params is NULL and @nparams is 0 and
 * @ncpus is 1, and the return value will be how many
 * statistics are available for the given @start_cpu.  This number
 * may be different for @start_cpu of -1 than for any non-negative
 * value, but will be the same for all non-negative @start_cpu.
 * Likewise, if @params is NULL and @nparams is 0 and @ncpus is 0,
 * the number of cpus available to query is returned.  From the
 * host perspective, this would typically match the cpus member
 * of virNodeGetInfo(), but might be less due to host cpu hotplug.
 *
 * For now, @flags is unused, and the statistics all relate to the
 * usage from the host perspective.  It is possible that a future
 * version will support a flag that queries the cpu usage from the
 * guest's perspective, where the maximum cpu to query would be
 * related to virDomainGetVcpusFlags() rather than virNodeGetInfo().
 * An individual guest vcpu cannot be reliably mapped back to a
 * specific host cpu unless a single-processor vcpu pinning was used,
 * but when @start_cpu is -1, any difference in usage between a host
 * and guest perspective would serve as a measure of hypervisor overhead.
 *
 * Typical use sequence is below.
 *
 * getting total stats: set start_cpu as -1, ncpus 1
 *
 *   virDomainGetCPUStats(dom, NULL, 0, -1, 1, 0); // nparams
 *   params = calloc(nparams, sizeof(virTypedParameter))
 *   virDomainGetCPUStats(dom, params, nparams, -1, 1, 0); // total stats.
 *
 * getting per-cpu stats:
 *
 *   virDomainGetCPUStats(dom, NULL, 0, 0, 0, 0); // ncpus
 *   virDomainGetCPUStats(dom, NULL, 0, 0, 1, 0); // nparams
 *   params = calloc(ncpus * nparams, sizeof(virTypedParameter));
 *   virDomainGetCPUStats(dom, params, nparams, 0, ncpus, 0); // per-cpu stats
 *
 * Returns -1 on failure, or the number of statistics that were
 * populated per cpu on success (this will be less than the total
 * number of populated @params, unless @ncpus was 1; and may be
 * less than @nparams).  The populated parameters start at each
 * stride of @nparams, which means the results may be discontiguous;
 * any unpopulated parameters will be zeroed on success (this includes
 * skipped elements if @nparams is too large, and tail elements if
 * @ncpus is too large).  The caller is responsible for freeing any
 * returned string parameters.
 */
int
virDomainGetCPUStats(virDomainPtr domain,
                     virTypedParameterPtr params,
                     unsigned int nparams,
                     int start_cpu,
                     unsigned int ncpus,
                     unsigned int flags)
{
    virConnectPtr conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain,
                     "params=%p, nparams=%d, start_cpu=%d, ncpus=%u, flags=0x%x",
                     params, nparams, start_cpu, ncpus, flags);
    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    /* Special cases:
     * start_cpu must be non-negative, or else -1
     * if start_cpu is -1, ncpus must be 1
     * params == NULL must match nparams == 0
     * ncpus must be non-zero unless params == NULL
     * nparams * ncpus must not overflow (RPC may restrict it even more)
     */
    if (start_cpu == -1) {
        if (ncpus != 1) {
            virReportInvalidArg(start_cpu, "%s",
                                _("ncpus must be 1 when start_cpu is -1"));
            goto error;
        }
    } else {
        virCheckNonNegativeArgGoto(start_cpu, error);
    }
    if (nparams)
        virCheckNonNullArgGoto(params, error);
    else
        virCheckNullArgGoto(params, error);
    if (ncpus == 0)
        virCheckNullArgGoto(params, error);

    if (nparams && ncpus > UINT_MAX / nparams) {
        virReportError(VIR_ERR_OVERFLOW, _("input too large: %u * %u"),
                       nparams, ncpus);
        goto error;
    }
    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    if (conn->driver->domainGetCPUStats) {
        int ret;

        ret = conn->driver->domainGetCPUStats(domain, params, nparams,
                                              start_cpu, ncpus, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainGetDiskErrors:
 * @dom: a domain object
 * @errors: array to populate on output
 * @maxerrors: size of @errors array
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * The function populates @errors array with all disks that encountered an
 * I/O error.  Disks with no error will not be returned in the @errors array.
 * Each disk is identified by its target (the dev attribute of target
 * subelement in domain XML), such as "vda", and accompanied with the error
 * that was seen on it.  The caller is also responsible for calling free()
 * on each disk name returned.
 *
 * In a special case when @errors is NULL and @maxerrors is 0, the function
 * returns preferred size of @errors that the caller should use to get all
 * disk errors.
 *
 * Since calling virDomainGetDiskErrors(dom, NULL, 0, 0) to get preferred size
 * of @errors array and getting the errors are two separate operations, new
 * disks may be hotplugged to the domain and new errors may be encountered
 * between the two calls.  Thus, this function may not return all disk errors
 * because the supplied array is not large enough.  Such errors may, however,
 * be detected by listening to domain events.
 *
 * Returns number of disks with errors filled in the @errors array or -1 on
 * error.
 */
int
virDomainGetDiskErrors(virDomainPtr dom,
                       virDomainDiskErrorPtr errors,
                       unsigned int maxerrors,
                       unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "errors=%p, maxerrors=%u, flags=0x%x",
                     errors, maxerrors, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);

    virCheckNonNullArrayArgGoto(errors, maxerrors, error);

    if (dom->conn->driver->domainGetDiskErrors) {
        int ret = dom->conn->driver->domainGetDiskErrors(dom, errors,
                                                         maxerrors, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainGetHostname:
 * @domain: a domain object
 * @flags: bitwise-OR of virDomainGetHostnameFlags
 *
 * Get the hostname for that domain. If no hostname is found,
 * then an error is raised with VIR_ERR_NO_HOSTNAME code.
 *
 * Dependent on hypervisor and @flags used, this may require a
 * guest agent to be available.
 *
 * Returns the hostname which must be freed by the caller, or
 * NULL if there was an error.
 */
char *
virDomainGetHostname(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    virCheckReadOnlyGoto(domain->conn->flags, error);

    if (conn->driver->domainGetHostname) {
        char *ret;
        ret = conn->driver->domainGetHostname(domain, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return NULL;
}


/**
 * virDomainFSTrim:
 * @dom: a domain object
 * @mountPoint: which mount point to trim
 * @minimum: Minimum contiguous free range to discard in bytes
 * @flags: extra flags, not used yet, so callers should always pass 0
 *
 * Calls FITRIM within the guest (hence guest agent may be
 * required depending on hypervisor used). Either call it on each
 * mounted filesystem (@mountPoint is NULL) or just on specified
 * @mountPoint. @minimum hints that free ranges smaller than this
 * may be ignored (this is a hint and the guest may not respect
 * it).  By increasing this value, the fstrim operation will
 * complete more quickly for filesystems with badly fragmented
 * free space, although not all blocks will be discarded.
 * If @minimum is not zero, the command may fail.
 *
 * Returns 0 on success, -1 otherwise.
 */
int
virDomainFSTrim(virDomainPtr dom,
                const char *mountPoint,
                unsigned long long minimum,
                unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "mountPoint=%s, minimum=%llu, flags=0x%x",
                     mountPoint, minimum, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);

    if (dom->conn->driver->domainFSTrim) {
        int ret = dom->conn->driver->domainFSTrim(dom, mountPoint,
                                                  minimum, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainFSFreeze:
 * @dom: a domain object
 * @mountpoints: list of mount points to be frozen
 * @nmountpoints: the number of mount points specified in @mountpoints
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Freeze specified filesystems within the guest (hence guest agent
 * may be required depending on hypervisor used). If @mountpoints is NULL and
 * @nmountpoints is 0, every mounted filesystem on the guest is frozen.
 * In some environments (e.g. QEMU guest with guest agent which doesn't
 * support mountpoints argument), @mountpoints may need to be NULL.
 *
 * Returns the number of frozen filesystems on success, -1 otherwise.
 */
int
virDomainFSFreeze(virDomainPtr dom,
                  const char **mountpoints,
                  unsigned int nmountpoints,
                  unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "mountpoints=%p, nmountpoints=%d, flags=0x%x",
                     mountpoints, nmountpoints, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);
    virCheckNonNullArrayArgGoto(mountpoints, nmountpoints, error);

    if (dom->conn->driver->domainFSFreeze) {
        int ret = dom->conn->driver->domainFSFreeze(
            dom, mountpoints, nmountpoints, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainFSThaw:
 * @dom: a domain object
 * @mountpoints: list of mount points to be thawed
 * @nmountpoints: the number of mount points specified in @mountpoints
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Thaw specified filesystems within the guest. If @mountpoints is NULL and
 * @nmountpoints is 0, every mounted filesystem on the guest is thawed.
 * In some drivers (e.g. QEMU driver), @mountpoints may need to be NULL.
 *
 * Returns the number of thawed filesystems on success, -1 otherwise.
 */
int
virDomainFSThaw(virDomainPtr dom,
                const char **mountpoints,
                unsigned int nmountpoints,
                unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);
    virCheckNonNullArrayArgGoto(mountpoints, nmountpoints, error);

    if (dom->conn->driver->domainFSThaw) {
        int ret = dom->conn->driver->domainFSThaw(
            dom, mountpoints, nmountpoints, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainGetTime:
 * @dom: a domain object
 * @seconds: domain's time in seconds
 * @nseconds: the nanosecond part of @seconds
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Extract information about guest time and store it into
 * @seconds and @nseconds. The @seconds represents the number of
 * seconds since the UNIX Epoch of 1970-01-01 00:00:00 in UTC.
 *
 * Please note that some hypervisors may require guest agent to
 * be configured and running in order to run this API.
 *
 * Returns 0 on success, -1 otherwise.
 */
int
virDomainGetTime(virDomainPtr dom,
                 long long *seconds,
                 unsigned int *nseconds,
                 unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "seconds=%p, nseconds=%p, flags=0x%x",
                     seconds, nseconds, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);

    if (dom->conn->driver->domainGetTime) {
        int ret = dom->conn->driver->domainGetTime(dom, seconds,
                                                   nseconds, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}

/**
 * virDomainSetTime:
 * @dom: a domain object
 * @seconds: time to set
 * @nseconds: the nanosecond part of @seconds
 * @flags: bitwise-OR of virDomainSetTimeFlags
 *
 * When a domain is suspended or restored from a file the
 * domain's OS has no idea that there was a big gap in the time.
 * Depending on how long the gap was, NTP might not be able to
 * resynchronize the guest.
 *
 * This API tries to set guest time to the given value. The time
 * to set (@seconds and @nseconds) should be in seconds relative
 * to the Epoch of 1970-01-01 00:00:00 in UTC.
 *
 * Please note that some hypervisors may require guest agent to
 * be configured and running in order to be able to run this API.
 *
 * Returns 0 on success, -1 otherwise.
 */
int
virDomainSetTime(virDomainPtr dom,
                 long long seconds,
                 unsigned int nseconds,
                 unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "seconds=%lld, nseconds=%u, flags=0x%x",
                     seconds, nseconds, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);

    if (dom->conn->driver->domainSetTime) {
        int ret = dom->conn->driver->domainSetTime(dom, seconds,
                                                   nseconds, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainSetUserPassword:
 * @dom: a domain object
 * @user: the username that will get a new password
 * @password: the password to set
 * @flags: bitwise-OR of virDomainSetUserPasswordFlags
 *
 * Sets the @user password to the value specified by @password.
 * If @flags contain VIR_DOMAIN_PASSWORD_ENCRYPTED, the password
 * is assumed to be encrypted by the method required by the guest OS.
 *
 * Please note that some hypervisors may require guest agent to
 * be configured and running in order to be able to run this API.
 *
 * Returns 0 on success, -1 otherwise.
 */
int
virDomainSetUserPassword(virDomainPtr dom,
                         const char *user,
                         const char *password,
                         unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "user=%s, password=%s, flags=0x%x",
                     NULLSTR(user), NULLSTR(password), flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);
    virCheckNonNullArgGoto(user, error);
    virCheckNonNullArgGoto(password, error);

    if (dom->conn->driver->domainSetUserPassword) {
        int ret = dom->conn->driver->domainSetUserPassword(dom, user, password,
                                                           flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virConnectGetDomainCapabilities:
 * @conn: pointer to the hypervisor connection
 * @emulatorbin: path to emulator
 * @arch: domain architecture
 * @machine: machine type
 * @virttype: virtualization type
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Prior creating a domain (for instance via virDomainCreateXML
 * or virDomainDefineXML) it may be suitable to know what the
 * underlying emulator and/or libvirt is capable of. For
 * instance, if host, libvirt and qemu is capable of VFIO
 * passthrough and so on.
 *
 * Returns NULL in case of error or an XML string
 * defining the capabilities.
 */
char *
virConnectGetDomainCapabilities(virConnectPtr conn,
                                const char *emulatorbin,
                                const char *arch,
                                const char *machine,
                                const char *virttype,
                                unsigned int flags)
{
    VIR_DEBUG("conn=%p, emulatorbin=%s, arch=%s, "
              "machine=%s, virttype=%s, flags=0x%x",
              conn, NULLSTR(emulatorbin), NULLSTR(arch),
              NULLSTR(machine), NULLSTR(virttype), flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->connectGetDomainCapabilities) {
        char *ret;
        ret = conn->driver->connectGetDomainCapabilities(conn, emulatorbin,
                                                         arch, machine,
                                                         virttype, flags);
        if (!ret)
            goto error;
        VIR_DEBUG("conn=%p, ret=%s", conn, ret);
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectGetAllDomainStats:
 * @conn: pointer to the hypervisor connection
 * @stats: stats to return, binary-OR of virDomainStatsTypes
 * @retStats: Pointer that will be filled with the array of returned stats
 * @flags: extra flags; binary-OR of virConnectGetAllDomainStatsFlags
 *
 * Query statistics for all domains on a given connection.
 *
 * Report statistics of various parameters for a running VM according to @stats
 * field. The statistics are returned as an array of structures for each queried
 * domain. The structure contains an array of typed parameters containing the
 * individual statistics. The typed parameter name for each statistic field
 * consists of a dot-separated string containing name of the requested group
 * followed by a group specific description of the statistic value.
 *
 * The statistic groups are enabled using the @stats parameter which is a
 * binary-OR of enum virDomainStatsTypes. The following groups are available
 * (although not necessarily implemented for each hypervisor):
 *
 * VIR_DOMAIN_STATS_STATE:
 *     Return domain state and reason for entering that state. The typed
 *     parameter keys are in this format:
 *
 *     "state.state" - state of the VM, returned as int from virDomainState enum
 *     "state.reason" - reason for entering given state, returned as int from
 *                      virDomain*Reason enum corresponding to given state.
 *
 * VIR_DOMAIN_STATS_CPU_TOTAL:
 *     Return CPU statistics and usage information. The typed parameter keys
 *     are in this format:
 *
 *     "cpu.time" - total cpu time spent for this domain in nanoseconds
 *                  as unsigned long long.
 *     "cpu.user" - user cpu time spent in nanoseconds as unsigned long long.
 *     "cpu.system" - system cpu time spent in nanoseconds as unsigned long
 *                    long.
 *     "cpu.cache.monitor.count" - the number of cache monitors for this domain
 *     "cpu.cache.monitor.<num>.name" - the name of cache monitor <num>
 *     "cpu.cache.monitor.<num>.vcpus" - vcpu list of cache monitor <num>
 *     "cpu.cache.monitor.<num>.bank.count" - the number of cache banks in
 *                                            cache monitor <num>
 *     "cpu.cache.monitor.<num>.bank.<index>.id" - host allocated cache id for
 *                                                 bank <index> in cache
 *                                                 monitor <num>
 *     "cpu.cache.monitor.<num>.bank.<index>.bytes" - the number of bytes of
 *                                                    last level cache that the
 *                                                    domain is using on cache
 *                                                    bank <index>
 *
 * VIR_DOMAIN_STATS_BALLOON:
 *     Return memory balloon device information.
 *     The typed parameter keys are in this format:
 *
 *     "balloon.current" - the memory in kiB currently used
 *                         as unsigned long long.
 *     "balloon.maximum" - the maximum memory in kiB allowed
 *                         as unsigned long long.
 *     "balloon.swap_in" - the amount of data read from swap space (in KiB)
 *                         as unsigned long long
 *     "balloon.swap_out" - the amount of memory written out to swap space
 *                          (in KiB) as unsigned long long
 *     "balloon.major_fault" - the number of page faults when disk IO was
 *                             required as unsigned long long
 *     "balloon.minor_fault" - the number of other page faults
 *                             as unsigned long long
 *     "balloon.unused" - the amount of memory left unused by the system
 *                        (in KiB) as unsigned long long
 *     "balloon.available" - the amount of usable memory as seen by the domain
 *                           (in KiB) as unsigned long long
 *     "balloon.rss" - Resident Set Size of running domain's process
 *                     (in KiB) as unsigned long long
 *     "balloon.usable" - the amount of memory which can be reclaimed by balloon
 *                        without causing host swapping (in KiB)
 *                        as unsigned long long
 *     "balloon.last-update" - timestamp of the last update of statistics
 *                             (in seconds) as unsigned long long
 *     "balloon.disk_caches" - the amount of memory that can be reclaimed
 *                             without additional I/O, typically disk (in KiB)
 *                             as unsigned long long
 *     "balloon.hugetlb_pgalloc" - the number of successful huge page allocations
 *                                 from inside the domain via virtio balloon
 *                                 as unsigned long long
 *     "balloon.hugetlb_pgfail" - the number of failed huge page allocations
 *                                from inside the domain via virtio balloon
 *                                as unsigned long long
 *
 * VIR_DOMAIN_STATS_VCPU:
 *     Return virtual CPU statistics.
 *     Due to VCPU hotplug, the vcpu.<num>.* array could be sparse.
 *     The actual size of the array corresponds to "vcpu.current".
 *     The array size will never exceed "vcpu.maximum".
 *     The typed parameter keys are in this format:
 *
 *     "vcpu.current" - current number of online virtual CPUs as unsigned int.
 *     "vcpu.maximum" - maximum number of online virtual CPUs as unsigned int.
 *     "vcpu.<num>.state" - state of the virtual CPU <num>, as int
 *                          from virVcpuState enum.
 *     "vcpu.<num>.time" - virtual cpu time spent by virtual CPU <num>
 *                         as unsigned long long.
 *     "vcpu.<num>.wait" - time the vCPU <num> wants to run, but the host
 *                         scheduler has something else running ahead of it.
 *     "vcpu.<num>.halted" - virtual CPU <num> is halted, may indicate the
 *                           processor is idle or even disabled, depending
 *                           on the architecture)
 *
 * VIR_DOMAIN_STATS_INTERFACE:
 *     Return network interface statistics (from domain point of view).
 *     The typed parameter keys are in this format:
 *
 *     "net.count" - number of network interfaces on this domain
 *                   as unsigned int.
 *     "net.<num>.name" - name of the interface <num> as string.
 *     "net.<num>.rx.bytes" - bytes received as unsigned long long.
 *     "net.<num>.rx.pkts" - packets received as unsigned long long.
 *     "net.<num>.rx.errs" - receive errors as unsigned long long.
 *     "net.<num>.rx.drop" - receive packets dropped as unsigned long long.
 *     "net.<num>.tx.bytes" - bytes transmitted as unsigned long long.
 *     "net.<num>.tx.pkts" - packets transmitted as unsigned long long.
 *     "net.<num>.tx.errs" - transmission errors as unsigned long long.
 *     "net.<num>.tx.drop" - transmit packets dropped as unsigned long long.
 *
 * VIR_DOMAIN_STATS_BLOCK:
 *     Return block devices statistics.  By default,
 *     this information is limited to the active layer of each <disk> of the
 *     domain (where block.count is equal to the number of disks), but adding
 *     VIR_CONNECT_GET_ALL_DOMAINS_STATS_BACKING to @flags will expand the
 *     array to cover backing chains (block.count corresponds to the number
 *     of host resources used together to provide the guest disks).
 *     The typed parameter keys are in this format:
 *
 *     "block.count" - number of block devices in the subsequent list,
 *                     as unsigned int.
 *     "block.<num>.name" - name of the block device <num> as string.
 *                          matches the target name (vda/sda/hda) of the
 *                          block device.  If the backing chain is listed,
 *                          this name is the same for all host resources tied
 *                          to the same guest device.
 *     "block.<num>.backingIndex" - unsigned int giving the <backingStore>
 *                                   index, only used when backing images
 *                                   are listed.
 *     "block.<num>.path" - string describing the source of block device <num>,
 *                          if it is a file or block device (omitted for network
 *                          sources and drives with no media inserted).
 *     "block.<num>.rd.reqs" - number of read requests as unsigned long long.
 *     "block.<num>.rd.bytes" - number of read bytes as unsigned long long.
 *     "block.<num>.rd.times" - total time (ns) spent on reads as
 *                              unsigned long long.
 *     "block.<num>.wr.reqs" - number of write requests as unsigned long long.
 *     "block.<num>.wr.bytes" - number of written bytes as unsigned long long.
 *     "block.<num>.wr.times" - total time (ns) spent on writes as
 *                              unsigned long long.
 *     "block.<num>.fl.reqs" - total flush requests as unsigned long long.
 *     "block.<num>.fl.times" - total time (ns) spent on cache flushing as
 *                              unsigned long long.
 *     "block.<num>.errors" - Xen only: the 'oo_req' value as
 *                            unsigned long long.
 *     "block.<num>.allocation" - offset of the highest written sector
 *                                as unsigned long long.
 *     "block.<num>.capacity" - logical size in bytes of the block device
 *                              backing image as unsigned long long.
 *     "block.<num>.physical" - physical size in bytes of the container of the
 *                              backing image as unsigned long long.
 *     "block.<num>.threshold" - current threshold for delivering the
 *                               VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD
 *                               event in bytes. See virDomainSetBlockThreshold.
 *
 * VIR_DOMAIN_STATS_PERF:
 *     Return perf event statistics.
 *     The typed parameter keys are in this format:
 *
 *     "perf.cmt" - the usage of l3 cache (bytes) by applications running on
 *                  the platform as unsigned long long. It is produced by cmt
 *                  perf event.
 *     "perf.mbmt" - the total system bandwidth (bytes/s) from one level of
 *                   cache to another as unsigned long long. It is produced
 *                   by mbmt perf event.
 *     "perf.mbml" - the amount of data (bytes/s) sent through the memory
 *                   controller on the socket as unsigned long long. It is
 *                   produced by mbml perf event.
 *     "perf.cache_misses" - the count of cache misses as unsigned long long.
 *                           It is produced by cache_misses perf event.
 *     "perf.cache_references" - the count of cache hits as unsigned long long.
 *                               It is produced by cache_references perf event.
 *     "perf.instructions" - The count of instructions as unsigned long long.
 *                           It is produced by instructions perf event.
 *     "perf.cpu_cycles" - The count of cpu cycles (total/elapsed) as an
 *                         unsigned long long. It is produced by cpu_cycles
 *                         perf event.
 *     "perf.branch_instructions" - The count of branch instructions as
 *                                  unsigned long long. It is produced by
 *                                  branch_instructions perf event.
 *     "perf.branch_misses" - The count of branch misses as unsigned long
 *                            long. It is produced by branch_misses perf event.
 *     "perf.bus_cycles" - The count of bus cycles as unsigned long
 *                         long. It is produced by bus_cycles perf event.
 *     "perf.stalled_cycles_frontend" - The count of stalled cpu cycles in the
 *                                      frontend of the instruction processor
 *                                      pipeline as unsigned long long. It is
 *                                      produced by stalled_cycles_frontend
 *                                      perf event.
 *     "perf.stalled_cycles_backend"  - The count of stalled cpu cycles in the
 *                                      backend of the instruction processor
 *                                      pipeline as unsigned long long. It is
 *                                      produced by stalled_cycles_backend
 *                                      perf event.
 *     "perf.ref_cpu_cycles" - The count of total cpu cycles not affected by
 *                             CPU frequency scaling by applications running
 *                             as unsigned long long. It is produced by the
 *                             ref_cpu_cycles perf event.
 *     "perf.cpu_clock" - The count of cpu clock time as unsigned long long.
 *                        It is produced by the cpu_clock perf event.
 *     "perf.task_clock" - The count of task clock time as unsigned long long.
 *                         It is produced by the task_clock perf event.
 *     "perf.page_faults" - The count of page faults as unsigned long long.
 *                          It is produced by the page_faults perf event
 *     "perf.context_switches" - The count of context switches as unsigned long
 *                               long. It is produced by the context_switches
 *                               perf event.
 *     "perf.cpu_migrations" - The count of cpu migrations, from one logical
 *                             processor to another, as unsigned long
 *                             long. It is produced by the cpu_migrations
 *                             perf event.
 *     "perf.page_faults_min" - The count of minor page faults as unsigned
 *                              long long. It is produced by the
 *                              page_faults_min perf event.
 *     "perf.page_faults_maj" - The count of major page faults as unsigned
 *                              long long. It is produced by the
 *                              page_faults_maj perf event.
 *     "perf.alignment_faults" - The count of alignment faults as unsigned
 *                               long long. It is produced by the
 *                               alignment_faults perf event
 *     "perf.emulation_faults" - The count of emulation faults as unsigned
 *                               long long. It is produced by the
 *                               emulation_faults perf event
 *
 * VIR_DOMAIN_STATS_IOTHREAD:
 *     Return IOThread statistics if available. IOThread polling is a
 *     timing mechanism that allows the hypervisor to generate a longer
 *     period of time in which the guest will perform operations on the
 *     CPU being used by the IOThread. The higher the value for poll-max-ns
 *     the longer the guest will keep the CPU. This may affect other host
 *     threads using the CPU. The poll-grow and poll-shrink values allow
 *     the hypervisor to generate a mechanism to add or remove polling time
 *     within the confines of 0 and poll-max-ns. For QEMU, the poll-grow is
 *     multiplied by the polling interval, while poll-shrink is used as a
 *     divisor. When not provided, QEMU may double the polling time until
 *     poll-max-ns is reached. When poll-shrink is 0 (zero) QEMU may reset
 *     the polling interval to 0 until it finds its "sweet spot". Setting
 *     poll-grow too large may cause frequent fluctuation of the time; however,
 *     this can be tempered by a high poll-shrink to reduce the polling
 *     interval. For example, a poll-grow of 3 will triple the polling time
 *     which could quickly exceed poll-max-ns; however, a poll-shrink of
 *     10 would cut that polling time more gradually.
 *
 *     The typed parameter keys are in this format:
 *
 *     "iothread.count" - maximum number of IOThreads in the subsequent list
 *                        as unsigned int. Each IOThread in the list will
 *                        will use it's iothread_id value as the <id>. There
 *                        may be fewer <id> entries than the iothread.count
 *                        value if the polling values are not supported.
 *     "iothread.<id>.poll-max-ns" - maximum polling time in ns as an unsigned
 *                                   long long. A 0 (zero) means polling is
 *                                   disabled.
 *     "iothread.<id>.poll-grow" - polling time factor as an unsigned int.
 *                                 A 0 (zero) indicates to allow the underlying
 *                                 hypervisor to choose how to grow the
 *                                 polling time.
 *     "iothread.<id>.poll-shrink" - polling time divisor as an unsigned int.
 *                                 A 0 (zero) indicates to allow the underlying
 *                                 hypervisor to choose how to shrink the
 *                                 polling time.
 *
 * VIR_DOMAIN_STATS_MEMORY:
 *     Return memory bandwidth statistics and the usage information. The typed
 *     parameter keys are in this format:
 *
 *     "memory.bandwidth.monitor.count" - the number of memory bandwidth
 *                                        monitors for this domain
 *     "memory.bandwidth.monitor.<num>.name" - the name of monitor <num>
 *     "memory.bandwidth.monitor.<num>.vcpus" - the vcpu list of monitor <num>
 *     "memory.bandwidth.monitor.<num>.node.count" - the number of memory
 *                                            controller in monitor <num>
 *     "memory.bandwidth.monitor.<num>.node.<index>.id" - host allocated memory
 *                                                 controller id for controller
 *                                                 <index> of monitor <num>
 *     "memory.bandwidth.monitor.<num>.node.<index>.bytes.local" - the
 *                       accumulative bytes consumed by @vcpus that passing
 *                       through the memory controller in the same processor
 *                       that the scheduled host CPU belongs to.
 *     "memory.bandwidth.monitor.<num>.node.<index>.bytes.total" - the total
 *                       bytes consumed by @vcpus that passing through all
 *                       memory controllers, either local or remote controller.
 *
 * Note that entire stats groups or individual stat fields may be missing from
 * the output in case they are not supported by the given hypervisor, are not
 * applicable for the current state of the guest domain, or their retrieval
 * was not successful.
 *
 * Using 0 for @stats returns all stats groups supported by the given
 * hypervisor.
 *
 * Specifying VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS as @flags makes
 * the function return error in case some of the stat types in @stats were
 * not recognized by the daemon.  However, even with this flag, a hypervisor
 * may omit individual fields within a known group if the information is not
 * available; as an extreme example, a supported group may produce zero
 * fields for offline domains if the statistics are meaningful only for a
 * running domain.
 *
 * Passing VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT in
 * @flags means when libvirt is unable to fetch stats for any of
 * the domains (for whatever reason) only a subset of statistics
 * is returned for the domain.  That subset being statistics that
 * don't involve querying the underlying hypervisor.
 *
 * Similarly to virConnectListAllDomains, @flags can contain various flags to
 * filter the list of domains to provide stats for.
 *
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_ACTIVE selects online domains while
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_INACTIVE selects offline ones.
 *
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_PERSISTENT and
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_TRANSIENT allow to filter the list
 * according to their persistence.
 *
 * To filter the list of VMs by domain state @flags can contain
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_RUNNING,
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_PAUSED,
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_SHUTOFF and/or
 * VIR_CONNECT_GET_ALL_DOMAINS_STATS_OTHER for all other states.
 *
 * Returns the count of returned statistics structures on success, -1 on error.
 * The requested data are returned in the @retStats parameter. The returned
 * array should be freed by the caller. See virDomainStatsRecordListFree.
 */
int
virConnectGetAllDomainStats(virConnectPtr conn,
                            unsigned int stats,
                            virDomainStatsRecordPtr **retStats,
                            unsigned int flags)
{
    int ret = -1;

    VIR_DEBUG("conn=%p, stats=0x%x, retStats=%p, flags=0x%x",
              conn, stats, retStats, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(retStats, cleanup);

    if (!conn->driver->connectGetAllDomainStats) {
        virReportUnsupportedError();
        goto cleanup;
    }

    ret = conn->driver->connectGetAllDomainStats(conn, NULL, 0, stats,
                                                 retStats, flags);

 cleanup:
    if (ret < 0)
        virDispatchError(conn);

    return ret;
}


/**
 * virDomainListGetStats:
 * @doms: NULL terminated array of domains
 * @stats: stats to return, binary-OR of virDomainStatsTypes
 * @retStats: Pointer that will be filled with the array of returned stats
 * @flags: extra flags; binary-OR of virConnectGetAllDomainStatsFlags
 *
 * Query statistics for domains provided by @doms. Note that all domains in
 * @doms must share the same connection.
 *
 * Report statistics of various parameters for a running VM according to @stats
 * field. The statistics are returned as an array of structures for each queried
 * domain. The structure contains an array of typed parameters containing the
 * individual statistics. The typed parameter name for each statistic field
 * consists of a dot-separated string containing name of the requested group
 * followed by a group specific description of the statistic value.
 *
 * The statistic groups are enabled using the @stats parameter which is a
 * binary-OR of enum virDomainStatsTypes. The stats groups are documented
 * in virConnectGetAllDomainStats.
 *
 * Using 0 for @stats returns all stats groups supported by the given
 * hypervisor.
 *
 * Specifying VIR_CONNECT_GET_ALL_DOMAINS_STATS_ENFORCE_STATS as @flags makes
 * the function return error in case some of the stat types in @stats were
 * not recognized by the daemon.  However, even with this flag, a hypervisor
 * may omit individual fields within a known group if the information is not
 * available; as an extreme example, a supported group may produce zero
 * fields for offline domains if the statistics are meaningful only for a
 * running domain.
 *
 * Passing VIR_CONNECT_GET_ALL_DOMAINS_STATS_NOWAIT in
 * @flags means when libvirt is unable to fetch stats for any of
 * the domains (for whatever reason) only a subset of statistics
 * is returned for the domain.  That subset being statistics that
 * don't involve querying the underlying hypervisor.
 *
 * Note that any of the domain list filtering flags in @flags may be rejected
 * by this function.
 *
 * Returns the count of returned statistics structures on success, -1 on error.
 * The requested data are returned in the @retStats parameter. The returned
 * array should be freed by the caller. See virDomainStatsRecordListFree.
 * Note that the count of returned stats may be less than the domain count
 * provided via @doms.
 */
int
virDomainListGetStats(virDomainPtr *doms,
                      unsigned int stats,
                      virDomainStatsRecordPtr **retStats,
                      unsigned int flags)
{
    virConnectPtr conn = NULL;
    virDomainPtr *nextdom = doms;
    unsigned int ndoms = 0;
    int ret = -1;

    VIR_DEBUG("doms=%p, stats=0x%x, retStats=%p, flags=0x%x",
              doms, stats, retStats, flags);

    virResetLastError();

    virCheckNonNullArgGoto(doms, cleanup);
    virCheckNonNullArgGoto(retStats, cleanup);

    if (!*doms) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("doms array in %s must contain at least one domain"),
                       __FUNCTION__);
        goto cleanup;
    }

    conn = doms[0]->conn;
    virCheckConnectReturn(conn, -1);

    if (!conn->driver->connectGetAllDomainStats) {
        virReportUnsupportedError();
        goto cleanup;
    }

    while (*nextdom) {
        virDomainPtr dom = *nextdom;

        virCheckDomainGoto(dom, cleanup);

        if (dom->conn != conn) {
            virReportError(VIR_ERR_INVALID_ARG, "%s",
                           _("domains in 'doms' array must belong to a "
                             "single connection"));
            goto cleanup;
        }

        ndoms++;
        nextdom++;
    }

    ret = conn->driver->connectGetAllDomainStats(conn, doms, ndoms,
                                                 stats, retStats, flags);

 cleanup:
    if (ret < 0)
        virDispatchError(conn);
    return ret;
}


/**
 * virDomainStatsRecordListFree:
 * @stats: NULL terminated array of virDomainStatsRecords to free
 *
 * Convenience function to free a list of domain stats returned by
 * virDomainListGetStats and virConnectGetAllDomainStats.
 */
void
virDomainStatsRecordListFree(virDomainStatsRecordPtr *stats)
{
    virDomainStatsRecordPtr *next;

    if (!stats)
        return;

    for (next = stats; *next; next++) {
        virTypedParamsFree((*next)->params, (*next)->nparams);
        virDomainFree((*next)->dom);
        VIR_FREE(*next);
    }

    VIR_FREE(stats);
}


/**
 * virDomainGetFSInfo:
 * @dom: a domain object
 * @info: a pointer to a variable to store an array of mount points information
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Get a list of mapping information for each mounted file systems within the
 * specified guest and the disks.
 *
 * Returns the number of returned mount points, or -1 in case of error.
 * On success, the array of the information is stored into @info. The caller is
 * responsible for calling virDomainFSInfoFree() on each array element, then
 * calling free() on @info. On error, @info is set to NULL.
 */
int
virDomainGetFSInfo(virDomainPtr dom,
                   virDomainFSInfoPtr **info,
                   unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "info=%p, flags=0x%x", info, flags);

    virResetLastError();

    virCheckDomainReturn(dom, -1);
    virCheckReadOnlyGoto(dom->conn->flags, error);
    virCheckNonNullArgGoto(info, error);
    *info = NULL;

    if (dom->conn->driver->domainGetFSInfo) {
        int ret = dom->conn->driver->domainGetFSInfo(dom, info, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainFSInfoFree:
 * @info: pointer to a FSInfo object
 *
 * Frees all the memory occupied by @info.
 */
void
virDomainFSInfoFree(virDomainFSInfoPtr info)
{
    size_t i;

    if (!info)
        return;

    g_free(info->mountpoint);
    g_free(info->name);
    g_free(info->fstype);

    for (i = 0; i < info->ndevAlias; i++)
        g_free(info->devAlias[i]);
    g_free(info->devAlias);

    g_free(info);
}

/**
 * virDomainInterfaceAddresses:
 * @dom: domain object
 * @ifaces: pointer to an array of pointers pointing to interface objects
 * @source: one of the virDomainInterfaceAddressesSource constants
 * @flags: currently unused, pass zero
 *
 * Return a pointer to the allocated array of pointers to interfaces
 * present in given domain along with their IP and MAC addresses. Note that
 * single interface can have multiple or even 0 IP addresses.
 *
 * This API dynamically allocates the virDomainInterfacePtr struct based on
 * how many interfaces domain @dom has, usually there's 1:1 correlation. The
 * count of the interfaces is returned as the return value.
 *
 * If @source is VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE, the DHCP lease
 * file associated with any virtual networks will be examined to obtain
 * the interface addresses. This only returns data for interfaces which
 * are connected to virtual networks managed by libvirt.
 *
 * If @source is VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT, a configured
 * guest agent is needed for successful return from this API. Moreover, if
 * guest agent is used then the interface name is the one seen by guest OS.
 * To match such interface with the one from @dom XML use MAC address or IP
 * range.
 *
 * If @source is VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP, the host
 * ARP table will be check to obtain the interface addresses. As
 * the arp cache refreshes in time, the returned ip address may
 * be unreachable. Depending on the route table config of the
 * guest, the returned mac address may be duplicated.
 *
 * Note that for some @source values some pieces of returned @ifaces
 * might be unset (e.g. VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_ARP does not
 * set IP address prefix as ARP table does not have any notion of that).
 *
 * @ifaces->name and @ifaces->hwaddr are never NULL.
 *
 * The caller *must* free @ifaces when no longer needed. Usual use case
 * looks like this:
 *
 *   virDomainInterfacePtr *ifaces = NULL;
 *   int ifaces_count = 0;
 *   size_t i, j;
 *   virDomainPtr dom = ... obtain a domain here ...;
 *
 *   if ((ifaces_count = virDomainInterfaceAddresses(dom, &ifaces,
 *            VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_LEASE)) < 0)
 *       goto cleanup;
 *
 *  ... do something with returned values, for example:
 *
 *   for (i = 0; i < ifaces_count; i++) {
 *       printf("name: %s", ifaces[i]->name);
 *       if (ifaces[i]->hwaddr)
 *           printf(" hwaddr: %s", ifaces[i]->hwaddr);
 *
 *       for (j = 0; j < ifaces[i]->naddrs; j++) {
 *           virDomainIPAddressPtr ip_addr = ifaces[i]->addrs + j;
 *           printf("[addr: %s prefix: %d type: %d]",
 *                  ip_addr->addr, ip_addr->prefix, ip_addr->type);
 *       }
 *       printf("\n");
 *   }
 *
 *   cleanup:
 *       if (ifaces && ifaces_count > 0)
 *           for (i = 0; i < ifaces_count; i++)
 *               virDomainInterfaceFree(ifaces[i]);
 *       free(ifaces);
 *
 * Returns the number of interfaces on success, -1 in case of error.
 */
int
virDomainInterfaceAddresses(virDomainPtr dom,
                            virDomainInterfacePtr **ifaces,
                            unsigned int source,
                            unsigned int flags)
{
    VIR_DOMAIN_DEBUG(dom, "ifaces=%p, source=%d, flags=0x%x", ifaces, source, flags);

    virResetLastError();

    if (ifaces)
        *ifaces = NULL;
    virCheckDomainReturn(dom, -1);
    virCheckNonNullArgGoto(ifaces, error);
    if (source == VIR_DOMAIN_INTERFACE_ADDRESSES_SRC_AGENT)
        virCheckReadOnlyGoto(dom->conn->flags, error);

    if (dom->conn->driver->domainInterfaceAddresses) {
        int ret;
        ret = dom->conn->driver->domainInterfaceAddresses(dom, ifaces, source, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportError(VIR_ERR_NO_SUPPORT, __FUNCTION__);

 error:
    virDispatchError(dom->conn);
    return -1;
}


/**
 * virDomainInterfaceFree:
 * @iface: an interface object
 *
 * Free the interface object. The data structure is
 * freed and should not be used thereafter. If @iface
 * is NULL, then this method has no effect.
 */
void
virDomainInterfaceFree(virDomainInterfacePtr iface)
{
    size_t i;

    if (!iface)
        return;

    VIR_FREE(iface->name);
    VIR_FREE(iface->hwaddr);

    for (i = 0; i < iface->naddrs; i++)
        VIR_FREE(iface->addrs[i].addr);
    VIR_FREE(iface->addrs);

    VIR_FREE(iface);
}


/**
 * virDomainGetGuestVcpus:
 * @domain: pointer to domain object
 * @params: pointer that will be filled with an array of typed parameters
 * @nparams: pointer filled with number of elements in @params
 * @flags: currently unused, callers shall pass 0
 *
 * Queries the guest agent for state and information regarding vCPUs from
 * guest's perspective. The reported data depends on the guest agent
 * implementation.
 *
 * Reported fields stored in @params:
 * 'vcpus': string containing bitmap representing vCPU ids as reported by the
 *          guest
 * 'online': string containing bitmap representing online vCPUs as reported
 *           by the guest agent.
 * 'offlinable': string containing bitmap representing ids of vCPUs that can be
 *               offlined
 *
 * This API requires the VM to run. The caller is responsible for calling
 * virTypedParamsFree to free memory returned in @params.
 *
 * Returns 0 on success, -1 on error.
 */
int
virDomainGetGuestVcpus(virDomainPtr domain,
                       virTypedParameterPtr *params,
                       unsigned int *nparams,
                       unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p, flags=0x%x",
                     params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);

    if (domain->conn->driver->domainGetGuestVcpus) {
        int ret;
        ret = domain->conn->driver->domainGetGuestVcpus(domain, params, nparams,
                                                        flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetGuestVcpus:
 * @domain: pointer to domain object
 * @cpumap: text representation of a bitmap of vcpus to set
 * @state: 0 to disable/1 to enable cpus described by @cpumap
 * @flags: currently unused, callers shall pass 0
 *
 * Sets state of individual vcpus described by @cpumap via guest agent. Other
 * vcpus are not modified.
 *
 * This API requires the VM to run. Various hypervisors or guest agent
 * implementation may limit to operate on just 1 vCPU per call.
 *
 * @cpumap is a list of vCPU numbers. Its syntax is a comma separated list and
 * a special markup using '-' and '^' (ex. '0-4', '0-3,^2'). The '-' denotes
 * the range and the '^' denotes exclusive. The expression is sequentially
 * evaluated, so "0-15,^8" is identical to "9-14,0-7,15" but not identical to
 * "^8,0-15".
 *
 * Note that OSes (notably Linux) may require vCPU 0 to stay online to support
 * low-level features a S3 sleep.
 *
 * Returns 0 on success, -1 on error.
 */
int
virDomainSetGuestVcpus(virDomainPtr domain,
                       const char *cpumap,
                       int state,
                       unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "cpumap='%s' state=%d flags=0x%x",
                     NULLSTR(cpumap), state, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    virCheckNonNullArgGoto(cpumap, error);

    if (domain->conn->driver->domainSetGuestVcpus) {
        int ret;
        ret = domain->conn->driver->domainSetGuestVcpus(domain, cpumap, state,
                                                        flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetVcpu:
 * @domain: pointer to domain object
 * @vcpumap: text representation of a bitmap of vcpus to set
 * @state: 0 to disable/1 to enable cpus described by @vcpumap
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Enables/disables individual vcpus described by @vcpumap in the hypervisor.
 *
 * Various hypervisor implementations may limit to operate on just 1
 * hotpluggable entity (which may contain multiple vCPUs on certain platforms).
 *
 * Note that OSes and hypervisors may require vCPU 0 to stay online.
 *
 * Returns 0 on success, -1 on error.
 */
int
virDomainSetVcpu(virDomainPtr domain,
                 const char *vcpumap,
                 int state,
                 unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "vcpumap='%s' state=%i flags=0x%x",
                     NULLSTR(vcpumap), state, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    virCheckNonNullArgGoto(vcpumap, error);

    if (domain->conn->driver->domainSetVcpu) {
        int ret;
        ret = domain->conn->driver->domainSetVcpu(domain, vcpumap, state, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}

/**
 * virDomainGetGuestInfo:
 * @domain: pointer to domain object
 * @types: types of information to return, binary-OR of virDomainGuestInfoTypes
 * @params: location to store the guest info parameters
 * @nparams: number of items in @params
 * @flags: currently unused, set to 0
 *
 * Queries the guest agent for the various information about the guest system.
 * The reported data depends on the guest agent implementation. The information
 * is returned as an array of typed parameters containing the individual
 * parameters. The parameter name for each information field consists of a
 * dot-separated string containing the name of the requested group followed by
 * a group-specific description of the statistic value.
 *
 * The information groups are enabled using the @types parameter which is a
 * binary-OR of enum virDomainGuestInfoTypes. The following groups are available
 * (although not necessarily implemented for each hypervisor):
 *
 * VIR_DOMAIN_GUEST_INFO_USERS:
 *  returns information about users that are currently logged in within the
 *  guest domain. The typed parameter keys are in this format:
 *
 *      "user.count" - the number of active users on this domain as an
 *                     unsigned int
 *      "user.<num>.name" - username of the user as a string
 *      "user.<num>.domain" - domain of the user as a string (may only be
 *                            present on certain guest types)
 *      "user.<num>.login-time" - the login time of a user in milliseconds
 *                                since the epoch as unsigned long long
 *
 * VIR_DOMAIN_GUEST_INFO_OS:
 *  Return information about the operating system running within the guest. The
 *  typed parameter keys are in this format:
 *
 *      "os.id" - a string identifying the operating system
 *      "os.name" - the name of the operating system, suitable for presentation
 *                  to a user, as a string
 *      "os.pretty-name" - a pretty name for the operating system, suitable for
 *                         presentation to a user, as a string
 *      "os.version" - the version of the operating system suitable for
 *                     presentation to a user, as a string
 *      "os.version-id" - the version id of the operating system suitable for
 *                        processing by scripts, as a string
 *      "os.kernel-release" - the release of the operating system kernel, as a
 *                            string
 *      "os.kernel-version" - the version of the operating system kernel, as a
 *                            string
 *      "os.machine" - the machine hardware name as a string
 *      "os.variant" - a specific variant or edition of the operating system
 *                     suitable for presentation to a user, as a string
 *      "os.variant-id" - the id for a specific variant or edition of the
 *                        operating system, as a string
 *
 * VIR_DOMAIN_GUEST_INFO_TIMEZONE:
 *  Returns information about the timezone within the domain. The typed
 *  parameter keys are in this format:
 *
 *      "timezone.name" - the name of the timezone as a string
 *      "timezone.offset" - the offset to UTC in seconds as an int
 *
 * VIR_DOMAIN_GUEST_INFO_FILESYSTEM:
 *  Returns information about the filesystems within the domain.  The typed
 *  parameter keys are in this format:
 *
 *      "fs.count" - the number of filesystems defined on this domain
 *                   as an unsigned int
 *      "fs.<num>.mountpoint" - the path to the mount point for the filesystem
 *      "fs.<num>.name" - device name in the guest (e.g. "sda1")
 *      "fs.<num>.fstype" - the type of filesystem
 *      "fs.<num>.total-bytes" - the total size of the filesystem
 *      "fs.<num>.used-bytes" - the number of bytes used in the filesystem
 *      "fs.<num>.disk.count" - the number of disks targeted by this filesystem
 *      "fs.<num>.disk.<num>.alias" - the device alias of the disk (e.g. sda)
 *      "fs.<num>.disk.<num>.serial" - the serial number of the disk
 *      "fs.<num>.disk.<num>.device" - the device node of the disk
 *
 * VIR_DOMAIN_GUEST_INFO_DISKS:
 *  Returns information about the disks within the domain.  The typed
 *  parameter keys are in this format:
 *
 *      "disk.count" - the number of disks defined on this domain
 *                      as an unsigned int
 *      "disk.<num>.name" - device node (Linux) or device UNC (Windows)
 *      "disk.<num>.partition" - whether this is a partition or disk
 *      "disk.<num>.dependency.count" - the number of device dependencies
 *                      e.g. for LVs of the LVM this will
 *                      hold the list of PVs, for LUKS encrypted volume this will
 *                      contain the disk where the volume is placed. (Linux)
 *      "disk.<num>.dependency.<num>.name" - a dependency
 *      "disk.<num>.alias" - the device alias of the disk (e.g. sda)
 *      "disk.<num>.guest_alias" - optional alias assigned to the disk, on Linux
 *                      this is a name assigned by device mapper
 *
 * VIR_DOMAIN_GUEST_INFO_HOSTNAME:
 *  Returns information about the hostname of the domain. The typed
 *  parameter keys are in this format:
 *
 *      "hostname" - the hostname of the domain
 *
 * Using 0 for @types returns all information groups supported by the given
 * hypervisor.
 *
 * This API requires the VM to run. The caller is responsible for calling
 * virTypedParamsFree to free memory returned in @params.
 *
 * Returns 0 on success, -1 on error.
 */
int virDomainGetGuestInfo(virDomainPtr domain,
                          unsigned int types,
                          virTypedParameterPtr *params,
                          int *nparams,
                          unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "types=0x%x, params=%p, nparams=%p, flags=0x%x",
                     types, params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);

    if (domain->conn->driver->domainGetGuestInfo) {
        int ret;
        ret = domain->conn->driver->domainGetGuestInfo(domain, types,
                                                       params, nparams, flags);

        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}

/**
 * virDomainSetBlockThreshold:
 * @domain: pointer to domain object
 * @dev: string specifying the block device or backing chain element
 * @threshold: threshold in bytes when to fire the event
 * @flags: currently unused, callers should pass 0
 *
 * Set the threshold level for delivering the
 * VIR_DOMAIN_EVENT_ID_BLOCK_THRESHOLD if the device or backing chain element
 * described by @dev is written beyond the set threshold level. The threshold
 * level is unset once the event fires. The event might not be delivered at all
 * if libvirtd was not running at the moment when the threshold was reached.
 * Note that if the threshold level is reached for a top level image, the event
 * is emitted for @dev corresponding to the disk target, and may also be reported
 * with @dev corresponding to the disk target with an index corresponding to the
 * 'index' attribute of 'source' in the live VM XML if the attribute is present.
 *
 * @dev can either be a disk target name (vda, sda) or disk target with index (
 * vda[4]). Without the index the top image in the backing chain will have the
 * threshold set. The index corresponds to the 'index' attribute reported in the
 * live VM XML for 'backingStore' or 'source' elements of a disk. If index is
 * given the threshold is set for the corresponding image.
 *
 * Note that the threshold event can be registered also for destinations of a
 * 'virDomainBlockCopy' destination by using the 'index' of the 'mirror' source.
 *
 * Hypervisors report the last written sector of an image in the bulk stats API
 * (virConnectGetAllDomainStats/virDomainListGetStats) as
 * "block.<num>.allocation" in the VIR_DOMAIN_STATS_BLOCK group. The current
 * threshold value is reported as "block.<num>.threshold".
 *
 * This event allows to use thin-provisioned storage which needs management
 * tools to grow it without the need for polling of the data.
 *
 * Returns 0 if the operation has started, -1 on failure.
 */
int
virDomainSetBlockThreshold(virDomainPtr domain,
                           const char *dev,
                           unsigned long long threshold,
                           unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "dev='%s' threshold=%llu flags=0x%x",
                     NULLSTR(dev), threshold, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    virCheckNonNullArgGoto(dev, error);

    if (domain->conn->driver->domainSetBlockThreshold) {
        int ret;
        ret = domain->conn->driver->domainSetBlockThreshold(domain, dev,
                                                            threshold, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainSetLifecycleAction:
 * @domain: pointer to domain object
 * @type: the lifecycle type from virDomainLifecycle
 * @action: the action type from virDomainLifecycleAction
 * @flags: bitwise-OR of virDomainModificationImpact
 *
 * Changes the actions of lifecycle events for domain represented as
 * <on_$type>$action</on_$type> in the domain XML.
 *
 * QEMU driver has a limitation that if all lifecycle events are set
 * to destroy when the domain is started, it's not possible to change
 * any action for running domain.
 *
 * Returns 0 on success, -1 on failure.
 */
int virDomainSetLifecycleAction(virDomainPtr domain,
                                unsigned int type,
                                unsigned int action,
                                unsigned int flags)
{
    VIR_DOMAIN_DEBUG(domain, "type='%u' action='%u' flags='0x%x'",
                     type, action, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckReadOnlyGoto(domain->conn->flags, error);

    if (type >= VIR_DOMAIN_LIFECYCLE_LAST) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("invalid lifecycle type '%u'"), type);
        goto error;
    }

    if (action >= VIR_DOMAIN_LIFECYCLE_ACTION_LAST) {
        virReportError(VIR_ERR_INVALID_ARG,
                       _("invalid lifecycle action '%u'"), action);
        goto error;
    }

    if (domain->conn->driver->domainSetLifecycleAction) {
        int ret;
        ret = domain->conn->driver->domainSetLifecycleAction(domain,
                                                             type,
                                                             action,
                                                             flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}

/**
 * virDomainGetLaunchSecurityInfo:
 * @domain: a domain object
 * @params: where to store security info
 * @nparams: number of items in @params
 * @flags: currently used, set to 0.
 *
 * Get the launch security info. In case of the SEV guest, this will
 * return the launch measurement.
 *
 * Returns -1 in case of failure, 0 in case of success.
 */
int virDomainGetLaunchSecurityInfo(virDomainPtr domain,
                                   virTypedParameterPtr *params,
                                   int *nparams,
                                   unsigned int flags)
{
    virConnectPtr conn = domain->conn;
    int rc;

    VIR_DOMAIN_DEBUG(domain, "params=%p, nparams=%p flags=0x%x",
                     params, nparams, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    virCheckNonNullArgGoto(params, error);
    virCheckNonNullArgGoto(nparams, error);
    virCheckReadOnlyGoto(conn->flags, error);

    rc = VIR_DRV_SUPPORTS_FEATURE(domain->conn->driver, domain->conn,
                                  VIR_DRV_FEATURE_TYPED_PARAM_STRING);
    if (rc < 0)
        goto error;
    if (rc)
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

    if (conn->driver->domainGetLaunchSecurityInfo) {
        int ret;
        ret = conn->driver->domainGetLaunchSecurityInfo(domain, params,
                                                        nparams, flags);
        if (ret < 0)
            goto error;
        return ret;
    }
    virReportUnsupportedError();

 error:
    virDispatchError(domain->conn);
    return -1;
}


/**
 * virDomainAgentSetResponseTimeout:
 * @domain: a domain object
 * @timeout: timeout in seconds
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Set how long to wait for a response from guest agent commands. By default,
 * agent commands block forever waiting for a response.
 *
 * @timeout must be a value from virDomainAgentResponseTimeoutValues or
 * positive:
 *
 *   VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_BLOCK(-2): meaning to block forever
 *      waiting for a result.
 *   VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_DEFAULT(-1): use default timeout value.
 *   VIR_DOMAIN_AGENT_RESPONSE_TIMEOUT_NOWAIT(0): does not wait.
 *   positive value: wait for @timeout seconds
 *
 * Returns 0 on success, -1 on failure
 */
int
virDomainAgentSetResponseTimeout(virDomainPtr domain,
                                 int timeout,
                                 unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "timeout=%i, flags=0x%x",
                     timeout, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAgentSetResponseTimeout) {
        if (conn->driver->domainAgentSetResponseTimeout(domain, timeout, flags) < 0)
            goto error;
        return 0;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainBackupBegin:
 * @domain: a domain object
 * @backupXML: description of the requested backup
 * @checkpointXML: description of a checkpoint to create or NULL
 * @flags: bitwise or of virDomainBackupBeginFlags
 *
 * Start a point-in-time backup job for the specified disks of a
 * running domain.
 *
 * A backup job is a domain job and thus mutually exclusive with any other
 * domain job such as migration.
 *
 * For now, backup jobs are also mutually exclusive with any
 * other block job on the same device, although this restriction may
 * be lifted in a future release. Progress of the backup job can be
 * tracked via virDomainGetJobStats(). Completion of the job is also announced
 * asynchronously via VIR_DOMAIN_EVENT_ID_JOB_COMPLETED event.
 *
 * There are two fundamental backup approaches. The first, called a
 * push model, instructs the hypervisor to copy the state of the guest
 * disk to the designated storage destination (which may be on the
 * local file system or a network device). In this mode, the
 * hypervisor writes the content of the guest disk to the destination,
 * then emits VIR_DOMAIN_EVENT_ID_JOB_COMPLETED when the backup is
 * either complete or failed (the backup image is invalid if the job
 * fails or virDomainAbortJob() is used prior to the event being
 * emitted). This kind of the job finishes automatically. Users can
 * determine success by using virDomainGetJobStats() with
 * VIR_DOMAIN_JOB_STATS_COMPLETED flag.
 *
 * The second, called a pull model, instructs the hypervisor to expose
 * the state of the guest disk over an NBD export. A third-party
 * client can then connect to this export and read whichever portions
 * of the disk it desires.  In this mode libvirt has to be informed via
 * virDomainAbortJob() when the third-party NBD client is done and the backup
 * resources can be released.
 *
 * The @backupXML parameter contains details about the backup in the top-level
 * element <domainbackup>, including which backup mode to use, whether the
 * backup is incremental from a previous checkpoint, which disks
 * participate in the backup, the destination for a push model backup,
 * and the temporary storage and NBD server details for a pull model
 * backup.
 *
 * virDomainBackupGetXMLDesc() can be called to learn actual
 * values selected.  For more information, see
 * formatcheckpoint.html#BackupAttributes.
 *
 * The @checkpointXML parameter is optional; if non-NULL, then libvirt
 * behaves as if virDomainCheckpointCreateXML() were called to create
 * a checkpoint atomically covering the same point in time as the
 * backup.
 *
 * The VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL specifies that the output or
 * temporary files described by the @backupXML document were created by the
 * caller with correct format and size to hold the backup or temporary data.
 *
 * The creation of a new checkpoint allows for future incremental backups.
 * Note that some hypervisors may require a particular disk format, such as
 * qcow2, in order to take advantage of checkpoints, while allowing arbitrary
 * formats if checkpoints are not involved.
 *
 * Returns 0 on success or -1 on failure.
 */
int
virDomainBackupBegin(virDomainPtr domain,
                     const char *backupXML,
                     const char *checkpointXML,
                     unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "backupXML=%s, checkpointXML=%s, flags=0x%x",
                     NULLSTR(backupXML), NULLSTR(checkpointXML), flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;

    virCheckNonNullArgGoto(backupXML, error);
    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainBackupBegin) {
        int ret;
        ret = conn->driver->domainBackupBegin(domain, backupXML, checkpointXML,
                                              flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainBackupGetXMLDesc:
 * @domain: a domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Queries the configuration of the active backup job.
 *
 * In some cases, a user can start a backup job without supplying all
 * details and rely on libvirt to fill in the rest (for example,
 * selecting the port used for an NBD export). This API can then be
 * used to learn what default values were chosen.
 *
 * Returns a NUL-terminated UTF-8 encoded XML instance or NULL in
 * case of error.  The caller must free() the returned value.
 */
char *
virDomainBackupGetXMLDesc(virDomainPtr domain,
                          unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "flags=0x%x", flags);

    virResetLastError();

    virCheckDomainReturn(domain, NULL);
    conn = domain->conn;

    if (conn->driver->domainBackupGetXMLDesc) {
        char *ret;
        ret = conn->driver->domainBackupGetXMLDesc(domain, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virDomainAuthorizedSSHKeysGet:
 * @domain: a domain object
 * @user: user to list keys for
 * @keys: pointer to a variable to store authorized keys
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * For given @user in @domain fetch list of public SSH authorized
 * keys and store them into @keys array which is allocated upon
 * successful return and is NULL terminated. The caller is
 * responsible for freeing @keys when no longer needed.
 *
 * Keys are in OpenSSH format (see sshd(8)) but from libvirt's
 * point of view are opaque strings, i.e. not interpreted.
 *
 * Please note that some hypervisors may require guest agent to
 * be configured and running in order to be able to run this API.
 *
 * Returns: number of keys stored in @keys,
 *          -1 otherwise.
 */
int
virDomainAuthorizedSSHKeysGet(virDomainPtr domain,
                              const char *user,
                              char ***keys,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "user=%s, keys=%p, flags=0x%x",
                     user, keys, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;
    virCheckNonNullArgGoto(user, error);
    virCheckNonNullArgGoto(keys, error);

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAuthorizedSSHKeysGet) {
        int ret;
        ret = conn->driver->domainAuthorizedSSHKeysGet(domain, user,
                                                       keys, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}


/**
 * virDomainAuthorizedSSHKeysSet:
 * @domain: a domain object
 * @user: user to add keys for
 * @keys: authorized keys to set
 * @nkeys: number of keys in @keys array
 * @flags: bitwise or of virDomainAuthorizedSSHKeysSetFlags
 *
 * For given @user in @domain set @keys in authorized keys file.
 * Any previous content of the file is overwritten with new keys.
 * That is, if this API is called with @nkeys = 0, @keys = NULL
 * and @flags = 0 then the authorized keys file for @user is
 * cleared out.
 *
 * Keys are in OpenSSH format (see sshd(8)) but from libvirt's
 * point of view are opaque strings, i.e. not interpreted.
 *
 * If VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_APPEND flag is set
 * then the file is not overwritten and new @keys are appended
 * instead.
 *
 * If VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_REMOVE flag is set then
 * instead of adding any new keys, provided @keys are removed
 * from the file. It's not considered error if the key doesn't
 * exist.
 *
 * Please note that some hypervisors may require guest agent to
 * be configured and running in order to be able to run this API.
 *
 * Returns: 0 on success,
 *         -1 otherwise.
 */
int
virDomainAuthorizedSSHKeysSet(virDomainPtr domain,
                              const char *user,
                              const char **keys,
                              unsigned int nkeys,
                              unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "user=%s, keys=%p, nkeys=%u, flags=0x%x",
                     user, keys, nkeys, flags);

    virResetLastError();

    virCheckDomainReturn(domain, -1);
    conn = domain->conn;
    virCheckNonNullArgGoto(user, error);

    if (flags & VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_APPEND ||
        flags & VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_REMOVE)
        virCheckNonNullArgGoto(keys, error);

    VIR_EXCLUSIVE_FLAGS_RET(VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_APPEND,
                            VIR_DOMAIN_AUTHORIZED_SSH_KEYS_SET_REMOVE,
                            -1);

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainAuthorizedSSHKeysSet) {
        int ret;
        ret = conn->driver->domainAuthorizedSSHKeysSet(domain, user,
                                                       keys, nkeys, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}
