<commit_before>/*
 * virnetworkobj.c: handle network objects
 *                  (derived from network_conf.c)
 *
 * 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 <dirent.h>

#include "datatypes.h"
#include "virnetworkobj.h"

#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virhash.h"
#include "virlog.h"
#include "virstring.h"

#define VIR_FROM_THIS VIR_FROM_NETWORK

VIR_LOG_INIT("conf.virnetworkobj");

/* Currently, /sbin/tc implementation allows up to 16 bits for
 * minor class size. But the initial bitmap doesn't have to be
 * that big. */
#define INIT_CLASS_ID_BITMAP_SIZE (1<<4)

struct _virNetworkObj {
    virObjectLockable parent;

    pid_t dnsmasqPid;
    pid_t radvdPid;
    bool active;
    bool autostart;
    bool persistent;

    virNetworkDefPtr def; /* The current definition */
    virNetworkDefPtr newDef; /* New definition to activate at shutdown */

    virBitmapPtr classIdMap; /* bitmap of class IDs for QoS */
    unsigned long long floor_sum; /* sum of all 'floor'-s of attached NICs */

    unsigned int taint;

    /* Immutable pointer, self locking APIs */
    virMacMapPtr macmap;

    GHashTable *ports; /* uuid -> virNetworkPortDefPtr */
};

struct _virNetworkObjList {
    virObjectRWLockable parent;

    GHashTable *objs;
};

static virClassPtr virNetworkObjClass;
static virClassPtr virNetworkObjListClass;
static void virNetworkObjDispose(void *obj);
static void virNetworkObjListDispose(void *obj);

static int
virNetworkObjOnceInit(void)
{
    if (!VIR_CLASS_NEW(virNetworkObj, virClassForObjectLockable()))
        return -1;

    if (!VIR_CLASS_NEW(virNetworkObjList, virClassForObjectRWLockable()))
        return -1;

    return 0;
}


VIR_ONCE_GLOBAL_INIT(virNetworkObj);

static int
virNetworkObjLoadAllPorts(virNetworkObjPtr net,
                          const char *stateDir);


static void
virNetworkObjPortFree(void *val)
{
    virNetworkPortDefFree(val);
}

virNetworkObjPtr
virNetworkObjNew(void)
{
    virNetworkObjPtr obj;

    if (virNetworkObjInitialize() < 0)
        return NULL;

    if (!(obj = virObjectLockableNew(virNetworkObjClass)))
        return NULL;

    obj->classIdMap = virBitmapNew(INIT_CLASS_ID_BITMAP_SIZE);

    /* The first three class IDs are already taken */
    if (virBitmapSetBitExpand(obj->classIdMap, 0) < 0 ||
        virBitmapSetBitExpand(obj->classIdMap, 1) < 0 ||
        virBitmapSetBitExpand(obj->classIdMap, 2) < 0)
        goto error;

    if (!(obj->ports = virHashNew(virNetworkObjPortFree)))
        goto error;

    virObjectLock(obj);

    return obj;

 error:
    virObjectUnref(obj);
    return NULL;
}


void
virNetworkObjEndAPI(virNetworkObjPtr *obj)
{
    if (!*obj)
        return;

    virObjectUnlock(*obj);
    virObjectUnref(*obj);
    *obj = NULL;
}


virNetworkDefPtr
virNetworkObjGetDef(virNetworkObjPtr obj)
{
    return obj->def;
}


void
virNetworkObjSetDef(virNetworkObjPtr obj,
                    virNetworkDefPtr def)
{
    obj->def = def;
}


virNetworkDefPtr
virNetworkObjGetNewDef(virNetworkObjPtr obj)
{
    return obj->newDef;
}


bool
virNetworkObjIsActive(virNetworkObjPtr obj)
{
    return obj->active;
}


void
virNetworkObjSetActive(virNetworkObjPtr obj,
                       bool active)
{
    obj->active = active;
}


bool
virNetworkObjIsPersistent(virNetworkObjPtr obj)
{
    return obj->persistent;
}


bool
virNetworkObjIsAutostart(virNetworkObjPtr obj)
{
    return obj->autostart;
}


void
virNetworkObjSetAutostart(virNetworkObjPtr obj,
                          bool autostart)
{
    obj->autostart = autostart;
}


pid_t
virNetworkObjGetDnsmasqPid(virNetworkObjPtr obj)
{
    return obj->dnsmasqPid;
}


void
virNetworkObjSetDnsmasqPid(virNetworkObjPtr obj,
                           pid_t dnsmasqPid)
{
    obj->dnsmasqPid = dnsmasqPid;
}


pid_t
virNetworkObjGetRadvdPid(virNetworkObjPtr obj)
{
    return obj->radvdPid;
}


void
virNetworkObjSetRadvdPid(virNetworkObjPtr obj,
                         pid_t radvdPid)
{
    obj->radvdPid = radvdPid;
}


virBitmapPtr
virNetworkObjGetClassIdMap(virNetworkObjPtr obj)
{
    return obj->classIdMap;
}


virMacMapPtr
virNetworkObjGetMacMap(virNetworkObjPtr obj)
{
    return obj->macmap;
}


unsigned long long
virNetworkObjGetFloorSum(virNetworkObjPtr obj)
{
    return obj->floor_sum;
}


void
virNetworkObjSetFloorSum(virNetworkObjPtr obj,
                         unsigned long long floor_sum)
{
    obj->floor_sum = floor_sum;
}


void
virNetworkObjSetMacMap(virNetworkObjPtr obj,
                       virMacMapPtr macmap)
{
    obj->macmap = macmap;
}


void
virNetworkObjUnrefMacMap(virNetworkObjPtr obj)
{
    virObjectUnref(obj->macmap);
    obj->macmap = NULL;
}


