/*
 * libvirt-host.c: entry points for vir{Connect,Node}Ptr 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 "virlog.h"
#include "virtypedparam.h"

VIR_LOG_INIT("libvirt.host");

#define VIR_FROM_THIS VIR_FROM_DOMAIN


/**
 * virConnectRef:
 * @conn: the connection to hold a reference on
 *
 * Increment the reference count on the connection. For each
 * additional call to this method, there shall be a corresponding
 * call to virConnectClose 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 connection would increment
 * the reference count.
 *
 * Returns 0 in case of success, -1 in case of failure
 */
int
virConnectRef(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p refs=%d", conn, conn ? conn->object.parent.u.s.refs : 0);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virObjectRef(conn);
    return 0;
}


/*
 * Not for public use.  This function is part of the internal
 * implementation of driver features in the remote case.
 */
int
virConnectSupportsFeature(virConnectPtr conn, int feature)
{
    int ret;
    VIR_DEBUG("conn=%p, feature=%d", conn, feature);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    if (!conn->driver->connectSupportsFeature)
        ret = 0;
    else
        ret = conn->driver->connectSupportsFeature(conn, feature);

    if (ret < 0)
        virDispatchError(conn);

    return ret;
}


/**
 * virConnectGetType:
 * @conn: pointer to the hypervisor connection
 *
 * Get the name of the Hypervisor driver used. This is merely the driver
 * name; for example, both KVM and QEMU guests are serviced by the
 * driver for the qemu:// URI, so a return of "QEMU" does not indicate
 * whether KVM acceleration is present.  For more details about the
 * hypervisor, use virConnectGetCapabilities().
 *
 * Returns NULL in case of error, a static zero terminated string otherwise.
 *
 * See also:
 * http://www.redhat.com/archives/libvir-list/2007-February/msg00096.html
 */
const char *
virConnectGetType(virConnectPtr conn)
{
    const char *ret;
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);

    if (conn->driver->connectGetType) {
        ret = conn->driver->connectGetType(conn);
        if (ret) return ret;
    }
    return conn->driver->name;
}


/**
 * virConnectGetVersion:
 * @conn: pointer to the hypervisor connection
 * @hvVer: return value for the version of the running hypervisor (OUT)
 *
 * Get the version level of the Hypervisor running. This may work only with
 * hypervisor call, i.e. with privileged access to the hypervisor, not
 * with a Read-Only connection.
 *
 * Returns -1 in case of error, 0 otherwise. if the version can't be
 *    extracted by lack of capacities returns 0 and @hvVer is 0, otherwise
 *    @hvVer value is major * 1,000,000 + minor * 1,000 + release
 */
