Exemple #1
0
/**
 * virDomainLxcEnterCGroup:
 * @domain: a domain object
 * @flags: currently unused, pass 0
 *
 * This API is LXC specific, so it will only work with hypervisor
 * connections to the LXC driver.
 *
 * Attaches the process to the control cgroups associated
 * with the container @domain.
 *
 * Returns 0 on success, -1 on error
 */
int virDomainLxcEnterCGroup(virDomainPtr domain,
                            unsigned int flags)
{
    virConnectPtr conn;
    virCgroupPtr cgroup = NULL;

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

    virResetLastError();

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

    virCheckReadOnlyGoto(conn->flags, error);
    virCheckFlagsGoto(0, error);

    if (virCgroupNewDetect(domain->id, -1, &cgroup) < 0)
        goto error;

    if (virCgroupAddTask(cgroup, getpid()) < 0)
        goto error;

    virCgroupFree(&cgroup);

    return 0;

 error:
    virDispatchError(NULL);
    virCgroupFree(&cgroup);
    return -1;
}
Exemple #2
0
/**
 * virDomainLxcOpenNamespace:
 * @domain: a domain object
 * @fdlist: pointer to an array to be filled with FDs
 * @flags: currently unused, pass 0
 *
 * This API is LXC specific, so it will only work with hypervisor
 * connections to the LXC driver.
 *
 * Open the namespaces associated with the container @domain.
 * The @fdlist array will be allocated to a suitable size,
 * and filled with file descriptors for the namespaces. It
 * is the caller's responsibility to close the file descriptors
 *
 * The returned file descriptors are intended to be used with
 * the setns() system call.
 *
 * Returns the number of opened file descriptors, or -1 on error
 */
int
virDomainLxcOpenNamespace(virDomainPtr domain,
                          int **fdlist,
                          unsigned int flags)
{
    virConnectPtr conn;

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

    virResetLastError();

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

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

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

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return -1;
}
/**
 * virDomainSnapshotCurrent:
 * @domain: a domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Get the current snapshot for a domain, if any.
 *
 * virDomainSnapshotFree should be used to free the resources after the
 * snapshot object is no longer needed.
 *
 * Returns a domain snapshot object or NULL in case of failure.  If the
 * current domain snapshot cannot be found, then the VIR_ERR_NO_DOMAIN_SNAPSHOT
 * error is raised.
 */
virDomainSnapshotPtr
virDomainSnapshotCurrent(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->domainSnapshotCurrent) {
        virDomainSnapshotPtr snap;
        snap = conn->driver->domainSnapshotCurrent(domain, flags);
        if (!snap)
            goto error;
        return snap;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}
/**
 * virDomainSnapshotLookupByName:
 * @domain: a domain object
 * @name: name for the domain snapshot
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Try to lookup a domain snapshot based on its name.
 *
 * Returns a domain snapshot object or NULL in case of failure.  If the
 * domain snapshot cannot be found, then the VIR_ERR_NO_DOMAIN_SNAPSHOT
 * error is raised.
 */