int
virNetworkObjMacMgrAdd(virNetworkObjPtr obj,
                       const char *dnsmasqStateDir,
                       const char *domain,
                       const virMacAddr *mac)
{
    char macStr[VIR_MAC_STRING_BUFLEN];
    char *file = NULL;
    int ret = -1;

    if (!obj->macmap)
        return 0;

    virMacAddrFormat(mac, macStr);

    if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
        goto cleanup;

    if (virMacMapAdd(obj->macmap, domain, macStr) < 0)
        goto cleanup;

    if (virMacMapWriteFile(obj->macmap, file) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(file);
    return ret;
}


int
virNetworkObjMacMgrDel(virNetworkObjPtr obj,
                       const char *dnsmasqStateDir,
                       const char *domain,
                       const virMacAddr *mac)
{
    char macStr[VIR_MAC_STRING_BUFLEN];
    char *file = NULL;
    int ret = -1;

    if (!obj->macmap)
        return 0;

    virMacAddrFormat(mac, macStr);

    if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
        goto cleanup;

    if (virMacMapRemove(obj->macmap, domain, macStr) < 0)
        goto cleanup;

    if (virMacMapWriteFile(obj->macmap, file) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(file);
    return ret;
}


virNetworkObjListPtr
virNetworkObjListNew(void)
{
    virNetworkObjListPtr nets;

    if (virNetworkObjInitialize() < 0)
        return NULL;

    if (!(nets = virObjectRWLockableNew(virNetworkObjListClass)))
        return NULL;

    if (!(nets->objs = virHashNew(virObjectFreeHashData))) {
        virObjectUnref(nets);
        return NULL;
    }

    return nets;
}


static virNetworkObjPtr
virNetworkObjFindByUUIDLocked(virNetworkObjListPtr nets,
                              const unsigned char *uuid)
{
    virNetworkObjPtr obj = NULL;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    virUUIDFormat(uuid, uuidstr);

    obj = virHashLookup(nets->objs, uuidstr);
    if (obj)
        virObjectRef(obj);
    return obj;
}


/**
 * virNetworkObjFindByUUID:
 * @nets: list of network objects
 * @uuid: network uuid to find
 *
 * This functions locks @nets and find network object which
 * corresponds to @uuid.
 *
 * Returns: locked and ref'd network object.
 */
virNetworkObjPtr
virNetworkObjFindByUUID(virNetworkObjListPtr nets,
                        const unsigned char *uuid)
{
    virNetworkObjPtr obj;

    virObjectRWLockRead(nets);
    obj = virNetworkObjFindByUUIDLocked(nets, uuid);
    virObjectRWUnlock(nets);
    if (obj)
        virObjectLock(obj);
    return obj;
}


static int
virNetworkObjSearchName(const void *payload,
                        const char *name G_GNUC_UNUSED,
                        const void *data)
{
    virNetworkObjPtr obj = (virNetworkObjPtr) payload;
    int want = 0;

    virObjectLock(obj);
    if (STREQ(obj->def->name, (const char *)data))
        want = 1;
    virObjectUnlock(obj);
    return want;
}


static virNetworkObjPtr
virNetworkObjFindByNameLocked(virNetworkObjListPtr nets,
                              const char *name)
{
    virNetworkObjPtr obj = NULL;

    obj = virHashSearch(nets->objs, virNetworkObjSearchName, name, NULL);
    if (obj)
        virObjectRef(obj);
    return obj;
}


/**
 * virNetworkObjFindByName:
 * @nets: list of network objects
 * @name: network name to find
 *
 * This functions locks @nets and find network object which
 * corresponds to @name.
 *
 * Returns: locked and ref'd network object.
 */
virNetworkObjPtr
virNetworkObjFindByName(virNetworkObjListPtr nets,
                        const char *name)
{
    virNetworkObjPtr obj;

    virObjectRWLockRead(nets);
    obj = virNetworkObjFindByNameLocked(nets, name);
    virObjectRWUnlock(nets);
    if (obj)
        virObjectLock(obj);
    return obj;
}


bool
virNetworkObjTaint(virNetworkObjPtr obj,
                   virNetworkTaintFlags taint)
{
    unsigned int flag = (1 << taint);

    if (obj->taint & flag)
        return false;

    obj->taint |= flag;
    return true;
}


static void
virNetworkObjDispose(void *opaque)
{
    virNetworkObjPtr obj = opaque;

    virHashFree(obj->ports);
    virNetworkDefFree(obj->def);
    virNetworkDefFree(obj->newDef);
    virBitmapFree(obj->classIdMap);
    virObjectUnref(obj->macmap);
}


static void
virNetworkObjListDispose(void *opaque)
{
    virNetworkObjListPtr nets = opaque;

    virHashFree(nets->objs);
}


/*
 * virNetworkObjUpdateAssignDef:
 * @network: the network object to update
 * @def: the new NetworkDef (will be consumed by this function)
 * @live: is this new def the "live" version, or the "persistent" version
 *
 * Replace the appropriate copy of the given network's def or newDef
 * with def. Use "live" and current state of the network to determine
 * which to replace and what to do with the old defs. When a non-live
 * def is set, indicate that the network is now persistent.
 *
 * NB: a persistent network can be made transient by calling with:
 * virNetworkObjAssignDef(network, NULL, false) (i.e. set the
 * persistent def to NULL)
 *
 */
void
virNetworkObjUpdateAssignDef(virNetworkObjPtr obj,
                             virNetworkDefPtr def,
                             bool live)
{
    if (live) {
        /* before setting new live def, save (into newDef) any
         * existing persistent (!live) def to be restored when the
         * network is destroyed, unless there is one already saved.
         */
        if (obj->persistent && !obj->newDef)
            obj->newDef = obj->def;
        else
            virNetworkDefFree(obj->def);
        obj->def = def;
    } else { /* !live */
        virNetworkDefFree(obj->newDef);
        if (virNetworkObjIsActive(obj)) {
            /* save new configuration to be restored on network
             * shutdown, leaving current live def alone
             */
            obj->newDef = def;
        } else { /* !live and !active */
            if (obj->def && !obj->persistent) {
                /* network isn't (yet) marked active or persistent,
                 * but already has a "live" def set. This means we are
                 * currently setting the persistent def as a part of
                 * the process of starting the network, so we need to
                 * preserve the "not yet live" def in network->def.
                 */
                obj->newDef = def;
            } else {
                /* either there is no live def set, or this network
                 * was already set as persistent, so the proper thing
                 * is to overwrite network->def.
                 */
                obj->newDef = NULL;
                virNetworkDefFree(obj->def);
                obj->def = def;
            }
        }
        obj->persistent = !!def;
    }
}


/*
 * If flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE then this will
 * refuse updating an existing def if the current def is live
 *
 * If flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE then the @def being
 * added is assumed to represent a live config, not a future
 * inactive config
 *
 * If flags is zero, network is considered as inactive and persistent.
 */
static virNetworkObjPtr
virNetworkObjAssignDefLocked(virNetworkObjListPtr nets,
                             virNetworkDefPtr def,
                             unsigned int flags)
{
    virNetworkObjPtr obj;
    virNetworkObjPtr ret = NULL;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    /* See if a network with matching UUID already exists */
    if ((obj = virNetworkObjFindByUUIDLocked(nets, def->uuid))) {
        virObjectLock(obj);
        /* UUID matches, but if names don't match, refuse it */
        if (STRNEQ(obj->def->name, def->name)) {
            virUUIDFormat(obj->def->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("network '%s' is already defined with uuid %s"),
                           obj->def->name, uuidstr);
            goto cleanup;
        }

        if (flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE) {
            /* UUID & name match, but if network is already active, refuse it */
            if (virNetworkObjIsActive(obj)) {
                virReportError(VIR_ERR_OPERATION_INVALID,
                               _("network is already active as '%s'"),
                               obj->def->name);
                goto cleanup;
            }
        }

        virNetworkObjUpdateAssignDef(obj, def,
                                     !!(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE));
    } else {
        /* UUID does not match, but if a name matches, refuse it */
        if ((obj = virNetworkObjFindByNameLocked(nets, def->name))) {
            virObjectLock(obj);
            virUUIDFormat(obj->def->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("network '%s' already exists with uuid %s"),
                           def->name, uuidstr);
            goto cleanup;
        }

        if (!(obj = virNetworkObjNew()))
              goto cleanup;

        virUUIDFormat(def->uuid, uuidstr);
        if (virHashAddEntry(nets->objs, uuidstr, obj) < 0)
            goto cleanup;
        virObjectRef(obj);

        obj->def = def;
        obj->persistent = !(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE);
    }

    ret = g_steal_pointer(&obj);

 cleanup:
    virNetworkObjEndAPI(&obj);
    return ret;
}


/*
 * virNetworkObjAssignDef:
 * @nets: list of all networks
 * @def: the new NetworkDef (will be consumed by this function iff successful)
 * @flags: bitwise-OR of VIR_NETWORK_OBJ_LIST_ADD_* flags
 *
 * Either replace the appropriate copy of the NetworkDef with name
 * matching def->name or, if not found, create a new NetworkObj with
 * def. For an existing network, use "live" and current state of the
 * network to determine which to replace.
 *
 * Look at virNetworkObjAssignDefLocked() for @flags description.
 *
 * Returns NULL on error, virNetworkObjPtr on success.
 */
virNetworkObjPtr
virNetworkObjAssignDef(virNetworkObjListPtr nets,
                       virNetworkDefPtr def,
                       unsigned int flags)
{
    virNetworkObjPtr obj;

    virObjectRWLockWrite(nets);
    obj = virNetworkObjAssignDefLocked(nets, def, flags);
    virObjectRWUnlock(nets);
    return obj;
}


/*
 * virNetworkObjSetDefTransient:
 * @network: network object pointer
 * @live: if true, run this operation even for an inactive network.
 *   this allows freely updated network->def with runtime defaults
 *   before starting the network, which will be discarded on network
 *   shutdown. Any cleanup paths need to be sure to handle newDef if
 *   the network is never started.
 *
 * Mark the active network config as transient. Ensures live-only update
 * operations do not persist past network destroy.
 *
 * Returns 0 on success, -1 on failure
 */
int
virNetworkObjSetDefTransient(virNetworkObjPtr obj,
                             bool live,
                             virNetworkXMLOptionPtr xmlopt)
{
    if (!virNetworkObjIsActive(obj) && !live)
        return 0;

    if (!obj->persistent || obj->newDef)
        return 0;

    obj->newDef = virNetworkDefCopy(obj->def,
                                    xmlopt,
                                    VIR_NETWORK_XML_INACTIVE);
    return obj->newDef ? 0 : -1;
}


/* virNetworkObjUnsetDefTransient:
 *
 * This *undoes* what virNetworkObjSetDefTransient did.
 */
void
virNetworkObjUnsetDefTransient(virNetworkObjPtr obj)
{
    if (obj->newDef) {
        virNetworkDefFree(obj->def);
        obj->def = obj->newDef;
        obj->newDef = NULL;
    }
}


/*
 * virNetworkObjGetPersistentDef:
 * @network: network object pointer
 *
 * Return the persistent network configuration. If network is transient,
 * return the running config.
 *
 * Returns NULL on error, virNetworkDefPtr on success.
 */
virNetworkDefPtr
virNetworkObjGetPersistentDef(virNetworkObjPtr obj)
{
    if (obj->newDef)
        return obj->newDef;
    else
        return obj->def;
}


/*
 * virNetworkObjReplacePersistentDef:
 * @network: network object pointer
 * @def: new virNetworkDef to replace current persistent config
 *
 * Replace the "persistent" network configuration with the given new
 * virNetworkDef. This pays attention to whether or not the network
 * is active.
 *
 * Returns -1 on error, 0 on success
 */
int
virNetworkObjReplacePersistentDef(virNetworkObjPtr obj,
                                  virNetworkDefPtr def)
{
    if (virNetworkObjIsActive(obj)) {
        virNetworkDefFree(obj->newDef);
        obj->newDef = def;
    } else {
        virNetworkDefFree(obj->def);
        obj->def = def;
    }
    return 0;
}


/*
 * virNetworkObjConfigChangeSetup:
 *
 * 1) checks whether network state is consistent with the requested
 *    type of modification.
 *
 * 3) make sure there are separate "def" and "newDef" copies of
 *    networkDef if appropriate.
 *
 * Returns 0 on success, -1 on error.
 */
static int
virNetworkObjConfigChangeSetup(virNetworkObjPtr obj,
                               virNetworkXMLOptionPtr xmlopt,
                               unsigned int flags)
{
    bool isActive;

    isActive = virNetworkObjIsActive(obj);

    if (!isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) {
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                       _("network is not running"));
        return -1;
    }

    if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {
        if (!obj->persistent) {
            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                           _("cannot change persistent config of a "
                             "transient network"));
            return -1;
        }
        /* this should already have been done by the driver, but do it
         * anyway just in case.
         */
        if (isActive && (virNetworkObjSetDefTransient(obj, false, xmlopt) < 0))
            return -1;
    }

    return 0;
}