int
virConnectGetVersion(virConnectPtr conn, unsigned long *hvVer)
{
    VIR_DEBUG("conn=%p, hvVer=%p", conn, hvVer);

    virResetLastError();

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

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

    virReportUnsupportedError();

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


/**
 * virConnectGetLibVersion:
 * @conn: pointer to the hypervisor connection
 * @libVer: returns the libvirt library version used on the connection (OUT)
 *
 * Provides @libVer, which is the version of libvirt used by the
 *   daemon running on the @conn host
 *
 * Returns -1 in case of failure, 0 otherwise, and values for @libVer have
 *      the format major * 1,000,000 + minor * 1,000 + release.
 */
int
virConnectGetLibVersion(virConnectPtr conn, unsigned long *libVer)
{
    int ret = -1;
    VIR_DEBUG("conn=%p, libVir=%p", conn, libVer);

    virResetLastError();

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

    if (conn->driver->connectGetLibVersion) {
        ret = conn->driver->connectGetLibVersion(conn, libVer);
        if (ret < 0)
            goto error;
        return ret;
    }

    *libVer = LIBVIR_VERSION_NUMBER;
    return 0;

 error:
    virDispatchError(conn);
    return ret;
}


/**
 * virConnectGetHostname:
 * @conn: pointer to a hypervisor connection
 *
 * This returns a system hostname on which the hypervisor is
 * running (based on the result of the gethostname system call, but
 * possibly expanded to a fully-qualified domain name via getaddrinfo).
 * If we are connected to a remote system, then this returns the
 * hostname of the remote system.
 *
 * Returns the hostname which must be freed by the caller, or
 * NULL if there was an error.
 */
char *
virConnectGetHostname(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);

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

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectGetURI:
 * @conn: pointer to a hypervisor connection
 *
 * This returns the URI (name) of the hypervisor connection.
 * Normally this is the same as or similar to the string passed
 * to the virConnectOpen/virConnectOpenReadOnly call, but
 * the driver may make the URI canonical.  If name == NULL
 * was passed to virConnectOpen, then the driver will return
 * a non-NULL URI which can be used to connect to the same
 * hypervisor later.
 *
 * Returns the URI string which must be freed by the caller, or
 * NULL if there was an error.
 */
char *
virConnectGetURI(virConnectPtr conn)
{
    char *name;
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);

    if (!(name = virURIFormat(conn->uri)))
        goto error;

    return name;

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectGetSysinfo:
 * @conn: pointer to a hypervisor connection
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This returns the XML description of the sysinfo details for the
 * host on which the hypervisor is running, in the same format as the
 * <sysinfo> element of a domain XML.  This information is generally
 * available only for hypervisors running with root privileges.
 *
 * Returns the XML string which must be freed by the caller, or
 * NULL if there was an error.
 */
char *
virConnectGetSysinfo(virConnectPtr conn, unsigned int flags)
{
    VIR_DEBUG("conn=%p, flags=%x", conn, flags);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);

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

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectGetMaxVcpus:
 * @conn: pointer to the hypervisor connection
 * @type: value of the 'type' attribute in the <domain> element
 *
 * Provides the maximum number of virtual CPUs supported for a guest VM of a
 * specific type. The 'type' parameter here corresponds to the 'type'
 * attribute in the <domain> element of the XML.
 *
 * Returns the maximum of virtual CPU or -1 in case of error.
 */
int
virConnectGetMaxVcpus(virConnectPtr conn,
                      const char *type)
{
    VIR_DEBUG("conn=%p, type=%s", conn, NULLSTR(type));

    virResetLastError();

    virCheckConnectReturn(conn, -1);

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

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


/**
 * virNodeGetInfo:
 * @conn: pointer to the hypervisor connection
 * @info: pointer to a virNodeInfo structure allocated by the user
 *
 * Extract hardware information about the node.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virNodeGetInfo(virConnectPtr conn, virNodeInfoPtr info)
{
    VIR_DEBUG("conn=%p, info=%p", conn, info);

    virResetLastError();

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

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

    virReportUnsupportedError();

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


/**
 * virConnectGetCapabilities:
 * @conn: pointer to the hypervisor connection
 *
 * Provides capabilities of the hypervisor / driver.
 *
 * Returns NULL in case of error, or an XML string
 * defining the capabilities.
 * The client must free the returned string after use.
 */
char *
virConnectGetCapabilities(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, NULL);

    if (conn->driver->connectGetCapabilities) {
        char *ret;
        ret = conn->driver->connectGetCapabilities(conn);
        if (!ret)
            goto error;
        VIR_DEBUG("conn=%p ret=%s", conn, ret);
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNodeGetCPUStats:
 * @conn: pointer to the hypervisor connection.
 * @cpuNum: number of node cpu. (VIR_NODE_CPU_STATS_ALL_CPUS means total cpu
 *          statistics)
 * @params: pointer to node cpu time parameter objects
 * @nparams: number of node cpu time parameter (this value should be same or
 *          less than the number of parameters supported)
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This function provides individual cpu statistics of the node.
 * If you want to get total cpu statistics of the node, you must specify
 * VIR_NODE_CPU_STATS_ALL_CPUS to @cpuNum.
 * The @params array will be filled with the values equal to the number of
 * parameters suggested by @nparams
 *
 * As the value of @nparams is dynamic, call the API setting @nparams to 0 and
 * @params as NULL, the API returns the number of parameters supported by the
 * HV by updating @nparams on SUCCESS. The caller should then allocate @params
 * array, i.e. (sizeof(@virNodeCPUStats) * @nparams) bytes and call
 * the API again.
 *
 * Here is a sample code snippet:
 *
 *   if (virNodeGetCPUStats(conn, cpuNum, NULL, &nparams, 0) == 0 &&
 *       nparams != 0) {
 *       if ((params = malloc(sizeof(virNodeCPUStats) * nparams)) == NULL)
 *           goto error;
 *       memset(params, 0, sizeof(virNodeCPUStats) * nparams);
 *       if (virNodeGetCPUStats(conn, cpuNum, params, &nparams, 0))
 *           goto error;
 *   }
 *
 * This function doesn't require privileged access to the hypervisor.
 * This function expects the caller to allocate the @params.
 *
 * CPU time Statistics:
 *
 * VIR_NODE_CPU_STATS_KERNEL:
 *     The cumulative CPU time which spends by kernel,
 *     when the node booting up.(nanoseconds)
 * VIR_NODE_CPU_STATS_USER:
 *     The cumulative CPU time which spends by user processes,
 *     when the node booting up.(nanoseconds)
 * VIR_NODE_CPU_STATS_IDLE:
 *     The cumulative idle CPU time, when the node booting up.(nanoseconds)
 * VIR_NODE_CPU_STATS_IOWAIT:
 *     The cumulative I/O wait CPU time, when the node booting up.(nanoseconds)
 * VIR_NODE_CPU_STATS_UTILIZATION:
 *     The CPU utilization. The usage value is in percent and 100%
 *     represents all CPUs on the server.
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virNodeGetCPUStats(virConnectPtr conn,
                   int cpuNum,
                   virNodeCPUStatsPtr params,
                   int *nparams, unsigned int flags)
{
    VIR_DEBUG("conn=%p, cpuNum=%d, params=%p, nparams=%d, flags=%x",
              conn, cpuNum, params, nparams ? *nparams : -1, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (cpuNum < 0 && cpuNum != VIR_NODE_CPU_STATS_ALL_CPUS) {
        virReportInvalidArg(cpuNum,
                            _("cpuNum in %s only accepts %d as a negative "
                              "value"),
                            __FUNCTION__, VIR_NODE_CPU_STATS_ALL_CPUS);
        goto error;
    }

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

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


/**
 * virNodeGetMemoryStats:
 * @conn: pointer to the hypervisor connection.
 * @cellNum: number of node cell. (VIR_NODE_MEMORY_STATS_ALL_CELLS means total
 *           cell statistics)
 * @params: pointer to node memory stats objects
 * @nparams: number of node memory stats (this value should be same or
 *          less than the number of stats supported)
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This function provides memory stats of the node.
 * If you want to get total memory statistics of the node, you must specify
 * VIR_NODE_MEMORY_STATS_ALL_CELLS to @cellNum.
 * The @params array will be filled with the values equal to the number of
 * stats suggested by @nparams
 *
 * As the value of @nparams is dynamic, call the API setting @nparams to 0 and
 * @params as NULL, the API returns the number of parameters supported by the
 * HV by updating @nparams on SUCCESS. The caller should then allocate @params
 * array, i.e. (sizeof(@virNodeMemoryStats) * @nparams) bytes and call
 * the API again.
 *
 * Here is the sample code snippet:
 *
 *   if (virNodeGetMemoryStats(conn, cellNum, NULL, &nparams, 0) == 0 &&
 *       nparams != 0) {
 *       if ((params = malloc(sizeof(virNodeMemoryStats) * nparams)) == NULL)
 *           goto error;
 *       memset(params, cellNum, 0, sizeof(virNodeMemoryStats) * nparams);
 *       if (virNodeGetMemoryStats(conn, params, &nparams, 0))
 *           goto error;
 *   }
 *
 * This function doesn't require privileged access to the hypervisor.
 * This function expects the caller to allocate the @params.
 *
 * Memory Stats:
 *
 * VIR_NODE_MEMORY_STATS_TOTAL:
 *     The total memory usage.(KB)
 * VIR_NODE_MEMORY_STATS_FREE:
 *     The free memory usage.(KB)
 *     On linux, this usage includes buffers and cached.
 * VIR_NODE_MEMORY_STATS_BUFFERS:
 *     The buffers memory usage.(KB)
 * VIR_NODE_MEMORY_STATS_CACHED:
 *     The cached memory usage.(KB)
 *
 * Returns -1 in case of error, 0 in case of success.
 */
int
virNodeGetMemoryStats(virConnectPtr conn,
                      int cellNum,
                      virNodeMemoryStatsPtr params,
                      int *nparams, unsigned int flags)
{
    VIR_DEBUG("conn=%p, cellNum=%d, params=%p, nparams=%d, flags=%x",
              conn, cellNum, params, nparams ? *nparams : -1, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(nparams, error);
    virCheckNonNegativeArgGoto(*nparams, error);
    if (cellNum < 0 && cellNum != VIR_NODE_MEMORY_STATS_ALL_CELLS) {
        virReportInvalidArg(cpuNum,
                            _("cellNum in %s only accepts %d as a negative "
                              "value"),
                            __FUNCTION__, VIR_NODE_MEMORY_STATS_ALL_CELLS);
        goto error;
    }

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

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


/**
 * virNodeGetFreeMemory:
 * @conn: pointer to the hypervisor connection
 *
 * provides the free memory available on the Node
 * Note: most libvirt APIs provide memory sizes in kibibytes, but in this
 * function the returned value is in bytes. Divide by 1024 as necessary.
 *
 * Returns the available free memory in bytes or 0 in case of error
 */
unsigned long long
virNodeGetFreeMemory(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, 0);

    if (conn->driver->nodeGetFreeMemory) {
        unsigned long long ret;
        ret = conn->driver->nodeGetFreeMemory(conn);
        if (ret == 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return 0;
}


/**
 * virNodeSuspendForDuration:
 * @conn: pointer to the hypervisor connection
 * @target: the state to which the host must be suspended to,
 *         such as: VIR_NODE_SUSPEND_TARGET_MEM (Suspend-to-RAM)
 *                  VIR_NODE_SUSPEND_TARGET_DISK (Suspend-to-Disk)
 *                  VIR_NODE_SUSPEND_TARGET_HYBRID (Hybrid-Suspend,
 *                  which is a combination of the former modes).
 * @duration: the time duration in seconds for which the host
 *            has to be suspended
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Attempt to suspend the node (host machine) for the given duration of
 * time in the specified state (Suspend-to-RAM, Suspend-to-Disk or
 * Hybrid-Suspend). Schedule the node's Real-Time-Clock interrupt to
 * resume the node after the duration is complete.
 *
 * Returns 0 on success (i.e., the node will be suspended after a short
 * delay), -1 on failure (the operation is not supported, or an attempted
 * suspend is already underway).
 */
int
virNodeSuspendForDuration(virConnectPtr conn,
                          unsigned int target,
                          unsigned long long duration,
                          unsigned int flags)
{
    VIR_DEBUG("conn=%p, target=%d, duration=%lld, flags=%x",
              conn, target, duration, flags);

    virResetLastError();

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

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

    virReportUnsupportedError();

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


/*
 * virNodeGetMemoryParameters:
 * @conn: pointer to the hypervisor connection
 * @params: pointer to memory parameter object
 *          (return value, allocated by the caller)
 * @nparams: pointer to number of memory parameters; input and output
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Get all node memory parameters (parameters unsupported by OS will be
 * omitted).  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.
 *
 * Returns 0 in case of success, and -1 in case of failure.
 */
int
virNodeGetMemoryParameters(virConnectPtr conn,
                           virTypedParameterPtr params,
                           int *nparams,
                           unsigned int flags)
{
    VIR_DEBUG("conn=%p, params=%p, nparams=%p, flags=%x",
              conn, params, nparams, flags);

    virResetLastError();

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

    if (VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn,
                                 VIR_DRV_FEATURE_TYPED_PARAM_STRING))
        flags |= VIR_TYPED_PARAM_STRING_OKAY;

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

    virReportUnsupportedError();

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


/*
 * virNodeSetMemoryParameters:
 * @conn: pointer to the hypervisor connection
 * @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: extra flags; not used yet, so callers should always pass 0
 *
 * Change all or a subset of the node memory tunables. The function
 * fails if not all of the tunables are supported.
 *
 * Note that it's not recommended to use this function while the
 * outside tuning program is running (such as ksmtuned under Linux),
 * as they could change the tunables in parallel, which could cause
 * conflicts.
 *
 * This function may require privileged access to the hypervisor.
 *
 * Returns 0 in case of success, -1 in case of failure.
 */
int
virNodeSetMemoryParameters(virConnectPtr conn,
                           virTypedParameterPtr params,
                           int nparams,
                           unsigned int flags)
{
    VIR_DEBUG("conn=%p, params=%p, nparams=%d, flags=%x",
              conn, params, nparams, flags);
    VIR_TYPED_PARAMS_DEBUG(params, nparams);

    virResetLastError();

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

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

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

    virReportUnsupportedError();

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


/**
 * virNodeGetSecurityModel:
 * @conn: a connection object
 * @secmodel: pointer to a virSecurityModel structure
 *
 * Extract the security model of a hypervisor. The 'model' field
 * in the @secmodel argument may be initialized to the empty
 * string if the driver has not activated a security model.
 *
 * Returns 0 in case of success, -1 in case of failure
 */
int
virNodeGetSecurityModel(virConnectPtr conn, virSecurityModelPtr secmodel)
{
    VIR_DEBUG("conn=%p secmodel=%p", conn, secmodel);

    virResetLastError();

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

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

    virReportUnsupportedError();

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


/**
 * virNodeGetCellsFreeMemory:
 * @conn: pointer to the hypervisor connection
 * @freeMems: pointer to the array of unsigned long long
 * @startCell: index of first cell to return freeMems info on.
 * @maxCells: Maximum number of cells for which freeMems information can
 *            be returned.
 *
 * This call returns the amount of free memory in one or more NUMA cells.
 * The @freeMems array must be allocated by the caller and will be filled
 * with the amount of free memory in bytes for each cell requested,
 * starting with startCell (in freeMems[0]), up to either
 * (startCell + maxCells), or the number of additional cells in the node,
 * whichever is smaller.
 *
 * Returns the number of entries filled in freeMems, or -1 in case of error.
 */
int
virNodeGetCellsFreeMemory(virConnectPtr conn, unsigned long long *freeMems,
                          int startCell, int maxCells)
{
    VIR_DEBUG("conn=%p, freeMems=%p, startCell=%d, maxCells=%d",
          conn, freeMems, startCell, maxCells);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonNullArgGoto(freeMems, error);
    virCheckPositiveArgGoto(maxCells, error);
    virCheckNonNegativeArgGoto(startCell, error);

    if (conn->driver->nodeGetCellsFreeMemory) {
        int ret;
        ret = conn->driver->nodeGetCellsFreeMemory(conn, freeMems, startCell, maxCells);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virConnectIsEncrypted:
 * @conn: pointer to the connection object
 *
 * Determine if the connection to the hypervisor is encrypted
 *
 * Returns 1 if encrypted, 0 if not encrypted, -1 on error
 */
int
virConnectIsEncrypted(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    if (conn->driver->connectIsEncrypted) {
        int ret;
        ret = conn->driver->connectIsEncrypted(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

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


/**
 * virConnectIsSecure:
 * @conn: pointer to the connection object
 *
 * Determine if the connection to the hypervisor is secure
 *
 * A connection will be classed as secure if it is either
 * encrypted, or running over a channel which is not exposed
 * to eavesdropping (eg a UNIX domain socket, or pipe)
 *
 * Returns 1 if secure, 0 if not secure, -1 on error
 */
int
virConnectIsSecure(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    if (conn->driver->connectIsSecure) {
        int ret;
        ret = conn->driver->connectIsSecure(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

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


/**
 * virConnectCompareCPU:
 * @conn: virConnect connection
 * @xmlDesc: XML describing the CPU to compare with host CPU
 * @flags: bitwise-OR of virConnectCompareCPUFlags
 *
 * Compares the given CPU description with the host CPU
 *
 * Returns comparison result according to enum virCPUCompareResult. If
 * VIR_CONNECT_COMPARE_CPU_FAIL_INCOMPATIBLE is used and @xmlDesc CPU is
 * incompatible with host CPU, this function will return VIR_CPU_COMPARE_ERROR
 * (instead of VIR_CPU_COMPARE_INCOMPATIBLE) and the error will use the
 * VIR_ERR_CPU_INCOMPATIBLE code with a message providing more details about
 * the incompatibility.
 */
int
virConnectCompareCPU(virConnectPtr conn,
                     const char *xmlDesc,
                     unsigned int flags)
{
    VIR_DEBUG("conn=%p, xmlDesc=%s, flags=%x", conn, NULLSTR(xmlDesc), flags);

    virResetLastError();

    virCheckConnectReturn(conn, VIR_CPU_COMPARE_ERROR);
    virCheckNonNullArgGoto(xmlDesc, error);

    if (conn->driver->connectCompareCPU) {
        int ret;

        ret = conn->driver->connectCompareCPU(conn, xmlDesc, flags);
        if (ret == VIR_CPU_COMPARE_ERROR)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return VIR_CPU_COMPARE_ERROR;
}


/**
 * virConnectGetCPUModelNames:
 *
 * @conn: virConnect connection
 * @arch: Architecture
 * @models: Pointer to a variable to store the NULL-terminated array of the
 *          CPU models supported for the specified architecture.  Each element
 *          and the array itself must be freed by the caller with free.  Pass
 *          NULL if only the list length is needed.
 * @flags: extra flags; not used yet, so callers should always pass 0.
 *
 * Get the list of supported CPU models for a specific architecture.
 *
 * Returns -1 on error, number of elements in @models on success.
 */
int
virConnectGetCPUModelNames(virConnectPtr conn, const char *arch, char ***models,
                           unsigned int flags)
{
    VIR_DEBUG("conn=%p, arch=%s, models=%p, flags=%x",
              conn, NULLSTR(arch), models, flags);
    virResetLastError();

    if (models)
        *models = NULL;

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

    if (conn->driver->connectGetCPUModelNames) {
        int ret;

        ret = conn->driver->connectGetCPUModelNames(conn, arch, models, flags);
        if (ret < 0)
            goto error;

        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virConnectBaselineCPU:
 *
 * @conn: virConnect connection
 * @xmlCPUs: array of XML descriptions of host CPUs
 * @ncpus: number of CPUs in xmlCPUs
 * @flags: bitwise-OR of virConnectBaselineCPUFlags
 *
 * Computes the most feature-rich CPU which is compatible with all given
 * host CPUs.
 *
 * If @flags includes VIR_CONNECT_BASELINE_CPU_EXPAND_FEATURES then libvirt
 * will explicitly list all CPU features that are part of the host CPU,
 * without this flag features that are part of the CPU model will not be
 * listed.
 *
 * If @flags includes VIR_CONNECT_BASELINE_CPU_MIGRATABLE, the resulting
 * CPU will not include features that block migration.
 *
 * Returns XML description of the computed CPU (caller frees) or NULL on error.
 */
char *
virConnectBaselineCPU(virConnectPtr conn,
                      const char **xmlCPUs,
                      unsigned int ncpus,
                      unsigned int flags)
{
    size_t i;

    VIR_DEBUG("conn=%p, xmlCPUs=%p, ncpus=%u, flags=%x",
              conn, xmlCPUs, ncpus, flags);
    if (xmlCPUs) {
        for (i = 0; i < ncpus; i++)
            VIR_DEBUG("xmlCPUs[%zu]=%s", i, NULLSTR(xmlCPUs[i]));
    }

    virResetLastError();

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

    if (conn->driver->connectBaselineCPU) {
        char *cpu;

        cpu = conn->driver->connectBaselineCPU(conn, xmlCPUs, ncpus, flags);
        if (!cpu)
            goto error;
        return cpu;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virConnectSetKeepAlive:
 * @conn: pointer to a hypervisor connection
 * @interval: number of seconds of inactivity before a keepalive message is sent
 * @count: number of messages that can be sent in a row
 *
 * Start sending keepalive messages after @interval seconds of inactivity and
 * consider the connection to be broken when no response is received after
 * @count keepalive messages sent in a row.  In other words, sending count + 1
 * keepalive message results in closing the connection.  When @interval is
 * <= 0, no keepalive messages will be sent.  When @count is 0, the connection
 * will be automatically closed after @interval seconds of inactivity without
 * sending any keepalive messages.
 *
 * Note: The client has to implement and run an event loop with
 * virEventRegisterImpl() or virEventRegisterDefaultImpl() to be able to
 * use keepalive messages.  Failure to do so may result in connections
 * being closed unexpectedly.
 *
 * Note: This API function controls only keepalive messages sent by the client.
 * If the server is configured to use keepalive you still need to run the event
 * loop to respond to them, even if you disable keepalives by this function.
 *
 * Returns -1 on error, 0 on success, 1 when remote party doesn't support
 * keepalive messages.
 */
int
virConnectSetKeepAlive(virConnectPtr conn,
                       int interval,
                       unsigned int count)
{
    int ret = -1;

    VIR_DEBUG("conn=%p, interval=%d, count=%u", conn, interval, count);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    if (conn->driver->connectSetKeepAlive) {
        ret = conn->driver->connectSetKeepAlive(conn, interval, count);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virConnectIsAlive:
 * @conn: pointer to the connection object
 *
 * Determine if the connection to the hypervisor is still alive
 *
 * A connection will be classed as alive if it is either local, or running
 * over a channel (TCP or UNIX socket) which is not closed.
 *
 * Returns 1 if alive, 0 if dead, -1 on error
 */
int
virConnectIsAlive(virConnectPtr conn)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    if (conn->driver->connectIsAlive) {
        int ret;
        ret = conn->driver->connectIsAlive(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

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


/**
 * virConnectRegisterCloseCallback:
 * @conn: pointer to connection object
 * @cb: callback to invoke upon close
 * @opaque: user data to pass to @cb
 * @freecb: callback to free @opaque
 *
 * Registers a callback to be invoked when the connection
 * is closed. This callback is invoked when there is any
 * condition that causes the socket connection to the
 * hypervisor to be closed.
 *
 * This function is only applicable to hypervisor drivers
 * which maintain a persistent open connection. Drivers
 * which open a new connection for every operation will
 * not invoke this.
 *
 * The @freecb must not invoke any other libvirt public
 * APIs, since it is not called from a re-entrant safe
 * context.
 *
 * Returns 0 on success, -1 on error
 */
int
virConnectRegisterCloseCallback(virConnectPtr conn,
                                virConnectCloseFunc cb,
                                void *opaque,
                                virFreeCallback freecb)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    virObjectRef(conn);

    virObjectLock(conn);
    virObjectLock(conn->closeCallback);

    virCheckNonNullArgGoto(cb, error);

    if (conn->closeCallback->callback) {
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                       _("A close callback is already registered"));
        goto error;
    }

    conn->closeCallback->conn = conn;
    conn->closeCallback->callback = cb;
    conn->closeCallback->opaque = opaque;
    conn->closeCallback->freeCallback = freecb;

    virObjectUnlock(conn->closeCallback);
    virObjectUnlock(conn);

    return 0;

 error:
    virObjectUnlock(conn->closeCallback);
    virObjectUnlock(conn);
    virDispatchError(conn);
    virObjectUnref(conn);
    return -1;
}


/**
 * virConnectUnregisterCloseCallback:
 * @conn: pointer to connection object
 * @cb: pointer to the current registered callback
 *
 * Unregisters the callback previously set with the
 * virConnectRegisterCloseCallback method. The callback
 * will no longer receive notifications when the connection
 * closes. If a virFreeCallback was provided at time of
 * registration, it will be invoked
 *
 * Returns 0 on success, -1 on error
 */
int
virConnectUnregisterCloseCallback(virConnectPtr conn,
                                  virConnectCloseFunc cb)
{
    VIR_DEBUG("conn=%p", conn);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    virObjectLock(conn);
    virObjectLock(conn->closeCallback);

    virCheckNonNullArgGoto(cb, error);

    if (conn->closeCallback->callback != cb) {
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                       _("A different callback was requested"));
        goto error;
    }

    conn->closeCallback->callback = NULL;
    if (conn->closeCallback->freeCallback)
        conn->closeCallback->freeCallback(conn->closeCallback->opaque);
    conn->closeCallback->freeCallback = NULL;

    virObjectUnlock(conn->closeCallback);
    virObjectUnlock(conn);
    virObjectUnref(conn);

    return 0;

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


/**
 * virNodeGetCPUMap:
 * @conn: pointer to the hypervisor connection
 * @cpumap: optional pointer to a bit map of real CPUs on the host node
 *      (in 8-bit bytes) (OUT)
 *      In case of success each bit set to 1 means that corresponding
 *      CPU is online.
 *      Bytes are stored in little-endian order: CPU0-7, 8-15...
 *      In each byte, lowest CPU number is least significant bit.
 *      The bit map is allocated by virNodeGetCPUMap and needs
 *      to be released using free() by the caller.
 * @online: optional number of online CPUs in cpumap (OUT)
 *      Contains the number of online CPUs if the call was successful.
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Get CPU map of host node CPUs.
 *
 * Returns number of CPUs present on the host node,
 * or -1 if there was an error.
 */
int
virNodeGetCPUMap(virConnectPtr conn,
                 unsigned char **cpumap,
                 unsigned int *online,
                 unsigned int flags)
{
    VIR_DEBUG("conn=%p, cpumap=%p, online=%p, flags=%x",
              conn, cpumap, online, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);

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

    virReportUnsupportedError();

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


/**
 * virNodeGetFreePages:
 * @conn: pointer to the hypervisor connection
 * @npages: number of items in the @pages array
 * @pages: page sizes to query
 * @startCell: index of first cell to return free pages info on.
 * @cellCount: maximum number of cells for which free pages
 *             information can be returned.
 * @counts: returned counts of free pages
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * This calls queries the host system on free pages of
 * specified size. For the input, @pages is expected to be
 * filled with pages that caller is interested in (the size
 * unit is kibibytes, so e.g. pass 2048 for 2MB), then @startcell
 * refers to the first NUMA node that info should be collected
 * from, and @cellcount tells how many consecutive nodes should
 * be queried. On the function output, @counts is filled with
 * desired information, where items are grouped by NUMA node.
 * So from @counts[0] till @counts[@npages - 1] you'll find count
 * for the first node (@startcell), then from @counts[@npages]
 * till @count[2 * @npages - 1] you'll find info for the
 * (@startcell + 1) node, and so on. It's callers responsibility
 * to allocate the @counts array.
 *
 * Example how to use this API:
 *
 *   unsigned int pages[] = { 4, 2048, 1048576}
 *   unsigned int npages = ARRAY_CARDINALITY(pages);
 *   int startcell = 0;
 *   unsigned int cellcount = 2;
 *
 *   unsigned long long counts = malloc(sizeof(long long) * npages * cellcount);
 *
 *   virNodeGetFreePages(conn, pages, npages,
 *                       startcell, cellcount, counts, 0);
 *
 *   for (i = 0 ; i < cellcount ; i++) {
 *       fprintf(stdout, "Cell %d\n", startcell + i);
 *       for (j = 0 ; j < npages ; j++) {
 *          fprintf(stdout, "  Page size=%d count=%d bytes=%llu\n",
 *                  pages[j], counts[(i * npages) +  j],
 *                  pages[j] * counts[(i * npages) +  j]);
 *       }
 *   }
 *
 *   This little code snippet will produce something like this:
 * Cell 0
 *    Page size=4096 count=300 bytes=1228800
 *    Page size=2097152 count=0 bytes=0
 *    Page size=1073741824 count=1 bytes=1073741824
 * Cell 1
 *    Page size=4096 count=0 bytes=0
 *    Page size=2097152 count=20 bytes=41943040
 *    Page size=1073741824 count=0 bytes=0
 *
 * Returns: the number of entries filled in @counts or -1 in case of error.
 */
int
virNodeGetFreePages(virConnectPtr conn,
                    unsigned int npages,
                    unsigned int *pages,
                    int startCell,
                    unsigned int cellCount,
                    unsigned long long *counts,
                    unsigned int flags)
{
    VIR_DEBUG("conn=%p, npages=%u, pages=%p, startCell=%u, "
              "cellCount=%u, counts=%p, flags=%x",
              conn, npages, pages, startCell, cellCount, counts, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckNonZeroArgGoto(npages, error);
    virCheckNonNullArgGoto(pages, error);
    virCheckNonZeroArgGoto(cellCount, error);
    virCheckNonNullArgGoto(counts, error);

    if (conn->driver->nodeGetFreePages) {
        int ret;
        ret = conn->driver->nodeGetFreePages(conn, npages, pages, startCell,
                                             cellCount, counts, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

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


/**
 * virNodeAllocPages:
 * @conn: pointer to the hypervisor connection
 * @npages: number of items in the @pageSizes and
 *          @pageCounts arrays
 * @pageSizes: which huge page sizes to allocate
 * @pageCounts: how many pages should be allocated
 * @startCell: index of first cell to allocate pages on
 * @cellCount: number of consecutive cells to allocate pages on
 * @flags: extra flags; binary-OR of virNodeAllocPagesFlags
 *
 * Sometimes, when trying to start a new domain, it may be
 * necessary to reserve some huge pages in the system pool which
 * can be then allocated by the domain. This API serves that
 * purpose. On its input, @pageSizes and @pageCounts are arrays
 * of the same cardinality of @npages. The @pageSizes contains
 * page sizes which are to be allocated in the system (the size
 * unit is kibibytes), and @pageCounts then contains the number
 * of pages to reserve.  If @flags is 0
 * (VIR_NODE_ALLOC_PAGES_ADD), each pool corresponding to
 * @pageSizes grows by the number of pages specified in the
 * corresponding @pageCounts.  If @flags contains
 * VIR_NODE_ALLOC_PAGES_SET, each pool mentioned is resized to
 * the given number of pages.  The pages pool can be allocated
 * over several NUMA nodes at once, just point at @startCell and
 * tell how many subsequent NUMA nodes should be taken in. As a
 * special case, if @startCell is equal to negative one, then
 * kernel is instructed to allocate the pages over all NUMA nodes
 * proportionally.
 *
 * Returns: the number of nodes successfully adjusted or -1 in
 * case of an error.
 */
int
virNodeAllocPages(virConnectPtr conn,
                  unsigned int npages,
                  unsigned int *pageSizes,
                  unsigned long long *pageCounts,
                  int startCell,
                  unsigned int cellCount,
                  unsigned int flags)
{
    VIR_DEBUG("conn=%p npages=%u pageSizes=%p pageCounts=%p "
              "startCell=%d cellCount=%u flagx=%x",
              conn, npages, pageSizes, pageCounts, startCell,
              cellCount, flags);

    virResetLastError();

    virCheckConnectReturn(conn, -1);
    virCheckReadOnlyGoto(conn->flags, error);
    virCheckNonZeroArgGoto(npages, error);
    virCheckNonNullArgGoto(pageSizes, error);
    virCheckNonNullArgGoto(pageCounts, error);
    virCheckNonZeroArgGoto(cellCount, error);

    if (conn->driver->nodeAllocPages) {
        int ret;
        ret = conn->driver->nodeAllocPages(conn, npages, pageSizes,
                                           pageCounts, startCell,
                                           cellCount, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

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