virDomainSnapshotPtr
virDomainSnapshotLookupByName(virDomainPtr domain,
                              const char *name,
                              unsigned int flags)
{
    virConnectPtr conn;

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

    virResetLastError();

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

    virCheckNonNullArgGoto(name, error);

    if (conn->driver->domainSnapshotLookupByName) {
        virDomainSnapshotPtr dom;
        dom = conn->driver->domainSnapshotLookupByName(domain, name, flags);
        if (!dom)
            goto error;
        return dom;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}
/**
 * virDomainListAllSnapshots:
 * @domain: a domain object
 * @snaps: pointer to variable to store the array containing snapshot objects
 *         or NULL if the list is not required (just returns number of
 *         snapshots)
 * @flags: bitwise-OR of supported virDomainSnapshotListFlags
 *
 * Collect the list of domain snapshots for the given domain and allocate
 * an array to store those objects.  This API solves the race inherent in
 * virDomainSnapshotListNames().
 *
 * If @flags contains VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL and @snaps
 * is non-NULL, and no other connection is modifying snapshots, then
 * it is guaranteed that for any snapshot in the resulting list, no
 * snapshots later in the list can be reached by a sequence of
 * virDomainSnapshotGetParent() starting from that earlier snapshot;
 * otherwise, the order of snapshots in the resulting list is
 * unspecified.
 *
 * By default, this command covers all snapshots. It is also possible
 * to limit things to just snapshots with no parents, when @flags
 * includes VIR_DOMAIN_SNAPSHOT_LIST_ROOTS.  Additional filters are
 * provided in groups listed below. Within a group, bits are mutually
 * exclusive, where all possible snapshots are described by exactly
 * one bit from the group. Some hypervisors might reject particular
 * flags where it cannot make a distinction for filtering. If the set
 * of filter flags selected forms an impossible combination, the
 * hypervisor may return either 0 or an error.
 *
 * The first group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_LEAVES and
 * VIR_DOMAIN_SNAPSHOT_LIST_NO_LEAVES, to filter based on snapshots that
 * have no further children (a leaf snapshot).
 *
 * The next group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_METADATA and
 * VIR_DOMAIN_SNAPSHOT_LIST_NO_METADATA, for filtering snapshots based on
 * whether they have metadata that would prevent the removal of the last
 * reference to a domain.
 *
 * The next group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_INACTIVE,
 * VIR_DOMAIN_SNAPSHOT_LIST_ACTIVE, and VIR_DOMAIN_SNAPSHOT_LIST_DISK_ONLY,
 * for filtering snapshots based on what domain state is tracked by the
 * snapshot.
 *
 * The next group of @flags is VIR_DOMAIN_SNAPSHOT_LIST_INTERNAL and
 * VIR_DOMAIN_SNAPSHOT_LIST_EXTERNAL, for filtering snapshots based on
 * whether the snapshot is stored inside the disk images or as
 * additional files.
 *
 * Returns the number of domain snapshots found or -1 and sets @snaps to
 * NULL in case of error.  On success, the array stored into @snaps 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 virDomainSnapshotFree() on each array element, then calling
 * free() on @snaps.
 */
int
virDomainListAllSnapshots(virDomainPtr domain, virDomainSnapshotPtr **snaps,
                          unsigned int flags)
{
    virConnectPtr conn;

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

    virResetLastError();

    if (snaps)
        *snaps = NULL;

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

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

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}
/**
 * virDomainSnapshotListNames:
 * @domain: a domain object
 * @names: array to collect the list of names of snapshots
 * @nameslen: size of @names
 * @flags: bitwise-OR of supported virDomainSnapshotListFlags
 *
 * Collect the list of domain snapshots for the given domain, and store
 * their names in @names.  The value to use for @nameslen can be determined
 * by virDomainSnapshotNum() with the same @flags.
 *
 * If @flags contains VIR_DOMAIN_SNAPSHOT_LIST_TOPOLOGICAL, and no
 * other connection is modifying snapshots, then it is guaranteed that
 * for any snapshot in the resulting list, no snapshots later in the
 * list can be reached by a sequence of virDomainSnapshotGetParent()
 * starting from that earlier snapshot; otherwise, the order of
 * snapshots in the resulting list is unspecified.
 *
 * By default, this command covers all snapshots. It is also possible
 * to limit things to just snapshots with no parents, when @flags
 * includes VIR_DOMAIN_SNAPSHOT_LIST_ROOTS.  Additional filters are
 * provided via the same @flags values as documented in
 * virDomainListAllSnapshots().
 *
 * Note that this command is inherently racy: another connection can
 * define a new snapshot between a call to virDomainSnapshotNum() and
 * this call.  You are only guaranteed that all currently defined
 * snapshots were listed if the return is less than @nameslen.  Likewise,
 * you should be prepared for virDomainSnapshotLookupByName() to fail when
 * converting a name from this call into a snapshot object, if another
 * connection deletes the snapshot in the meantime.  For more control over
 * the results, see virDomainListAllSnapshots().
 *
 * Returns the number of domain snapshots found or -1 in case of error.
 * The caller is responsible to call free() for each member of the array.
 */
int
virDomainSnapshotListNames(virDomainPtr domain, char **names, int nameslen,
                           unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "names=%p, nameslen=%d, flags=0x%x",
                     names, nameslen, flags);

    virResetLastError();

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

    virCheckNonNullArgGoto(names, error);
    virCheckNonNegativeArgGoto(nameslen, error);

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

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return -1;
}
Exemple #7
0
/**
 * virDomainQemuMonitorCommand:
 * @domain: a domain object
 * @cmd: the qemu monitor command string
 * @result: a string returned by @cmd
 * @flags: bitwise-or of supported virDomainQemuMonitorCommandFlags
 *
 * This API is QEMU specific, so it will only work with hypervisor
 * connections to the QEMU driver.
 *
 * Send an arbitrary monitor command @cmd to @domain through the
 * qemu monitor. There are several requirements to safely and
 * successfully use this API:
 *
 *   - A @cmd that queries state without making any modifications is safe
 *   - A @cmd that alters state that is also tracked by libvirt is unsafe,
 *     and may cause libvirtd to crash
 *   - A @cmd that alters state not tracked by the current version of
 *     libvirt is possible as a means to test new qemu features before
 *     they have support in libvirt, but no guarantees are made to safety
 *
 * If VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP is set, the command is
 * considered to be a human monitor command and libvirt will automatically
 * convert it into QMP if needed.  In that case the @result will also
 * be converted back from QMP.
 *
 * If successful, @result will be filled with the string output of the
 * @cmd, and the caller must free this string.
 *
 * Returns 0 in case of success, -1 in case of failure
 *
 */