void
virNetworkObjRemoveInactive(virNetworkObjListPtr nets,
                            virNetworkObjPtr obj)
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    virUUIDFormat(obj->def->uuid, uuidstr);
    virObjectRef(obj);
    virObjectUnlock(obj);
    virObjectRWLockWrite(nets);
    virObjectLock(obj);
    virHashRemoveEntry(nets->objs, uuidstr);
    virObjectRWUnlock(nets);
    virObjectUnref(obj);
}


static char *
virNetworkObjFormat(virNetworkObjPtr obj,
                    virNetworkXMLOptionPtr xmlopt,
                    unsigned int flags)
{
    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
    char *classIdStr = virBitmapFormat(obj->classIdMap);
    size_t i;

    if (!classIdStr)
        return NULL;

    virBufferAddLit(&buf, "<networkstatus>\n");
    virBufferAdjustIndent(&buf, 2);
    virBufferAsprintf(&buf, "<class_id bitmap='%s'/>\n", classIdStr);
    virBufferAsprintf(&buf, "<floor sum='%llu'/>\n", obj->floor_sum);
    VIR_FREE(classIdStr);

    for (i = 0; i < VIR_NETWORK_TAINT_LAST; i++) {
        if (obj->taint & (1 << i))
            virBufferAsprintf(&buf, "<taint flag='%s'/>\n",
                              virNetworkTaintTypeToString(i));
    }

    if (virNetworkDefFormatBuf(&buf, obj->def, xmlopt, flags) < 0)
        return NULL;

    virBufferAdjustIndent(&buf, -2);
    virBufferAddLit(&buf, "</networkstatus>");

    return virBufferContentAndReset(&buf);
}


