/*
 * libvirt-nwfilter.c: entry points for virNwfilterPtr 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 "datatypes.h"
#include "virlog.h"

VIR_LOG_INIT("libvirt.nwfilter");

#define VIR_FROM_THIS VIR_FROM_NWFILTER


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

    virResetLastError();

    virCheckConnectReturn(conn, -1);

    if (conn->nwfilterDriver && conn->nwfilterDriver->connectNumOfNWFilters) {
        int ret;
        ret = conn->nwfilterDriver->connectNumOfNWFilters(conn);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virConnectListAllNWFilters:
 * @conn: Pointer to the hypervisor connection.
 * @filters: Pointer to a variable to store the array containing the network
 *           filter objects or NULL if the list is not required (just returns
 *           number of network filters).
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Collect the list of network filters, and allocate an array to store those
 * objects.
 *
 * Returns the number of network filters found or -1 and sets @filters to  NULL
 * in case of error.  On success, the array stored into @filters 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
 * virNWFilterFree() on each array element, then calling free() on @filters.
 */
int
virConnectListAllNWFilters(virConnectPtr conn,
                           virNWFilterPtr **filters,
                           unsigned int flags)
{
    VIR_DEBUG("conn=%p, filters=%p, flags=0x%x", conn, filters, flags);

    virResetLastError();

    if (filters)
        *filters = NULL;

    virCheckConnectReturn(conn, -1);

    if (conn->nwfilterDriver &&
        conn->nwfilterDriver->connectListAllNWFilters) {
        int ret;
        ret = conn->nwfilterDriver->connectListAllNWFilters(conn, filters, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virConnectListNWFilters:
 * @conn: pointer to the hypervisor connection
 * @names: array to collect the list of names of network filters
 * @maxnames: size of @names
 *
 * Collect the list of network filters, and store their names in @names
 *
 * Returns the number of network filters found or -1 in case of error
 */
int
virConnectListNWFilters(virConnectPtr conn, char **const names, int maxnames)
{
    VIR_DEBUG("conn=%p, names=%p, maxnames=%d", conn, names, maxnames);

    virResetLastError();

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

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

    virReportUnsupportedError();

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


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

    virResetLastError();

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

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterLookupByName) {
        virNWFilterPtr ret;
        ret = conn->nwfilterDriver->nwfilterLookupByName(conn, name);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


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

    virResetLastError();

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

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterLookupByUUID) {
        virNWFilterPtr ret;
        ret = conn->nwfilterDriver->nwfilterLookupByUUID(conn, uuid);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNWFilterLookupByUUIDString:
 * @conn: pointer to the hypervisor connection
 * @uuidstr: the string UUID for the nwfilter
 *
 * Try to lookup an nwfilter on the given hypervisor based on its UUID.
 *
 * virNWFilterFree should be used to free the resources after the
 * nwfilter object is no longer needed.
 *
 * Returns a new nwfilter object or NULL in case of failure.  If the
 * nwfilter cannot be found, then VIR_ERR_NO_NWFILTER error is raised.
 */
virNWFilterPtr
virNWFilterLookupByUUIDString(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,
                            _("uuidstr in %s must be a valid UUID"),
                            __FUNCTION__);
        goto error;
    }

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

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNWFilterFree:
 * @nwfilter: a nwfilter object
 *
 * Free the nwfilter 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
virNWFilterFree(virNWFilterPtr nwfilter)
{
    VIR_DEBUG("nwfilter=%p", nwfilter);

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, -1);

    virObjectUnref(nwfilter);
    return 0;
}


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

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, NULL);

    return nwfilter->name;
}


/**
 * virNWFilterGetUUID:
 * @nwfilter: a nwfilter object
 * @uuid: pointer to a VIR_UUID_BUFLEN bytes array
 *
 * Get the UUID for a network filter
 *
 * Returns -1 in case of error, 0 in case of success
 */
int
virNWFilterGetUUID(virNWFilterPtr nwfilter, unsigned char *uuid)
{
    VIR_DEBUG("nwfilter=%p, uuid=%p", nwfilter, uuid);

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, -1);
    virCheckNonNullArgGoto(uuid, error);

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

    return 0;

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


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

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, -1);
    virCheckNonNullArgGoto(buf, error);

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

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