int
virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd,
                            char **result, unsigned int flags)
{
    virConnectPtr conn;

    VIR_DOMAIN_DEBUG(domain, "cmd=%s, result=%p, flags=%x",
                     cmd, result, flags);

    virResetLastError();

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

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

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

    virReportUnsupportedError();

error:
    virDispatchError(conn);
    return -1;
}
Exemple #8
0
/**
 * virDomainQemuAgentCommand:
 * @domain: a domain object
 * @cmd: the guest agent command string
 * @timeout: timeout seconds
 * @flags: execution flags
 *
 * Execute an arbitrary Guest Agent command.
 *
 * Issue @cmd to the guest agent running in @domain.
 * @timeout must be -2, -1, 0 or positive.
 * VIR_DOMAIN_QEMU_AGENT_COMMAND_BLOCK(-2): meaning to block forever waiting for
 * a result.
 * VIR_DOMAIN_QEMU_AGENT_COMMAND_DEFAULT(-1): use default timeout value.
 * VIR_DOMAIN_QEMU_AGENT_COMMAND_NOWAIT(0): does not wait.
 * positive value: wait for @timeout seconds
 *
 * Returns strings if success, NULL in failure.
 */
char *
virDomainQemuAgentCommand(virDomainPtr domain,
                          const char *cmd,
                          int timeout,
                          unsigned int flags)
{
    virConnectPtr conn;
    char *ret;

    VIR_DOMAIN_DEBUG(domain, "cmd=%s, timeout=%d, flags=%x",
                     cmd, timeout, flags);

    virResetLastError();

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

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->driver->domainQemuAgentCommand) {
        ret = conn->driver->domainQemuAgentCommand(domain, cmd,
                                                   timeout, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

error:
    virDispatchError(conn);
    return NULL;
}
Exemple #9
0
/**
 * virDomainLxcEnterNamespace:
 * @domain: a domain object
 * @nfdlist: number of FDs in @fdlist
 * @fdlist: list of namespace file descriptors
 * @noldfdlist: filled with number of old FDs
 * @oldfdlist: pointer to hold list of old namespace file descriptors
 * @flags: currently unused, pass 0
 *
 * This API is LXC specific, so it will only work with hypervisor
 * connections to the LXC driver.
 *
 * Attaches the process to the namespaces associated
 * with the FDs in @fdlist
 *
 * If @oldfdlist is non-NULL, it will be populated with file
 * descriptors representing the old namespace. This allows
 * the caller to switch back to its current namespace later
 *
 * Returns 0 on success, -1 on error
 */
int
virDomainLxcEnterNamespace(virDomainPtr domain,
                           unsigned int nfdlist,
                           int *fdlist,
                           unsigned int *noldfdlist,
                           int **oldfdlist,
                           unsigned int flags)
{
    size_t i;

    VIR_DOMAIN_DEBUG(domain, "nfdlist=%d, fdlist=%p, "
                     "noldfdlist=%p, oldfdlist=%p, flags=%x",
                     nfdlist, fdlist, noldfdlist, oldfdlist, flags);

    virResetLastError();

    virCheckFlagsGoto(0, error);

    if (noldfdlist && oldfdlist) {
        size_t nfds;
        if (virProcessGetNamespaces(getpid(),
                                    &nfds,
                                    oldfdlist) < 0)
            goto error;
        *noldfdlist = nfds;
    }

    if (virProcessSetNamespaces(nfdlist, fdlist) < 0) {
        if (oldfdlist && noldfdlist) {
            for (i = 0; i < *noldfdlist; i++)
                VIR_FORCE_CLOSE((*oldfdlist)[i]);
            VIR_FREE(*oldfdlist);
            *noldfdlist = 0;
        }
        goto error;
    }

    return 0;

 error:
    virDispatchError(domain->conn);
    return -1;
}
/**
 * virDomainSnapshotCreateXML:
 * @domain: a domain object
 * @xmlDesc: string containing an XML description of the domain snapshot
 * @flags: bitwise-OR of virDomainSnapshotCreateFlags
 *
 * Creates a new snapshot of a domain based on the snapshot xml
 * contained in xmlDesc, with a top-level element <domainsnapshot>.
 *
 * If @flags is 0, the domain can be active, in which case the
 * snapshot will be a full system snapshot (capturing both disk state,
 * and runtime VM state such as RAM contents), where reverting to the
 * snapshot is
 * the same as resuming from hibernation (TCP connections may have
 * timed out, but everything else picks up where it left off); or
 * the domain can be inactive, in which case the snapshot includes
 * just the disk state prior to booting.  The newly created snapshot
 * becomes current (see virDomainSnapshotCurrent()), and is a child
 * of any previous current snapshot.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE, then this
 * is a request to reinstate snapshot metadata that was previously
 * captured from virDomainSnapshotGetXMLDesc() before removing that
 * metadata, rather than creating a new snapshot.  This can be used to
 * recreate a snapshot hierarchy on a destination, then remove it on
 * the source, in order to allow migration (since migration normally
 * fails if snapshot metadata still remains on the source machine).
 * Note that while original creation can omit a number of elements
 * from @xmlDesc (and libvirt will supply sane defaults based on the
 * domain state at that point in time), a redefinition must supply
 * more elements (as the domain may have changed in the meantime, so
 * that libvirt no longer has a way to resupply correct
 * defaults). When redefining snapshot metadata, the domain's current
 * snapshot will not be altered unless the
 * VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flag is also present.  It is an
 * error to request the VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT flag
 * without VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE.  On some hypervisors,
 * redefining an existing snapshot can be used to alter host-specific
 * portions of the domain XML to be used during revert (such as
 * backing filenames associated with disk devices), but must not alter
 * guest-visible layout.  When redefining a snapshot name that does
 * not exist, the hypervisor may validate that reverting to the
 * snapshot appears to be possible (for example, disk images have
 * snapshot contents by the requested name).  Not all hypervisors
 * support these flags.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA, then the
 * domain's disk images are modified according to @xmlDesc, but
 * libvirt does not track any metadata (similar to immediately calling
 * virDomainSnapshotDelete() with
 * VIR_DOMAIN_SNAPSHOT_DELETE_METADATA_ONLY).  This flag is
 * incompatible with VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_HALT, then the domain
 * will be inactive after the snapshot completes, regardless of whether
 * it was active before; otherwise, a running domain will still be
 * running after the snapshot.  This flag is invalid on transient domains,
 * and is incompatible with VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_LIVE, then the domain
 * is not paused while creating the snapshot. This increases the size
 * of the memory dump file, but reduces downtime of the guest while
 * taking the snapshot. Some hypervisors only support this flag during
 * external snapshots.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY, then the
 * snapshot will be limited to the disks described in @xmlDesc, and no
 * VM state will be saved.  For an active guest, the disk image may be
 * inconsistent (as if power had been pulled), and specifying this
 * with the VIR_DOMAIN_SNAPSHOT_CREATE_HALT flag risks data loss.
 *
 * If @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_QUIESCE, then the
 * libvirt will attempt to use guest agent to freeze and thaw all
 * file systems in use within domain OS. However, if the guest agent
 * is not present, an error is thrown. Moreover, this flag requires
 * VIR_DOMAIN_SNAPSHOT_CREATE_DISK_ONLY to be passed as well.
 *
 * By default, if the snapshot involves external files, and any of the
 * destination files already exist as a non-empty regular file, the
 * snapshot is rejected to avoid losing contents of those files.
 * However, if @flags includes VIR_DOMAIN_SNAPSHOT_CREATE_REUSE_EXT,
 * then the destination files must be pre-created manually with
 * the correct image format and metadata including backing store path
 * (this allows a management app to pre-create files with relative backing
 * file names, rather than the default of creating with absolute backing
 * file names). Note that only the file specified in the snapshot XML is
 * inserted as a snapshot thus setting incorrect metadata in the pre-created
 * image may lead to the VM being unable to start or other block jobs may fail.
 *
 * Be aware that although libvirt prefers to report errors up front with
 * no other effect, some hypervisors have certain types of failures where
 * the overall command can easily fail even though the guest configuration
 * was partially altered (for example, if a disk snapshot request for two
 * disks fails on the second disk, but the first disk alteration cannot be
 * rolled back).  If this API call fails, it is therefore normally
 * necessary to follow up with virDomainGetXMLDesc() and check each disk
 * to determine if any partial changes occurred.  However, if @flags
 * contains VIR_DOMAIN_SNAPSHOT_CREATE_ATOMIC, then libvirt guarantees
 * that this command will not alter any disks unless the entire set of
 * changes can be done atomically, making failure recovery simpler (note
 * that it is still possible to fail after disks have changed, but only
 * in the much rarer cases of running out of memory or disk space).
 *
 * Some hypervisors may prevent this operation if there is a current
 * block copy operation; in that case, use virDomainBlockJobAbort()
 * to stop the block copy first.
 *
 * virDomainSnapshotFree should be used to free the resources after the
 * snapshot object is no longer needed.
 *
 * Returns an (opaque) new virDomainSnapshotPtr on success or NULL on
 * failure.
 */
virDomainSnapshotPtr
virDomainSnapshotCreateXML(virDomainPtr domain,
                           const char *xmlDesc,
                           unsigned int flags)
{
    virConnectPtr conn;

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

    virResetLastError();

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

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

    VIR_REQUIRE_FLAG_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_CURRENT,
                          VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE,
                          error);

    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE,
                             VIR_DOMAIN_SNAPSHOT_CREATE_NO_METADATA,
                             error);
    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_DOMAIN_SNAPSHOT_CREATE_REDEFINE,
                             VIR_DOMAIN_SNAPSHOT_CREATE_HALT,
                             error);

    if (conn->driver->domainSnapshotCreateXML) {
        virDomainSnapshotPtr ret;
        ret = conn->driver->domainSnapshotCreateXML(domain, xmlDesc, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();
 error:
    virDispatchError(conn);
    return NULL;
}
/**
 * virDomainHasCurrentSnapshot:
 * @domain: pointer to the domain object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Determine if the domain has a current snapshot.
 *
 * Returns 1 if such snapshot exists, 0 if it doesn't, -1 on error.
 */
int
virDomainHasCurrentSnapshot(virDomainPtr domain, unsigned int flags)
{
    virConnectPtr conn;

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

    virResetLastError();

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

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

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