int
virNetworkObjSaveStatus(const char *statusDir,
                        virNetworkObjPtr obj,
                        virNetworkXMLOptionPtr xmlopt)
{
    int ret = -1;
    int flags = 0;
    char *xml;

    if (!(xml = virNetworkObjFormat(obj, xmlopt, flags)))
        goto cleanup;

    if (virNetworkSaveXML(statusDir, obj->def, xml))
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(xml);
    return ret;
}


static virNetworkObjPtr
virNetworkLoadState(virNetworkObjListPtr nets,
                    const char *stateDir,
                    const char *name,
                    virNetworkXMLOptionPtr xmlopt)
{
    char *configFile = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr obj = NULL;
    xmlDocPtr xml = NULL;
    xmlNodePtr node = NULL, *nodes = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virBitmapPtr classIdMap = NULL;
    unsigned long long floor_sum_val = 0;
    unsigned int taint = 0;
    int n;
    size_t i;


    if ((configFile = virNetworkConfigFile(stateDir, name)) == NULL)
        goto error;

    if (!(xml = virXMLParseCtxt(configFile, NULL, _("(network status)"), &ctxt)))
        goto error;

    if (!(node = virXPathNode("//network", ctxt))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Could not find any 'network' element in status file"));
        goto error;
    }

    /* parse the definition first */
    ctxt->node = node;
    if (!(def = virNetworkDefParseXML(ctxt, xmlopt)))
        goto error;

    if (STRNEQ(name, def->name)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Network config filename '%s'"
                         " does not match network name '%s'"),
                       configFile, def->name);
        goto error;
    }

    /* now parse possible status data */
    node = xmlDocGetRootElement(xml);
    if (virXMLNodeNameEqual(node, "networkstatus")) {
        /* Newer network status file. Contains useful
         * info which are not to be found in bare config XML */
        char *classIdStr = NULL;
        char *floor_sum = NULL;

        ctxt->node = node;
        if ((classIdStr = virXPathString("string(./class_id[1]/@bitmap)",
                                         ctxt))) {
            if (!(classIdMap = virBitmapParseUnlimited(classIdStr))) {
                VIR_FREE(classIdStr);
                goto error;
            }
        }
        VIR_FREE(classIdStr);

        floor_sum = virXPathString("string(./floor[1]/@sum)", ctxt);
        if (floor_sum &&
            virStrToLong_ull(floor_sum, NULL, 10, &floor_sum_val) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Malformed 'floor_sum' attribute: %s"),
                           floor_sum);
            VIR_FREE(floor_sum);
            goto error;
        }
        VIR_FREE(floor_sum);

        if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0)
            goto error;

        for (i = 0; i < n; i++) {
            char *str = virXMLPropString(nodes[i], "flag");
            if (str) {
                int flag = virNetworkTaintTypeFromString(str);
                if (flag < 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("Unknown taint flag %s"), str);
                    VIR_FREE(str);
                    goto error;
                }
                VIR_FREE(str);
                /* Compute taint mask here. The network object does not
                 * exist yet, so we can't use virNetworkObjtTaint. */
                taint |= (1 << flag);
            }
        }
        VIR_FREE(nodes);
    }

    /* create the object */
    if (!(obj = virNetworkObjAssignDef(nets, def,
                                       VIR_NETWORK_OBJ_LIST_ADD_LIVE)))
        goto error;
    /* do not put any "goto error" below this comment */

    /* assign status data stored in the network object */
    if (classIdMap) {
        virBitmapFree(obj->classIdMap);
        obj->classIdMap = classIdMap;
    }

    if (floor_sum_val > 0)
        obj->floor_sum = floor_sum_val;

    obj->taint = taint;
    obj->active = true; /* network with a state file is by definition active */

 cleanup:
    VIR_FREE(configFile);
    xmlFreeDoc(xml);
    xmlXPathFreeContext(ctxt);
    return obj;

 error:
    VIR_FREE(nodes);
    virBitmapFree(classIdMap);
    virNetworkDefFree(def);
    goto cleanup;
}


static virNetworkObjPtr
virNetworkLoadConfig(virNetworkObjListPtr nets,
                     const char *configDir,
                     const char *autostartDir,
                     const char *name,
                     virNetworkXMLOptionPtr xmlopt)
{
    char *configFile = NULL, *autostartLink = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr obj;
    int autostart;

    if ((configFile = virNetworkConfigFile(configDir, name)) == NULL)
        goto error;
    if ((autostartLink = virNetworkConfigFile(autostartDir, name)) == NULL)
        goto error;

    if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
        goto error;

    if (!(def = virNetworkDefParseFile(configFile, xmlopt)))
        goto error;

    if (STRNEQ(name, def->name)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Network config filename '%s'"
                         " does not match network name '%s'"),
                       configFile, def->name);
        goto error;
    }

    switch ((virNetworkForwardType) def->forward.type) {
    case VIR_NETWORK_FORWARD_NONE:
    case VIR_NETWORK_FORWARD_NAT:
    case VIR_NETWORK_FORWARD_ROUTE:
    case VIR_NETWORK_FORWARD_OPEN:
        if (!def->mac_specified) {
            virNetworkSetBridgeMacAddr(def);
            virNetworkSaveConfig(configDir, def, xmlopt);
        }
        break;

    case VIR_NETWORK_FORWARD_BRIDGE:
    case VIR_NETWORK_FORWARD_PRIVATE:
    case VIR_NETWORK_FORWARD_VEPA:
    case VIR_NETWORK_FORWARD_PASSTHROUGH:
    case VIR_NETWORK_FORWARD_HOSTDEV:
        /* Throw away MAC address for other forward types,
         * which could have been generated by older libvirt RPMs */
        def->mac_specified = false;
        break;

    case VIR_NETWORK_FORWARD_LAST:
    default:
        virReportEnumRangeError(virNetworkForwardType, def->forward.type);
        goto error;
    }

    if (!(obj = virNetworkObjAssignDef(nets, def, 0)))<commit_msg>conf: Write network config to disk after generating UUID<commit_after>/*
 * virnetworkobj.c: handle network objects
 *                  (derived from network_conf.c)
 *
 * 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 <dirent.h>

#include "datatypes.h"
#include "virnetworkobj.h"

#include "viralloc.h"
#include "virerror.h"
#include "virfile.h"
#include "virhash.h"
#include "virlog.h"
#include "virstring.h"

#define VIR_FROM_THIS VIR_FROM_NETWORK

VIR_LOG_INIT("conf.virnetworkobj");

/* Currently, /sbin/tc implementation allows up to 16 bits for
 * minor class size. But the initial bitmap doesn't have to be
 * that big. */
#define INIT_CLASS_ID_BITMAP_SIZE (1<<4)

struct _virNetworkObj {
    virObjectLockable parent;