/**
 * virNWFilterDefineXML:
 * @conn: pointer to the hypervisor connection
 * @xmlDesc: an XML description of the nwfilter
 *
 * Define a new network filter, based on an XML description
 * similar to the one returned by virNWFilterGetXMLDesc()
 *
 * virNWFilterFree should be used to free the resources after the
 * nwfilter object is no longer needed.
 *
 * Returns a new nwfilter object or NULL in case of failure
 */
virNWFilterPtr
virNWFilterDefineXML(virConnectPtr conn, const char *xmlDesc)
{
    VIR_DEBUG("conn=%p, xmlDesc=%s", conn, NULLSTR(xmlDesc));

    virResetLastError();

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

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterDefineXML) {
        virNWFilterPtr ret;
        ret = conn->nwfilterDriver->nwfilterDefineXML(conn, xmlDesc);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNWFilterUndefine:
 * @nwfilter: a nwfilter object
 *
 * Undefine the nwfilter object. This call will not succeed if
 * a running VM is referencing the filter. This does not free the
 * associated virNWFilterPtr object.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virNWFilterUndefine(virNWFilterPtr nwfilter)
{
    virConnectPtr conn;
    VIR_DEBUG("nwfilter=%p", nwfilter);

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, -1);
    conn = nwfilter->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterUndefine) {
        int ret;
        ret = conn->nwfilterDriver->nwfilterUndefine(nwfilter);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virNWFilterGetXMLDesc:
 * @nwfilter: a nwfilter object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Provide an XML description of the network filter. The description may be
 * reused later to redefine the network filter with virNWFilterCreateXML().
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case
 * of error. The caller must free() the returned value.
 */
char *
virNWFilterGetXMLDesc(virNWFilterPtr nwfilter, unsigned int flags)
{
    virConnectPtr conn;
    VIR_DEBUG("nwfilter=%p, flags=0x%x", nwfilter, flags);

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, NULL);
    conn = nwfilter->conn;

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterGetXMLDesc) {
        char *ret;
        ret = conn->nwfilterDriver->nwfilterGetXMLDesc(nwfilter, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


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

    virResetLastError();

    virCheckNWFilterReturn(nwfilter, -1);

    virObjectRef(nwfilter);
    return 0;
}


/**
 * virConnectListAllNWFilterBindings:
 * @conn: Pointer to the hypervisor connection.
 * @bindings: Pointer to a variable to store the array containing the network
 *            filter objects or NULL if the list is not required (just returns
 *            number of network filters).
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Collect the list of network filters, and allocate an array to store those
 * objects.
 *
 * Returns the number of network filters found or -1 and sets @filters to  NULL
 * in case of error.  On success, the array stored into @filters 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
 * virNWFilterFree() on each array element, then calling free() on @filters.
 */
int
virConnectListAllNWFilterBindings(virConnectPtr conn,
                                  virNWFilterBindingPtr **bindings,
                                  unsigned int flags)
{
    VIR_DEBUG("conn=%p, bindings=%p, flags=0x%x", conn, bindings, flags);

    virResetLastError();

    if (bindings)
        *bindings = NULL;

    virCheckConnectReturn(conn, -1);

    if (conn->nwfilterDriver &&
        conn->nwfilterDriver->connectListAllNWFilterBindings) {
        int ret;
        ret = conn->nwfilterDriver->connectListAllNWFilterBindings(conn, bindings, flags);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virNWFilterBindingLookupByPortDev:
 * @conn: pointer to the hypervisor connection
 * @portdev: name for the network port device
 *
 * Try to lookup a network filter binding on the given hypervisor based
 * on network port device name.
 *
 * virNWFilterBindingFree should be used to free the resources after the
 * binding object is no longer needed.
 *
 * Returns a new binding object or NULL in case of failure.  If the
 * network filter cannot be found, then VIR_ERR_NO_NWFILTER_BINDING
 * error is raised.
 */
virNWFilterBindingPtr
virNWFilterBindingLookupByPortDev(virConnectPtr conn, const char *portdev)
{
    VIR_DEBUG("conn=%p, name=%s", conn, NULLSTR(portdev));

    virResetLastError();

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

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterBindingLookupByPortDev) {
        virNWFilterBindingPtr ret;
        ret = conn->nwfilterDriver->nwfilterBindingLookupByPortDev(conn, portdev);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNWFilterBindingFree:
 * @binding: a binding object
 *
 * Free the binding 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
virNWFilterBindingFree(virNWFilterBindingPtr binding)
{
    VIR_DEBUG("binding=%p", binding);

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, -1);

    virObjectUnref(binding);
    return 0;
}


/**
 * virNWFilterBindingGetPortDev:
 * @binding: a binding object
 *
 * Get the port dev name for the network filter binding
 *
 * Returns a pointer to the name or NULL, the string need not be deallocated
 * its lifetime will be the same as the binding object.
 */
const char *
virNWFilterBindingGetPortDev(virNWFilterBindingPtr binding)
{
    VIR_DEBUG("binding=%p", binding);

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, NULL);

    return binding->portdev;
}


/**
 * virNWFilterBindingGetFilterName:
 * @binding: a binding object
 *
 * Get the filter name for the network filter binding
 *
 * Returns a pointer to the name or NULL, the string need not be deallocated
 * its lifetime will be the same as the binding object.
 */
const char *
virNWFilterBindingGetFilterName(virNWFilterBindingPtr binding)
{
    VIR_DEBUG("binding=%p", binding);

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, NULL);

    return binding->filtername;
}


/**
 * virNWFilterBindingCreateXML:
 * @conn: pointer to the hypervisor connection
 * @xml: an XML description of the binding
 * @flags: currently unused, pass 0
 *
 * Define a new network filter, based on an XML description
 * similar to the one returned by virNWFilterGetXMLDesc(). This
 * API may be used to associate a filter with a currently running
 * guest that does not have a filter defined for a specific network
 * port. Since the bindings are generally automatically managed by
 * the hypervisor, using this command to define a filter for a network
 * port and then starting the guest afterwards may prevent the guest
 * from starting if it attempts to use the network port and finds a
 * filter already defined.
 *
 * virNWFilterFree should be used to free the resources after the
 * binding object is no longer needed.
 *
 * Returns a new binding object or NULL in case of failure
 */
virNWFilterBindingPtr
virNWFilterBindingCreateXML(virConnectPtr conn, const char *xml, unsigned int flags)
{
    VIR_DEBUG("conn=%p, xml=%s", conn, NULLSTR(xml));

    virResetLastError();

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

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterBindingCreateXML) {
        virNWFilterBindingPtr ret;
        ret = conn->nwfilterDriver->nwfilterBindingCreateXML(conn, xml, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

 error:
    virDispatchError(conn);
    return NULL;
}


/**
 * virNWFilterBindingDelete:
 * @binding: a binding object
 *
 * Delete the binding object. This does not free the
 * associated virNWFilterBindingPtr object. This API
 * may be used to remove the network port binding filter
 * currently in use for the guest while the guest is
 * running without needing to restart the guest. Restoring
 * the network port binding filter for the running guest
 * would be accomplished by using virNWFilterBindingCreateXML.
 *
 * Returns 0 in case of success and -1 in case of failure.
 */
int
virNWFilterBindingDelete(virNWFilterBindingPtr binding)
{
    virConnectPtr conn;
    VIR_DEBUG("binding=%p", binding);

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, -1);
    conn = binding->conn;

    virCheckReadOnlyGoto(conn->flags, error);

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterBindingDelete) {
        int ret;
        ret = conn->nwfilterDriver->nwfilterBindingDelete(binding);
        if (ret < 0)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


/**
 * virNWFilterBindingGetXMLDesc:
 * @binding: a binding object
 * @flags: extra flags; not used yet, so callers should always pass 0
 *
 * Provide an XML description of the network filter. The description may be
 * reused later to redefine the network filter with virNWFilterCreateXML().
 *
 * Returns a 0 terminated UTF-8 encoded XML instance, or NULL in case
 * of error. The caller must free() the returned value.
 */
char *
virNWFilterBindingGetXMLDesc(virNWFilterBindingPtr binding, unsigned int flags)
{
    virConnectPtr conn;
    VIR_DEBUG("binding=%p, flags=0x%x", binding, flags);

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, NULL);
    conn = binding->conn;

    if (conn->nwfilterDriver && conn->nwfilterDriver->nwfilterBindingGetXMLDesc) {
        char *ret;
        ret = conn->nwfilterDriver->nwfilterBindingGetXMLDesc(binding, flags);
        if (!ret)
            goto error;
        return ret;
    }

    virReportUnsupportedError();

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


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

    virResetLastError();

    virCheckNWFilterBindingReturn(binding, -1);

    virObjectRef(binding);
    return 0;
}
