/* * 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 * . */ #include #include #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 */ #define CLASS_ID_BITMAP_SIZE (1<<16) struct _virNetworkObjList { virObjectLockable parent; virHashTablePtr objs; }; static virClassPtr virNetworkObjClass; static virClassPtr virNetworkObjListClass; static void virNetworkObjDispose(void *obj); static void virNetworkObjListDispose(void *obj); static int virNetworkObjOnceInit(void) { if (!(virNetworkObjClass = virClassNew(virClassForObjectLockable(), "virNetworkObj", sizeof(virNetworkObj), virNetworkObjDispose))) return -1; if (!(virNetworkObjListClass = virClassNew(virClassForObjectLockable(), "virNetworkObjList", sizeof(virNetworkObjList), virNetworkObjListDispose))) return -1; return 0; } VIR_ONCE_GLOBAL_INIT(virNetworkObj) virNetworkObjPtr virNetworkObjNew(void) { virNetworkObjPtr net; if (virNetworkObjInitialize() < 0) return NULL; if (!(net = virObjectLockableNew(virNetworkObjClass))) return NULL; if (!(net->class_id = virBitmapNew(CLASS_ID_BITMAP_SIZE))) goto error; /* The first three class IDs are already taken */ ignore_value(virBitmapSetBit(net->class_id, 0)); ignore_value(virBitmapSetBit(net->class_id, 1)); ignore_value(virBitmapSetBit(net->class_id, 2)); return net; error: virObjectUnref(net); return NULL; } void virNetworkObjEndAPI(virNetworkObjPtr *net) { if (!*net) return; virObjectUnlock(*net); virObjectUnref(*net); *net = NULL; } virNetworkObjListPtr virNetworkObjListNew(void) { virNetworkObjListPtr nets; if (virNetworkObjInitialize() < 0) return NULL; if (!(nets = virObjectLockableNew(virNetworkObjListClass))) return NULL; if (!(nets->objs = virHashCreate(50, virObjectFreeHashData))) { virObjectUnref(nets); return NULL; } return nets; } /** * virNetworkObjFindByUUIDLocked: * @nets: list of network objects * @uuid: network uuid to find * * This functions requires @nets to be locked already! * * Returns: not locked, but ref'd network object. */ virNetworkObjPtr virNetworkObjFindByUUIDLocked(virNetworkObjListPtr nets, const unsigned char *uuid) { virNetworkObjPtr ret = NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(uuid, uuidstr); ret = virHashLookup(nets->objs, uuidstr); if (ret) virObjectRef(ret); return ret; } /** * 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 ret; virObjectLock(nets); ret = virNetworkObjFindByUUIDLocked(nets, uuid); virObjectUnlock(nets); if (ret) virObjectLock(ret); return ret; } static int virNetworkObjSearchName(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *data) { virNetworkObjPtr net = (virNetworkObjPtr) payload; int want = 0; virObjectLock(net); if (STREQ(net->def->name, (const char *)data)) want = 1; virObjectUnlock(net); return want; } /* * virNetworkObjFindByNameLocked: * @nets: list of network objects * @name: network name to find * * This functions requires @nets to be locked already! * * Returns: not locked, but ref'd network object. */ virNetworkObjPtr virNetworkObjFindByNameLocked(virNetworkObjListPtr nets, const char *name) { virNetworkObjPtr ret = NULL; ret = virHashSearch(nets->objs, virNetworkObjSearchName, name); if (ret) virObjectRef(ret); return ret; } /** * 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 ret; virObjectLock(nets); ret = virNetworkObjFindByNameLocked(nets, name); virObjectUnlock(nets); if (ret) virObjectLock(ret); return ret; } 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 *obj) { virNetworkObjPtr net = obj; virNetworkDefFree(net->def); virNetworkDefFree(net->newDef); virBitmapFree(net->class_id); virObjectUnref(net->macmap); } static void virNetworkObjListDispose(void *obj) { virNetworkObjListPtr nets = obj; 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 network, 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 (network->persistent && !network->newDef) network->newDef = network->def; else virNetworkDefFree(network->def); network->def = def; } else { /* !live */ virNetworkDefFree(network->newDef); if (virNetworkObjIsActive(network)) { /* save new configuration to be restored on network * shutdown, leaving current live def alone */ network->newDef = def; } else { /* !live and !active */ if (network->def && !network->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. */ network->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. */ network->newDef = NULL; virNetworkDefFree(network->def); network->def = def; } } network->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 network; virNetworkObjPtr ret = NULL; char uuidstr[VIR_UUID_STRING_BUFLEN]; /* See if a network with matching UUID already exists */ if ((network = virNetworkObjFindByUUIDLocked(nets, def->uuid))) { virObjectLock(network); /* UUID matches, but if names don't match, refuse it */ if (STRNEQ(network->def->name, def->name)) { virUUIDFormat(network->def->uuid, uuidstr); virReportError(VIR_ERR_OPERATION_FAILED, _("network '%s' is already defined with uuid %s"), network->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(network)) { virReportError(VIR_ERR_OPERATION_INVALID, _("network is already active as '%s'"), network->def->name); goto cleanup; } } virNetworkObjUpdateAssignDef(network, def, !!(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE)); } else { /* UUID does not match, but if a name matches, refuse it */ if ((network = virNetworkObjFindByNameLocked(nets, def->name))) { virObjectLock(network); virUUIDFormat(network->def->uuid, uuidstr); virReportError(VIR_ERR_OPERATION_FAILED, _("network '%s' already exists with uuid %s"), def->name, uuidstr); goto cleanup; } if (!(network = virNetworkObjNew())) goto cleanup; virObjectLock(network); virUUIDFormat(def->uuid, uuidstr); if (virHashAddEntry(nets->objs, uuidstr, network) < 0) goto cleanup; network->def = def; network->persistent = !(flags & VIR_NETWORK_OBJ_LIST_ADD_LIVE); virObjectRef(network); } ret = network; network = NULL; cleanup: virNetworkObjEndAPI(&network); 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 network; virObjectLock(nets); network = virNetworkObjAssignDefLocked(nets, def, flags); virObjectUnlock(nets); return network; } /* * 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 network, bool live) { if (!virNetworkObjIsActive(network) && !live) return 0; if (!network->persistent || network->newDef) return 0; network->newDef = virNetworkDefCopy(network->def, VIR_NETWORK_XML_INACTIVE); return network->newDef ? 0 : -1; } /* virNetworkObjUnsetDefTransient: * * This *undoes* what virNetworkObjSetDefTransient did. */ void virNetworkObjUnsetDefTransient(virNetworkObjPtr network) { if (network->newDef) { virNetworkDefFree(network->def); network->def = network->newDef; network->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 network) { if (network->newDef) return network->newDef; else return network->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 network, virNetworkDefPtr def) { if (virNetworkObjIsActive(network)) { virNetworkDefFree(network->newDef); network->newDef = def; } else { virNetworkDefFree(network->def); network->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 network, unsigned int flags) { bool isActive; int ret = -1; isActive = virNetworkObjIsActive(network); if (!isActive && (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("network is not running")); goto cleanup; } if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { if (!network->persistent) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("cannot change persistent config of a " "transient network")); goto cleanup; } /* this should already have been done by the driver, but do it * anyway just in case. */ if (isActive && (virNetworkObjSetDefTransient(network, false) < 0)) goto cleanup; } ret = 0; cleanup: return ret; } void virNetworkObjRemoveInactive(virNetworkObjListPtr nets, virNetworkObjPtr net) { char uuidstr[VIR_UUID_STRING_BUFLEN]; virUUIDFormat(net->def->uuid, uuidstr); virObjectRef(net); virObjectUnlock(net); virObjectLock(nets); virObjectLock(net); virHashRemoveEntry(nets->objs, uuidstr); virObjectUnlock(nets); virObjectUnref(net); } static char * virNetworkObjFormat(virNetworkObjPtr net, unsigned int flags) { virBuffer buf = VIR_BUFFER_INITIALIZER; char *class_id = virBitmapFormat(net->class_id); size_t i; if (!class_id) goto error; virBufferAddLit(&buf, "\n"); virBufferAdjustIndent(&buf, 2); virBufferAsprintf(&buf, "\n", class_id); virBufferAsprintf(&buf, "\n", net->floor_sum); VIR_FREE(class_id); for (i = 0; i < VIR_NETWORK_TAINT_LAST; i++) { if (net->taint & (1 << i)) virBufferAsprintf(&buf, "\n", virNetworkTaintTypeToString(i)); } if (virNetworkDefFormatBuf(&buf, net->def, flags) < 0) goto error; virBufferAdjustIndent(&buf, -2); virBufferAddLit(&buf, ""); if (virBufferCheckError(&buf) < 0) goto error; return virBufferContentAndReset(&buf); error: virBufferFreeAndReset(&buf); return NULL; } int virNetworkObjSaveStatus(const char *statusDir, virNetworkObjPtr network) { int ret = -1; int flags = 0; char *xml; if (!(xml = virNetworkObjFormat(network, flags))) goto cleanup; if (virNetworkSaveXML(statusDir, network->def, xml)) goto cleanup; ret = 0; cleanup: VIR_FREE(xml); return ret; } static virNetworkObjPtr virNetworkLoadState(virNetworkObjListPtr nets, const char *stateDir, const char *name) { char *configFile = NULL; virNetworkDefPtr def = NULL; virNetworkObjPtr net = NULL; xmlDocPtr xml = NULL; xmlNodePtr node = NULL, *nodes = NULL; xmlXPathContextPtr ctxt = NULL; virBitmapPtr class_id_map = 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))) 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 (xmlStrEqual(node->name, BAD_CAST "networkstatus")) { /* Newer network status file. Contains useful * info which are not to be found in bare config XML */ char *class_id = NULL; char *floor_sum = NULL; ctxt->node = node; if ((class_id = virXPathString("string(./class_id[1]/@bitmap)", ctxt))) { if (virBitmapParse(class_id, &class_id_map, CLASS_ID_BITMAP_SIZE) < 0) { VIR_FREE(class_id); goto error; } } VIR_FREE(class_id); 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 (!(net = 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 (class_id_map) { virBitmapFree(net->class_id); net->class_id = class_id_map; } if (floor_sum_val > 0) net->floor_sum = floor_sum_val; net->taint = taint; net->active = 1; /* any network with a state file is by definition active */ cleanup: VIR_FREE(configFile); xmlFreeDoc(xml); xmlXPathFreeContext(ctxt); return net; error: VIR_FREE(nodes); virBitmapFree(class_id_map); virNetworkDefFree(def); goto cleanup; } static virNetworkObjPtr virNetworkLoadConfig(virNetworkObjListPtr nets, const char *configDir, const char *autostartDir, const char *name) { char *configFile = NULL, *autostartLink = NULL; virNetworkDefPtr def = NULL; virNetworkObjPtr net; 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))) 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; } if (def->forward.type == VIR_NETWORK_FORWARD_NONE || def->forward.type == VIR_NETWORK_FORWARD_NAT || def->forward.type == VIR_NETWORK_FORWARD_ROUTE || def->forward.type == VIR_NETWORK_FORWARD_OPEN) { if (!def->mac_specified) { virNetworkSetBridgeMacAddr(def); virNetworkSaveConfig(configDir, def); } } else { /* Throw away MAC address for other forward types, * which could have been generated by older libvirt RPMs */ def->mac_specified = false; } if (!(net = virNetworkObjAssignDef(nets, def, 0))) goto error; net->autostart = autostart; VIR_FREE(configFile); VIR_FREE(autostartLink); return net; error: VIR_FREE(configFile); VIR_FREE(autostartLink); virNetworkDefFree(def); return NULL; } int virNetworkObjLoadAllState(virNetworkObjListPtr nets, const char *stateDir) { DIR *dir; struct dirent *entry; int ret = -1; int rc; if ((rc = virDirOpenIfExists(&dir, stateDir)) <= 0) return rc; while ((ret = virDirRead(dir, &entry, stateDir)) > 0) { virNetworkObjPtr net; if (!virFileStripSuffix(entry->d_name, ".xml")) continue; net = virNetworkLoadState(nets, stateDir, entry->d_name); virNetworkObjEndAPI(&net); } VIR_DIR_CLOSE(dir); return ret; } int virNetworkObjLoadAllConfigs(virNetworkObjListPtr nets, const char *configDir, const char *autostartDir) { DIR *dir; struct dirent *entry; int ret = -1; int rc; if ((rc = virDirOpenIfExists(&dir, configDir)) <= 0) return rc; while ((ret = virDirRead(dir, &entry, configDir)) > 0) { virNetworkObjPtr net; if (!virFileStripSuffix(entry->d_name, ".xml")) continue; /* NB: ignoring errors, so one malformed config doesn't kill the whole process */ net = virNetworkLoadConfig(nets, configDir, autostartDir, entry->d_name); virNetworkObjEndAPI(&net); } VIR_DIR_CLOSE(dir); return ret; } int virNetworkObjDeleteConfig(const char *configDir, const char *autostartDir, virNetworkObjPtr net) { char *configFile = NULL; char *autostartLink = NULL; int ret = -1; if ((configFile = virNetworkConfigFile(configDir, net->def->name)) == NULL) goto error; if ((autostartLink = virNetworkConfigFile(autostartDir, net->def->name)) == NULL) goto error; /* Not fatal if this doesn't work */ unlink(autostartLink); net->autostart = 0; if (unlink(configFile) < 0) { virReportSystemError(errno, _("cannot remove config file '%s'"), configFile); goto error; } ret = 0; error: VIR_FREE(configFile); VIR_FREE(autostartLink); return ret; } struct virNetworkObjBridgeInUseHelperData { const char *bridge; const char *skipname; }; static int virNetworkObjBridgeInUseHelper(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *opaque) { int ret; virNetworkObjPtr net = (virNetworkObjPtr) payload; const struct virNetworkObjBridgeInUseHelperData *data = opaque; virObjectLock(net); if (data->skipname && ((net->def && STREQ(net->def->name, data->skipname)) || (net->newDef && STREQ(net->newDef->name, data->skipname)))) ret = 0; else if ((net->def && net->def->bridge && STREQ(net->def->bridge, data->bridge)) || (net->newDef && net->newDef->bridge && STREQ(net->newDef->bridge, data->bridge))) ret = 1; else ret = 0; virObjectUnlock(net); return ret; } int virNetworkObjBridgeInUse(virNetworkObjListPtr nets, const char *bridge, const char *skipname) { virNetworkObjPtr obj; struct virNetworkObjBridgeInUseHelperData data = {bridge, skipname}; virObjectLock(nets); obj = virHashSearch(nets->objs, virNetworkObjBridgeInUseHelper, &data); virObjectUnlock(nets); return obj != NULL; } /* * virNetworkObjUpdate: * * Apply the supplied update to the given virNetworkObj. Except for * @network pointing to an actual network object rather than the * opaque virNetworkPtr, parameters are identical to the public API * virNetworkUpdate. * * The original virNetworkDefs are copied, and all modifications made * to these copies. The originals are replaced with the copies only * after success has been guaranteed. * * Returns: -1 on error, 0 on success. */ int virNetworkObjUpdate(virNetworkObjPtr network, unsigned int command, /* virNetworkUpdateCommand */ unsigned int section, /* virNetworkUpdateSection */ int parentIndex, const char *xml, unsigned int flags) /* virNetworkUpdateFlags */ { int ret = -1; virNetworkDefPtr livedef = NULL, configdef = NULL; /* normalize config data, and check for common invalid requests. */ if (virNetworkObjConfigChangeSetup(network, flags) < 0) goto cleanup; if (flags & VIR_NETWORK_UPDATE_AFFECT_LIVE) { virNetworkDefPtr checkdef; /* work on a copy of the def */ if (!(livedef = virNetworkDefCopy(network->def, 0))) goto cleanup; if (virNetworkDefUpdateSection(livedef, command, section, parentIndex, xml, flags) < 0) { goto cleanup; } /* run a final format/parse cycle to make sure we didn't * add anything illegal to the def */ if (!(checkdef = virNetworkDefCopy(livedef, 0))) goto cleanup; virNetworkDefFree(checkdef); } if (flags & VIR_NETWORK_UPDATE_AFFECT_CONFIG) { virNetworkDefPtr checkdef; /* work on a copy of the def */ if (!(configdef = virNetworkDefCopy(virNetworkObjGetPersistentDef(network), VIR_NETWORK_XML_INACTIVE))) { goto cleanup; } if (virNetworkDefUpdateSection(configdef, command, section, parentIndex, xml, flags) < 0) { goto cleanup; } if (!(checkdef = virNetworkDefCopy(configdef, VIR_NETWORK_XML_INACTIVE))) { goto cleanup; } virNetworkDefFree(checkdef); } if (configdef) { /* successfully modified copy, now replace original */ if (virNetworkObjReplacePersistentDef(network, configdef) < 0) goto cleanup; configdef = NULL; } if (livedef) { /* successfully modified copy, now replace original */ virNetworkDefFree(network->def); network->def = livedef; livedef = NULL; } ret = 0; cleanup: virNetworkDefFree(livedef); virNetworkDefFree(configdef); return ret; } #define MATCH(FLAG) (flags & (FLAG)) static bool virNetworkMatch(virNetworkObjPtr netobj, unsigned int flags) { /* filter by active state */ if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_ACTIVE) && !((MATCH(VIR_CONNECT_LIST_NETWORKS_ACTIVE) && virNetworkObjIsActive(netobj)) || (MATCH(VIR_CONNECT_LIST_NETWORKS_INACTIVE) && !virNetworkObjIsActive(netobj)))) return false; /* filter by persistence */ if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_PERSISTENT) && !((MATCH(VIR_CONNECT_LIST_NETWORKS_PERSISTENT) && netobj->persistent) || (MATCH(VIR_CONNECT_LIST_NETWORKS_TRANSIENT) && !netobj->persistent))) return false; /* filter by autostart option */ if (MATCH(VIR_CONNECT_LIST_NETWORKS_FILTERS_AUTOSTART) && !((MATCH(VIR_CONNECT_LIST_NETWORKS_AUTOSTART) && netobj->autostart) || (MATCH(VIR_CONNECT_LIST_NETWORKS_NO_AUTOSTART) && !netobj->autostart))) return false; return true; } #undef MATCH struct virNetworkObjListData { virConnectPtr conn; virNetworkPtr *nets; virNetworkObjListFilter filter; unsigned int flags; int nnets; bool error; }; static int virNetworkObjListPopulate(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) { struct virNetworkObjListData *data = opaque; virNetworkObjPtr obj = payload; virNetworkPtr net = NULL; if (data->error) return 0; virObjectLock(obj); if (data->filter && !data->filter(data->conn, obj->def)) goto cleanup; if (!virNetworkMatch(obj, data->flags)) goto cleanup; if (!data->nets) { data->nnets++; goto cleanup; } if (!(net = virGetNetwork(data->conn, obj->def->name, obj->def->uuid))) { data->error = true; goto cleanup; } data->nets[data->nnets++] = net; cleanup: virObjectUnlock(obj); return 0; } int virNetworkObjListExport(virConnectPtr conn, virNetworkObjListPtr netobjs, virNetworkPtr **nets, virNetworkObjListFilter filter, unsigned int flags) { int ret = -1; struct virNetworkObjListData data = { conn, NULL, filter, flags, 0, false}; virObjectLock(netobjs); if (nets && VIR_ALLOC_N(data.nets, virHashSize(netobjs->objs) + 1) < 0) goto cleanup; virHashForEach(netobjs->objs, virNetworkObjListPopulate, &data); if (data.error) goto cleanup; if (data.nets) { /* trim the array to the final size */ ignore_value(VIR_REALLOC_N(data.nets, data.nnets + 1)); *nets = data.nets; data.nets = NULL; } ret = data.nnets; cleanup: virObjectUnlock(netobjs); while (data.nets && data.nnets) virObjectUnref(data.nets[--data.nnets]); VIR_FREE(data.nets); return ret; } struct virNetworkObjListForEachHelperData { virNetworkObjListIterator callback; void *opaque; int ret; }; static int virNetworkObjListForEachHelper(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) { struct virNetworkObjListForEachHelperData *data = opaque; if (data->callback(payload, data->opaque) < 0) data->ret = -1; return 0; } /** * virNetworkObjListForEach: * @nets: a list of network objects * @callback: function to call over each of object in the list * @opaque: pointer to pass to the @callback * * Function iterates over the list of network objects and calls * passed callback over each one of them. You should avoid * calling those virNetworkObjList APIs, which lock the list * again in favor of their virNetworkObj*Locked variants. * * Returns: 0 on success, -1 otherwise. */ int virNetworkObjListForEach(virNetworkObjListPtr nets, virNetworkObjListIterator callback, void *opaque) { struct virNetworkObjListForEachHelperData data = {callback, opaque, 0}; virObjectLock(nets); virHashForEach(nets->objs, virNetworkObjListForEachHelper, &data); virObjectUnlock(nets); return data.ret; } struct virNetworkObjListGetHelperData { virConnectPtr conn; virNetworkObjListFilter filter; char **names; int nnames; bool active; int got; bool error; }; static int virNetworkObjListGetHelper(void *payload, const void *name ATTRIBUTE_UNUSED, void *opaque) { struct virNetworkObjListGetHelperData *data = opaque; virNetworkObjPtr obj = payload; if (data->error) return 0; if (data->nnames >= 0 && data->got == data->nnames) return 0; virObjectLock(obj); if (data->filter && !data->filter(data->conn, obj->def)) goto cleanup; if ((data->active && virNetworkObjIsActive(obj)) || (!data->active && !virNetworkObjIsActive(obj))) { if (data->names && VIR_STRDUP(data->names[data->got], obj->def->name) < 0) { data->error = true; goto cleanup; } data->got++; } cleanup: virObjectUnlock(obj); return 0; } int virNetworkObjListGetNames(virNetworkObjListPtr nets, bool active, char **names, int nnames, virNetworkObjListFilter filter, virConnectPtr conn) { int ret = -1; struct virNetworkObjListGetHelperData data = { conn, filter, names, nnames, active, 0, false}; virObjectLock(nets); virHashForEach(nets->objs, virNetworkObjListGetHelper, &data); virObjectUnlock(nets); if (data.error) goto cleanup; ret = data.got; cleanup: if (ret < 0) { while (data.got) VIR_FREE(data.names[--data.got]); } return ret; } int virNetworkObjListNumOfNetworks(virNetworkObjListPtr nets, bool active, virNetworkObjListFilter filter, virConnectPtr conn) { struct virNetworkObjListGetHelperData data = { conn, filter, NULL, -1, active, 0, false}; virObjectLock(nets); virHashForEach(nets->objs, virNetworkObjListGetHelper, &data); virObjectUnlock(nets); return data.got; } struct virNetworkObjListPruneHelperData { unsigned int flags; }; static int virNetworkObjListPruneHelper(const void *payload, const void *name ATTRIBUTE_UNUSED, const void *opaque) { const struct virNetworkObjListPruneHelperData *data = opaque; virNetworkObjPtr obj = (virNetworkObjPtr) payload; int want = 0; virObjectLock(obj); want = virNetworkMatch(obj, data->flags); virObjectUnlock(obj); return want; } /** * virNetworkObjListPrune: * @nets: a list of network objects * @flags: bitwise-OR of virConnectListAllNetworksFlags * * Iterate over list of network objects and remove the desired * ones from it. */ void virNetworkObjListPrune(virNetworkObjListPtr nets, unsigned int flags) { struct virNetworkObjListPruneHelperData data = {flags}; virObjectLock(nets); virHashRemoveSet(nets->objs, virNetworkObjListPruneHelper, &data); virObjectUnlock(nets); }