    pid_t dnsmasqPid;
    pid_t radvdPid;
    bool active;
    bool autostart;
    bool persistent;

    virNetworkDefPtr def; /* The current definition */
    virNetworkDefPtr newDef; /* New definition to activate at shutdown */

    virBitmapPtr classIdMap; /* bitmap of class IDs for QoS */
    unsigned long long floor_sum; /* sum of all 'floor'-s of attached NICs */

    unsigned int taint;

    /* Immutable pointer, self locking APIs */
    virMacMapPtr macmap;

    GHashTable *ports; /* uuid -> virNetworkPortDefPtr */
};

struct _virNetworkObjList {
    virObjectRWLockable parent;

    GHashTable *objs;
};

static virClassPtr virNetworkObjClass;
static virClassPtr virNetworkObjListClass;
static void virNetworkObjDispose(void *obj);
static void virNetworkObjListDispose(void *obj);

static int
virNetworkObjOnceInit(void)
{
    if (!VIR_CLASS_NEW(virNetworkObj, virClassForObjectLockable()))
        return -1;

    if (!VIR_CLASS_NEW(virNetworkObjList, virClassForObjectRWLockable()))
        return -1;

    return 0;
}


VIR_ONCE_GLOBAL_INIT(virNetworkObj);

static int
virNetworkObjLoadAllPorts(virNetworkObjPtr net,
                          const char *stateDir);


static void
virNetworkObjPortFree(void *val)
{
    virNetworkPortDefFree(val);
}

virNetworkObjPtr
virNetworkObjNew(void)
{
    virNetworkObjPtr obj;

    if (virNetworkObjInitialize() < 0)
        return NULL;

    if (!(obj = virObjectLockableNew(virNetworkObjClass)))
        return NULL;

    obj->classIdMap = virBitmapNew(INIT_CLASS_ID_BITMAP_SIZE);

    /* The first three class IDs are already taken */
    if (virBitmapSetBitExpand(obj->classIdMap, 0) < 0 ||
        virBitmapSetBitExpand(obj->classIdMap, 1) < 0 ||
        virBitmapSetBitExpand(obj->classIdMap, 2) < 0)
        goto error;

    if (!(obj->ports = virHashNew(virNetworkObjPortFree)))
        goto error;

    virObjectLock(obj);

    return obj;

 error:
    virObjectUnref(obj);
    return NULL;
}


void
virNetworkObjEndAPI(virNetworkObjPtr *obj)
{
    if (!*obj)
        return;

    virObjectUnlock(*obj);
    virObjectUnref(*obj);
    *obj = NULL;
}


virNetworkDefPtr
virNetworkObjGetDef(virNetworkObjPtr obj)
{
    return obj->def;
}


void
virNetworkObjSetDef(virNetworkObjPtr obj,
                    virNetworkDefPtr def)
{
    obj->def = def;
}


virNetworkDefPtr
virNetworkObjGetNewDef(virNetworkObjPtr obj)
{
    return obj->newDef;
}


bool
virNetworkObjIsActive(virNetworkObjPtr obj)
{
    return obj->active;
}


void
virNetworkObjSetActive(virNetworkObjPtr obj,
                       bool active)
{
    obj->active = active;
}


bool
virNetworkObjIsPersistent(virNetworkObjPtr obj)
{
    return obj->persistent;
}


bool
virNetworkObjIsAutostart(virNetworkObjPtr obj)
{
    return obj->autostart;
}


void
virNetworkObjSetAutostart(virNetworkObjPtr obj,
                          bool autostart)
{
    obj->autostart = autostart;
}


pid_t
virNetworkObjGetDnsmasqPid(virNetworkObjPtr obj)
{
    return obj->dnsmasqPid;
}


void
virNetworkObjSetDnsmasqPid(virNetworkObjPtr obj,
                           pid_t dnsmasqPid)
{
    obj->dnsmasqPid = dnsmasqPid;
}


pid_t
virNetworkObjGetRadvdPid(virNetworkObjPtr obj)
{
    return obj->radvdPid;
}


void
virNetworkObjSetRadvdPid(virNetworkObjPtr obj,
                         pid_t radvdPid)
{
    obj->radvdPid = radvdPid;
}


virBitmapPtr
virNetworkObjGetClassIdMap(virNetworkObjPtr obj)
{
    return obj->classIdMap;
}


virMacMapPtr
virNetworkObjGetMacMap(virNetworkObjPtr obj)
{
    return obj->macmap;
}


unsigned long long
virNetworkObjGetFloorSum(virNetworkObjPtr obj)
{
    return obj->floor_sum;
}


void
virNetworkObjSetFloorSum(virNetworkObjPtr obj,
                         unsigned long long floor_sum)
{
    obj->floor_sum = floor_sum;
}


void
virNetworkObjSetMacMap(virNetworkObjPtr obj,
                       virMacMapPtr macmap)
{
    obj->macmap = macmap;
}


void
virNetworkObjUnrefMacMap(virNetworkObjPtr obj)
{
    virObjectUnref(obj->macmap);
    obj->macmap = NULL;
}


