/*
* 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->classIdMap = virBitmapNew(CLASS_ID_BITMAP_SIZE)))
goto error;
/* The first three class IDs are already taken */
ignore_value(virBitmapSetBit(net->classIdMap, 0));
ignore_value(virBitmapSetBit(net->classIdMap, 1));
ignore_value(virBitmapSetBit(net->classIdMap, 2));
return net;
error:
virObjectUnref(net);
return NULL;
}
void
virNetworkObjEndAPI(virNetworkObjPtr *net)
{
if (!*net)
return;
virObjectUnlock(*net);
virObjectUnref(*net);
*net = NULL;
}
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;
}
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 = virObjectLockableNew(virNetworkObjListClass)))
return NULL;
if (!(nets->objs = virHashCreate(50, virObjectFreeHashData))) {
virObjectUnref(nets);
return NULL;
}
return nets;
}
static 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;
}
static virNetworkObjPtr
virNetworkObjFindByNameLocked(virNetworkObjListPtr nets,
const char *name)
{
virNetworkObjPtr ret = NULL;
ret = virHashSearch(nets->objs, virNetworkObjSearchName, name, NULL);
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->classIdMap);
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 *classIdStr = virBitmapFormat(net->classIdMap);
size_t i;
if (!classIdStr)
goto error;
virBufferAddLit(&buf, "\n");
virBufferAdjustIndent(&buf, 2);
virBufferAsprintf(&buf, "\n", classIdStr);
virBufferAsprintf(&buf, "\n", net->floor_sum);
VIR_FREE(classIdStr);
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 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)))
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 (virBitmapParse(classIdStr, &classIdMap,
CLASS_ID_BITMAP_SIZE) < 0) {
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 (!(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 (classIdMap) {
virBitmapFree(net->classIdMap);
net->classIdMap = classIdMap;
}
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(classIdMap);
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, NULL);
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);
}