int
virNetworkObjMacMgrAdd(virNetworkObjPtr obj,
                       const char *dnsmasqStateDir,
                       const char *domain,
                       const virMacAddr *mac)
{
    char macStr[VIR_MAC_STRING_BUFLEN];
    char *file = NULL;
    int ret = -1;

    if (!obj->macmap)
        return 0;

    virMacAddrFormat(mac, macStr);

    if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
        goto cleanup;

    if (virMacMapAdd(obj->macmap, domain, macStr) < 0)
        goto cleanup;

    if (virMacMapWriteFile(obj->macmap, file) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(file);
    return ret;
}


int
virNetworkObjMacMgrDel(virNetworkObjPtr obj,
                       const char *dnsmasqStateDir,
                       const char *domain,
                       const virMacAddr *mac)
{
    char macStr[VIR_MAC_STRING_BUFLEN];
    char *file = NULL;
    int ret = -1;

    if (!obj->macmap)
        return 0;

    virMacAddrFormat(mac, macStr);

    if (!(file = virMacMapFileName(dnsmasqStateDir, obj->def->bridge)))
        goto cleanup;

    if (virMacMapRemove(obj->macmap, domain, macStr) < 0)
        goto cleanup;

    if (virMacMapWriteFile(obj->macmap, file) < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(file);
    return ret;
}


virNetworkObjListPtr
virNetworkObjListNew(void)
{
    virNetworkObjListPtr nets;

    if (virNetworkObjInitialize() < 0)
        return NULL;

    if (!(nets = virObjectRWLockableNew(virNetworkObjListClass)))
        return NULL;

    if (!(nets->objs = virHashNew(virObjectFreeHashData))) {
        virObjectUnref(nets);
        return NULL;
    }

    return nets;
}


static virNetworkObjPtr
virNetworkObjFindByUUIDLocked(virNetworkObjListPtr nets,
                              const unsigned char *uuid)
{
    virNetworkObjPtr obj = NULL;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    virUUIDFormat(uuid, uuidstr);

    obj = virHashLookup(nets->objs, uuidstr);
    if (obj)
        virObjectRef(obj);
    return obj;
}


/**
 * virNetworkObjFindByUUID:
 * @nets: list of network objects
 * @uuid: network uuid to find
 *
 * This functions locks @nets and find network object which
 * corresponds to @uuid.
 *
 * Returns: locked and ref'd network object.
 */
virNetworkObjPtr
virNetworkObjFindByUUID(virNetworkObjListPtr nets,
                        const unsigned char *uuid)
{
    virNetworkObjPtr obj;

    virObjectRWLockRead(nets);
    obj = virNetworkObjFindByUUIDLocked(nets, uuid);
    virObjectRWUnlock(nets);
    if (obj)
        virObjectLock(obj);
    return obj;
}


static int
virNetworkObjSearchName(const void *payload,
                        const char *name G_GNUC_UNUSED,
                        const void *data)
{
    virNetworkObjPtr obj = (virNetworkObjPtr) payload;
    int want = 0;

    virObjectLock(obj);
    if (STREQ(obj->def->name, (const char *)data))
        want = 1;
    virObjectUnlock(obj);
    return want;
}


static virNetworkObjPtr
virNetworkObjFindByNameLocked(virNetworkObjListPtr nets,
                              const char *name)
{
    virNetworkObjPtr obj = NULL;

    obj = virHashSearch(nets->objs, virNetworkObjSearchName, name, NULL);
    if (obj)
        virObjectRef(obj);
    return obj;
}


/**
 * virNetworkObjFindByName:
 * @nets: list of network objects
 * @name: network name to find
 *
 * This functions locks @nets and find network object which
 * corresponds to @name.
 *
 * Returns: locked and ref'd network object.
 */
virNetworkObjPtr
virNetworkObjFindByName(virNetworkObjListPtr nets,
                        const char *name)
{
    virNetworkObjPtr obj;

    virObjectRWLockRead(nets);
    obj = virNetworkObjFindByNameLocked(nets, name);
    virObjectRWUnlock(nets);
    if (obj)
        virObjectLock(obj);
    return obj;
}


bool
virNetworkObjTaint(virNetworkObjPtr obj,
                   virNetworkTaintFlags taint)
{
    unsigned int flag = (1 << taint);

    if (obj->taint & flag)
        return false;

    obj->taint |= flag;
    return true;
}


static void
virNetworkObjDispose(void *opaque)
{
    virNetworkObjPtr obj = opaque;

    virHashFree(obj->ports);
    virNetworkDefFree(obj->def);
    virNetworkDefFree(obj->newDef);
    virBitmapFree(obj->classIdMap);
    virObjectUnref(obj->macmap);
}


static void
virNetworkObjListDispose(void *opaque)
{
    virNetworkObjListPtr nets = opaque;

    virHashFree(nets->objs);
}


/*
 * virNetworkObjUpdateAssignDef:
 * @network: the network object to update
 * @def: the new NetworkDef (will be consumed by this function)
 * @live: is this new def the "live" version, or the "persistent" version
 *
 * Replace the appropriate copy of the given network's def or newDef
 * with def. Use "live" and current state of the network to determine
 * which to replace and what to do with the old defs. When a non-live
 * def is set, indicate that the network is now persistent.
 *
 * NB: a persistent network can be made transient by calling with:
 * virNetworkObjAssignDef(network, NULL, false) (i.e. set the
 * persistent def to NULL)
 *
 */
void
virNetworkObjUpdateAssignDef(virNetworkObjPtr obj,
                             virNetworkDefPtr def,
                             bool live)
{
    if (live) {
        /* before setting new live def, save (into newDef) any
         * existing persistent (!live) def to be restored when the
         * network is destroyed, unless there is one already saved.
         */
        if (obj->persistent && !obj->newDef)
            obj->newDef = obj->def;
        else
            virNetworkDefFree(obj->def);
        obj->def = def;
    } else { /* !live */
        virNetworkDefFree(obj->newDef);
        if (virNetworkObjIsActive(obj)) {
            /* save new configuration to be restored on network
             * shutdown, leaving current live def alone
             */
            obj->newDef = def;
        } else { /* !live and !active */
            if (obj->def && !obj->persistent) {
                /* network isn't (yet) marked active or persistent,
                 * but already has a "live" def set. This means we are
                 * currently setting the persistent def as a part of
                 * the process of starting the network, so we need to
                 * preserve the "not yet live" def in network->def.
                 */
                obj->newDef = def;
            } else {
                /* either there is no live def set, or this network
                 * was already set as persistent, so the proper thing
                 * is to overwrite network->def.
                 */
                obj->newDef = NULL;
                virNetworkDefFree(obj->def);
                obj->def = def;
            }
        }
        obj->persistent = !!def;
    }
}


/*
 * If flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE then this will
 * refuse updating an existing def if the current def is live
 *
 * If flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE then the @def being
 * added is assumed to represent a live config, not a future
 * inactive config
 *
 * If flags is zero, network is considered as inactive and persistent.
 */
static virNetworkObjPtr
virNetworkObjAssignDefLocked(virNetworkObjListPtr nets,
                             virNetworkDefPtr def,
                             unsigned int flags)
{
    virNetworkObjPtr obj;
    virNetworkObjPtr ret = NULL;
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    /* See if a network with matching UUID already exists */
    if ((obj = virNetworkObjFindByUUIDLocked(nets, def->uuid))) {
        virObjectLock(obj);
        /* UUID matches, but if names don't match, refuse it */
        if (STRNEQ(obj->def->name, def->name)) {
            virUUIDFormat(obj->def->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("network '%s' is already defined with uuid %s"),
                           obj->def->name, uuidstr);
            goto cleanup;
        }

        if (flags & VIR_NETWORK_OBJ_LIST_ADD_CHECK_LIVE) {
            /* UUID & name match, but if network is already active, refuse it */
            if (virNetworkObjIsActive(obj)) {
                virReportError(VIR_ERR_OPERATION_INVALID,
                               _("network is already active as '%s'"),
                               obj->def->name);
                goto cleanup;
            }
        }

        virNetworkObjUpdateAssignDef(obj, def,
                                     !!(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE));
    } else {
        /* UUID does not match, but if a name matches, refuse it */
        if ((obj = virNetworkObjFindByNameLocked(nets, def->name))) {
            virObjectLock(obj);
            virUUIDFormat(obj->def->uuid, uuidstr);
            virReportError(VIR_ERR_OPERATION_FAILED,
                           _("network '%s' already exists with uuid %s"),
                           def->name, uuidstr);
            goto cleanup;
        }

        if (!(obj = virNetworkObjNew()))
              goto cleanup;

        virUUIDFormat(def->uuid, uuidstr);
        if (virHashAddEntry(nets->objs, uuidstr, obj) < 0)
            goto cleanup;
        virObjectRef(obj);

        obj->def = def;
        obj->persistent = !(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE);
    }

    ret = g_steal_pointer(&obj);

 cleanup:
    virNetworkObjEndAPI(&obj);
    return ret;
}


/*
 * virNetworkObjAssignDef:
 * @nets: list of all networks
 * @def: the new NetworkDef (will be consumed by this function iff successful)
 * @flags: bitwise-OR of VIR_NETWORK_OBJ_LIST_ADD_* flags
 *
 * Either replace the appropriate copy of the NetworkDef with name
 * matching def->name or, if not found, create a new NetworkObj with
 * def. For an existing network, use "live" and current state of the
 * network to determine which to replace.
 *
 * Look at virNetworkObjAssignDefLocked() for @flags description.
 *
 * Returns NULL on error, virNetworkObjPtr on success.
 */
virNetworkObjPtr
virNetworkObjAssignDef(virNetworkObjListPtr nets,
                       virNetworkDefPtr def,
                       unsigned int flags)
{
    virNetworkObjPtr obj;

    virObjectRWLockWrite(nets);
    obj = virNetworkObjAssignDefLocked(nets, def, flags);
    virObjectRWUnlock(nets);
    return obj;
}


/*
 * virNetworkObjSetDefTransient:
 * @network: network object pointer
 * @live: if true, run this operation even for an inactive network.
 *   this allows freely updated network->def with runtime defaults
 *   before starting the network, which will be discarded on network
 *   shutdown. Any cleanup paths need to be sure to handle newDef if
 *   the network is never started.
 *
 * Mark the active network config as transient. Ensures live-only update
 * operations do not persist past network destroy.
 *
 * Returns 0 on success, -1 on failure
 */
int
virNetworkObjSetDefTransient(virNetworkObjPtr obj,
                             bool live,
                             virNetworkXMLOptionPtr xmlopt)
{
    if (!virNetworkObjIsActive(obj) && !live)
        return 0;

    if (!obj->persistent || obj->newDef)
        return 0;

    obj->newDef = virNetworkDefCopy(obj->def,
                                    xmlopt,
                                    VIR_NETWORK_XML_INACTIVE);
    return obj->newDef ? 0 : -1;
}


/* virNetworkObjUnsetDefTransient:
 *
 * This *undoes* what virNetworkObjSetDefTransient did.
 */
void
virNetworkObjUnsetDefTransient(virNetworkObjPtr obj)
{
    if (obj->newDef) {
        virNetworkDefFree(obj->def);
        obj->def = obj->newDef;
        obj->newDef = NULL;
    }
}


/*
 * virNetworkObjGetPersistentDef:
 * @network: network object pointer
 *
 * Return the persistent network configuration. If network is transient,
 * return the running config.
 *
 * Returns NULL on error, virNetworkDefPtr on success.
 */
virNetworkDefPtr
virNetworkObjGetPersistentDef(virNetworkObjPtr obj)
{
    if (obj->newDef)
        return obj->newDef;
    else
        return obj->def;
}


/*
 * virNetworkObjReplacePersistentDef:
 * @network: network object pointer
 * @def: new virNetworkDef to replace current persistent config
 *
 * Replace the "persistent" network configuration with the given new
 * virNetworkDef. This pays attention to whether or not the network
 * is active.
 *
 * Returns -1 on error, 0 on success
 */
int
virNetworkObjReplacePersistentDef(virNetworkObjPtr obj,
                                  virNetworkDefPtr def)
{
    if (virNetworkObjIsActive(obj)) {
        virNetworkDefFree(obj->newDef);
        obj->newDef = def;
    } else {
        virNetworkDefFree(obj->def);
        obj->def = def;
    }
    return 0;
}


/*
 * virNetworkObjConfigChangeSetup:
 *
 * 1) checks whether network state is consistent with the requested
 *    type of modification.
 *
 * 3) make sure there are separate "def" and "newDef" copies of
 *    networkDef if appropriate.
 *
 * Returns 0 on success, -1 on error.
 */
static int
virNetworkObjConfigChangeSetup(virNetworkObjPtr obj,
                               virNetworkXMLOptionPtr xmlopt,
                               unsigned int flags)
{
    bool isActive;

    isActive = virNetworkObjIsActive(obj);

    if (!isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) {
        virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                       _("network is not running"));
        return -1;
    }

    if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) {
        if (!obj->persistent) {
            virReportError(VIR_ERR_OPERATION_INVALID, "%s",
                           _("cannot change persistent config of a "
                             "transient network"));
            return -1;
        }
        /* this should already have been done by the driver, but do it
         * anyway just in case.
         */
        if (isActive && (virNetworkObjSetDefTransient(obj, false, xmlopt) < 0))
            return -1;
    }

    return 0;
}


void
virNetworkObjRemoveInactive(virNetworkObjListPtr nets,
                            virNetworkObjPtr obj)
{
    char uuidstr[VIR_UUID_STRING_BUFLEN];

    virUUIDFormat(obj->def->uuid, uuidstr);
    virObjectRef(obj);
    virObjectUnlock(obj);
    virObjectRWLockWrite(nets);
    virObjectLock(obj);
    virHashRemoveEntry(nets->objs, uuidstr);
    virObjectRWUnlock(nets);
    virObjectUnref(obj);
}


static char *
virNetworkObjFormat(virNetworkObjPtr obj,
                    virNetworkXMLOptionPtr xmlopt,
                    unsigned int flags)
{
    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
    char *classIdStr = virBitmapFormat(obj->classIdMap);
    size_t i;

    if (!classIdStr)
        return NULL;

    virBufferAddLit(&buf, "<networkstatus>\n");
    virBufferAdjustIndent(&buf, 2);
    virBufferAsprintf(&buf, "<class_id bitmap='%s'/>\n", classIdStr);
    virBufferAsprintf(&buf, "<floor sum='%llu'/>\n", obj->floor_sum);
    VIR_FREE(classIdStr);

    for (i = 0; i < VIR_NETWORK_TAINT_LAST; i++) {
        if (obj->taint & (1 << i))
            virBufferAsprintf(&buf, "<taint flag='%s'/>\n",
                              virNetworkTaintTypeToString(i));
    }

    if (virNetworkDefFormatBuf(&buf, obj->def, xmlopt, flags) < 0)
        return NULL;

    virBufferAdjustIndent(&buf, -2);
    virBufferAddLit(&buf, "</networkstatus>");

    return virBufferContentAndReset(&buf);
}


int
virNetworkObjSaveStatus(const char *statusDir,
                        virNetworkObjPtr obj,
                        virNetworkXMLOptionPtr xmlopt)
{
    int ret = -1;
    int flags = 0;
    char *xml;

    if (!(xml = virNetworkObjFormat(obj, xmlopt, flags)))
        goto cleanup;

    if (virNetworkSaveXML(statusDir, obj->def, xml))
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_FREE(xml);
    return ret;
}


static virNetworkObjPtr
virNetworkLoadState(virNetworkObjListPtr nets,
                    const char *stateDir,
                    const char *name,
                    virNetworkXMLOptionPtr xmlopt)
{
    char *configFile = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr obj = NULL;
    xmlDocPtr xml = NULL;
    xmlNodePtr node = NULL, *nodes = NULL;
    xmlXPathContextPtr ctxt = NULL;
    virBitmapPtr classIdMap = NULL;
    unsigned long long floor_sum_val = 0;
    unsigned int taint = 0;
    int n;
    size_t i;


    if ((configFile = virNetworkConfigFile(stateDir, name)) == NULL)
        goto error;

    if (!(xml = virXMLParseCtxt(configFile, NULL, _("(network status)"), &ctxt)))
        goto error;

    if (!(node = virXPathNode("//network", ctxt))) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Could not find any 'network' element in status file"));
        goto error;
    }

    /* parse the definition first */
    ctxt->node = node;
    if (!(def = virNetworkDefParseXML(ctxt, xmlopt)))
        goto error;

    if (STRNEQ(name, def->name)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Network config filename '%s'"
                         " does not match network name '%s'"),
                       configFile, def->name);
        goto error;
    }

    /* now parse possible status data */
    node = xmlDocGetRootElement(xml);
    if (virXMLNodeNameEqual(node, "networkstatus")) {
        /* Newer network status file. Contains useful
         * info which are not to be found in bare config XML */
        char *classIdStr = NULL;
        char *floor_sum = NULL;

        ctxt->node = node;
        if ((classIdStr = virXPathString("string(./class_id[1]/@bitmap)",
                                         ctxt))) {
            if (!(classIdMap = virBitmapParseUnlimited(classIdStr))) {
                VIR_FREE(classIdStr);
                goto error;
            }
        }
        VIR_FREE(classIdStr);

        floor_sum = virXPathString("string(./floor[1]/@sum)", ctxt);
        if (floor_sum &&
            virStrToLong_ull(floor_sum, NULL, 10, &floor_sum_val) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Malformed 'floor_sum' attribute: %s"),
                           floor_sum);
            VIR_FREE(floor_sum);
            goto error;
        }
        VIR_FREE(floor_sum);

        if ((n = virXPathNodeSet("./taint", ctxt, &nodes)) < 0)
            goto error;

        for (i = 0; i < n; i++) {
            char *str = virXMLPropString(nodes[i], "flag");
            if (str) {
                int flag = virNetworkTaintTypeFromString(str);
                if (flag < 0) {
                    virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
                                   _("Unknown taint flag %s"), str);
                    VIR_FREE(str);
                    goto error;
                }
                VIR_FREE(str);
                /* Compute taint mask here. The network object does not
                 * exist yet, so we can't use virNetworkObjtTaint. */
                taint |= (1 << flag);
            }
        }
        VIR_FREE(nodes);
    }

    /* create the object */
    if (!(obj = virNetworkObjAssignDef(nets, def,
                                       VIR_NETWORK_OBJ_LIST_ADD_LIVE)))
        goto error;
    /* do not put any "goto error" below this comment */

    /* assign status data stored in the network object */
    if (classIdMap) {
        virBitmapFree(obj->classIdMap);
        obj->classIdMap = classIdMap;
    }

    if (floor_sum_val > 0)
        obj->floor_sum = floor_sum_val;

    obj->taint = taint;
    obj->active = true; /* network with a state file is by definition active */

 cleanup:
    VIR_FREE(configFile);
    xmlFreeDoc(xml);
    xmlXPathFreeContext(ctxt);
    return obj;

 error:
    VIR_FREE(nodes);
    virBitmapFree(classIdMap);
    virNetworkDefFree(def);
    goto cleanup;
}


static virNetworkObjPtr
virNetworkLoadConfig(virNetworkObjListPtr nets,
                     const char *configDir,
                     const char *autostartDir,
                     const char *name,
                     virNetworkXMLOptionPtr xmlopt)
{
    char *configFile = NULL, *autostartLink = NULL;
    virNetworkDefPtr def = NULL;
    virNetworkObjPtr obj;
    bool saveConfig = false;
    int autostart;

    if ((configFile = virNetworkConfigFile(configDir, name)) == NULL)
        goto error;
    if ((autostartLink = virNetworkConfigFile(autostartDir, name)) == NULL)
        goto error;

    if ((autostart = virFileLinkPointsTo(autostartLink, configFile)) < 0)
        goto error;

    if (!(def = virNetworkDefParseFile(configFile, xmlopt)))
        goto error;

    if (STRNEQ(name, def->name)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("Network config filename '%s'"
                         " does not match network name '%s'"),
                       configFile, def->name);
        goto error;
    }

    switch ((virNetworkForwardType) def->forward.type) {
    case VIR_NETWORK_FORWARD_NONE:
    case VIR_NETWORK_FORWARD_NAT:
    case VIR_NETWORK_FORWARD_ROUTE:
    case VIR_NETWORK_FORWARD_OPEN:
        if (!def->mac_specified) {
            virNetworkSetBridgeMacAddr(def);
            /* We just generated a new MAC address, and we need to persist
             * the configuration to disk to avoid the network getting a
             * different one the next time the daemon is started */
            saveConfig = true;
        }
        break;

    case VIR_NETWORK_FORWARD_BRIDGE:
    case VIR_NETWORK_FORWARD_PRIVATE:
    case VIR_NETWORK_FORWARD_VEPA:
    case VIR_NETWORK_FORWARD_PASSTHROUGH:
    case VIR_NETWORK_FORWARD_HOSTDEV:
        /* Throw away MAC address for other forward types,
         * which could have been generated by older libvirt RPMs */
        def->mac_specified = false;
        break;

    case VIR_NETWORK_FORWARD_LAST:
    default:
        virReportEnumRangeError(virNetworkForwardType, def->forward.type);
        goto error;
    }

    /* The network didn't have a UUID so we generated a new one, and
     * we need to persist the configuration to disk to avoid the network
     * getting a different one the next time the daemon is started */
    if (!def->uuid_specified)
        saveConfig = true;

    if (saveConfig &&
        virNetworkSaveConfig(configDir, def, xmlopt) < 0) {
        goto error;
    }

    if (!(obj = virNetworkObjAssignDef(nets, def, 0)))
{'fraction_non_alphanumeric': 0.09192845552172407, 'fraction_numerical': 0.0026072529035316427, 'mean_word_length': 2.3653208982697262, 'pattern_counts': {'":': 0, '<': 72, '<?xml version=': 0, '>': 274, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 54}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1326288', 'n_tokens_mistral': 16748, 'n_tokens_neox': 16118, 'n_words': 4390}