mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-09 23:10:08 +00:00
690efb960f
Use 'g_clear_pointer(&ptr, g_hash_table_unref)' instead. In few instances it allows us to also remove explicit clearing of pointers. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
584 lines
13 KiB
C
584 lines
13 KiB
C
/*
|
|
* virinterfaceobj.c: interface object handling
|
|
* (derived from interface_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 "datatypes.h"
|
|
#include "interface_conf.h"
|
|
|
|
#include "viralloc.h"
|
|
#include "virerror.h"
|
|
#include "virinterfaceobj.h"
|
|
#include "virhash.h"
|
|
#include "virlog.h"
|
|
#include "virstring.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_INTERFACE
|
|
|
|
VIR_LOG_INIT("conf.virinterfaceobj");
|
|
|
|
struct _virInterfaceObj {
|
|
virObjectLockable parent;
|
|
|
|
bool active; /* true if interface is active (up) */
|
|
virInterfaceDef *def; /* The interface definition */
|
|
};
|
|
|
|
struct _virInterfaceObjList {
|
|
virObjectRWLockable parent;
|
|
|
|
/* name string -> virInterfaceObj mapping
|
|
* for O(1), lockless lookup-by-name */
|
|
GHashTable *objsName;
|
|
};
|
|
|
|
/* virInterfaceObj manipulation */
|
|
|
|
static virClass *virInterfaceObjClass;
|
|
static virClass *virInterfaceObjListClass;
|
|
static void virInterfaceObjDispose(void *obj);
|
|
static void virInterfaceObjListDispose(void *obj);
|
|
|
|
static int
|
|
virInterfaceObjOnceInit(void)
|
|
{
|
|
if (!VIR_CLASS_NEW(virInterfaceObj, virClassForObjectLockable()))
|
|
return -1;
|
|
|
|
if (!VIR_CLASS_NEW(virInterfaceObjList, virClassForObjectRWLockable()))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virInterfaceObj);
|
|
|
|
|
|
static void
|
|
virInterfaceObjDispose(void *opaque)
|
|
{
|
|
virInterfaceObj *obj = opaque;
|
|
|
|
virInterfaceDefFree(obj->def);
|
|
}
|
|
|
|
|
|
static virInterfaceObj *
|
|
virInterfaceObjNew(void)
|
|
{
|
|
virInterfaceObj *obj;
|
|
|
|
if (virInterfaceObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(obj = virObjectLockableNew(virInterfaceObjClass)))
|
|
return NULL;
|
|
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
void
|
|
virInterfaceObjEndAPI(virInterfaceObj **obj)
|
|
{
|
|
if (!*obj)
|
|
return;
|
|
|
|
virObjectUnlock(*obj);
|
|
virObjectUnref(*obj);
|
|
*obj = NULL;
|
|
}
|
|
|
|
|
|
virInterfaceDef *
|
|
virInterfaceObjGetDef(virInterfaceObj *obj)
|
|
{
|
|
return obj->def;
|
|
}
|
|
|
|
|
|
bool
|
|
virInterfaceObjIsActive(virInterfaceObj *obj)
|
|
{
|
|
return obj->active;
|
|
}
|
|
|
|
|
|
void
|
|
virInterfaceObjSetActive(virInterfaceObj *obj,
|
|
bool active)
|
|
{
|
|
obj->active = active;
|
|
}
|
|
|
|
|
|
/* virInterfaceObjList manipulation */
|
|
virInterfaceObjList *
|
|
virInterfaceObjListNew(void)
|
|
{
|
|
virInterfaceObjList *interfaces;
|
|
|
|
if (virInterfaceObjInitialize() < 0)
|
|
return NULL;
|
|
|
|
if (!(interfaces = virObjectRWLockableNew(virInterfaceObjListClass)))
|
|
return NULL;
|
|
|
|
interfaces->objsName = virHashNew(virObjectFreeHashData);
|
|
|
|
return interfaces;
|
|
}
|
|
|
|
|
|
struct _virInterfaceObjFindMACData {
|
|
const char *matchStr;
|
|
bool error;
|
|
int nnames;
|
|
int maxnames;
|
|
char **const names;
|
|
};
|
|
|
|
static int
|
|
virInterfaceObjListFindByMACStringCb(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virInterfaceObj *obj = payload;
|
|
struct _virInterfaceObjFindMACData *data = opaque;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
if (data->nnames == data->maxnames)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (STRCASEEQ(obj->def->mac, data->matchStr)) {
|
|
data->names[data->nnames] = g_strdup(obj->def->name);
|
|
data->nnames++;
|
|
}
|
|
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virInterfaceObjListFindByMACString(virInterfaceObjList *interfaces,
|
|
const char *mac,
|
|
char **const matches,
|
|
int maxmatches)
|
|
{
|
|
struct _virInterfaceObjFindMACData data = { .matchStr = mac,
|
|
.error = false,
|
|
.nnames = 0,
|
|
.maxnames = maxmatches,
|
|
.names = matches };
|
|
|
|
virObjectRWLockRead(interfaces);
|
|
virHashForEach(interfaces->objsName, virInterfaceObjListFindByMACStringCb,
|
|
&data);
|
|
virObjectRWUnlock(interfaces);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.nnames;
|
|
|
|
error:
|
|
while (--data.nnames >= 0)
|
|
VIR_FREE(data.names[data.nnames]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static virInterfaceObj *
|
|
virInterfaceObjListFindByNameLocked(virInterfaceObjList *interfaces,
|
|
const char *name)
|
|
{
|
|
return virObjectRef(virHashLookup(interfaces->objsName, name));
|
|
}
|
|
|
|
|
|
virInterfaceObj *
|
|
virInterfaceObjListFindByName(virInterfaceObjList *interfaces,
|
|
const char *name)
|
|
{
|
|
virInterfaceObj *obj;
|
|
virObjectRWLockRead(interfaces);
|
|
obj = virInterfaceObjListFindByNameLocked(interfaces, name);
|
|
virObjectRWUnlock(interfaces);
|
|
if (obj)
|
|
virObjectLock(obj);
|
|
|
|
return obj;
|
|
}
|
|
|
|
|
|
#define MATCH(FLAG) (flags & (FLAG))
|
|
static bool
|
|
virInterfaceObjMatch(virInterfaceObj *obj,
|
|
unsigned int flags)
|
|
{
|
|
/* filter by active state */
|
|
if (MATCH(VIR_CONNECT_LIST_INTERFACES_FILTERS_ACTIVE) &&
|
|
!((MATCH(VIR_CONNECT_LIST_INTERFACES_ACTIVE) &&
|
|
virInterfaceObjIsActive(obj)) ||
|
|
(MATCH(VIR_CONNECT_LIST_INTERFACES_INACTIVE) &&
|
|
!virInterfaceObjIsActive(obj))))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
#undef MATCH
|
|
|
|
|
|
typedef struct _virInterfaceObjListExportData virInterfaceObjListExportData;
|
|
struct _virInterfaceObjListExportData {
|
|
virConnectPtr conn;
|
|
virInterfacePtr *ifaces;
|
|
virInterfaceObjListFilter filter;
|
|
unsigned int flags;
|
|
int nifaces;
|
|
bool error;
|
|
};
|
|
|
|
static int
|
|
virInterfaceObjListExportCallback(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virInterfaceObjListExportData *data = opaque;
|
|
virInterfaceObj *obj = payload;
|
|
virInterfacePtr iface = NULL;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->filter &&
|
|
!data->filter(data->conn, obj->def))
|
|
goto cleanup;
|
|
|
|
if (!virInterfaceObjMatch(obj, data->flags))
|
|
goto cleanup;
|
|
|
|
if (!data->ifaces) {
|
|
data->nifaces++;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(iface = virGetInterface(data->conn, obj->def->name, obj->def->mac))) {
|
|
data->error = true;
|
|
goto cleanup;
|
|
}
|
|
|
|
data->ifaces[data->nifaces++] = iface;
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virInterfaceObjListExport(virConnectPtr conn,
|
|
virInterfaceObjList *ifaceobjs,
|
|
virInterfacePtr **ifaces,
|
|
virInterfaceObjListFilter filter,
|
|
unsigned int flags)
|
|
{
|
|
int ret = -1;
|
|
virInterfaceObjListExportData data = {
|
|
.conn = conn, .ifaces = NULL, .filter = filter, .flags = flags,
|
|
.nifaces = 0, .error = false };
|
|
|
|
virObjectRWLockRead(ifaceobjs);
|
|
if (ifaces)
|
|
data.ifaces = g_new0(virInterfacePtr, virHashSize(ifaceobjs->objsName) + 1);
|
|
|
|
virHashForEach(ifaceobjs->objsName, virInterfaceObjListExportCallback, &data);
|
|
|
|
if (data.error)
|
|
goto cleanup;
|
|
|
|
if (data.ifaces) {
|
|
/* trim the array to the final size */
|
|
VIR_REALLOC_N(data.ifaces, data.nifaces + 1);
|
|
*ifaces = g_steal_pointer(&data.ifaces);
|
|
}
|
|
|
|
ret = data.nifaces;
|
|
cleanup:
|
|
virObjectRWUnlock(ifaceobjs);
|
|
while (data.ifaces && data.nifaces)
|
|
virObjectUnref(data.ifaces[--data.nifaces]);
|
|
|
|
VIR_FREE(data.ifaces);
|
|
return ret;
|
|
}
|
|
|
|
|
|
void
|
|
virInterfaceObjListDispose(void *obj)
|
|
{
|
|
virInterfaceObjList *interfaces = obj;
|
|
|
|
g_clear_pointer(&interfaces->objsName, g_hash_table_unref);
|
|
}
|
|
|
|
|
|
struct _virInterfaceObjListCloneData {
|
|
bool error;
|
|
virInterfaceObjList *dest;
|
|
};
|
|
|
|
static int
|
|
virInterfaceObjListCloneCb(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virInterfaceObj *srcObj = payload;
|
|
struct _virInterfaceObjListCloneData *data = opaque;
|
|
char *xml = NULL;
|
|
g_autoptr(virInterfaceDef) backup = NULL;
|
|
virInterfaceObj *obj;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
virObjectLock(srcObj);
|
|
|
|
if (!(xml = virInterfaceDefFormat(srcObj->def)))
|
|
goto error;
|
|
|
|
if (!(backup = virInterfaceDefParseString(xml, 0)))
|
|
goto error;
|
|
VIR_FREE(xml);
|
|
|
|
if (!(obj = virInterfaceObjListAssignDef(data->dest, &backup)))
|
|
goto error;
|
|
virInterfaceObjEndAPI(&obj);
|
|
|
|
virObjectUnlock(srcObj);
|
|
return 0;
|
|
|
|
error:
|
|
data->error = true;
|
|
VIR_FREE(xml);
|
|
virObjectUnlock(srcObj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
virInterfaceObjList *
|
|
virInterfaceObjListClone(virInterfaceObjList *interfaces)
|
|
{
|
|
struct _virInterfaceObjListCloneData data = { .error = false,
|
|
.dest = NULL };
|
|
|
|
if (!interfaces)
|
|
return NULL;
|
|
|
|
if (!(data.dest = virInterfaceObjListNew()))
|
|
return NULL;
|
|
|
|
virObjectRWLockRead(interfaces);
|
|
virHashForEach(interfaces->objsName, virInterfaceObjListCloneCb, &data);
|
|
virObjectRWUnlock(interfaces);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.dest;
|
|
|
|
error:
|
|
virObjectUnref(data.dest);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* virInterfaceObjListAssignDef:
|
|
* @interfaces: virInterface object list
|
|
* @def: new definition
|
|
*
|
|
* Assigns new definition to either an existing or newly created
|
|
* virInterface object. Upon successful return the virInterface
|
|
* object is the owner of @def and callers should use
|
|
* virInterfaceObjGetDef() if they need to access the definition
|
|
* as @def is set to NULL.
|
|
*
|
|
* Returns: a virInterface object instance on success, or
|
|
* NULL on error.
|
|
*/
|
|
virInterfaceObj *
|
|
virInterfaceObjListAssignDef(virInterfaceObjList *interfaces,
|
|
virInterfaceDef **def)
|
|
{
|
|
virInterfaceObj *obj;
|
|
|
|
virObjectRWLockWrite(interfaces);
|
|
if ((obj = virInterfaceObjListFindByNameLocked(interfaces, (*def)->name))) {
|
|
virInterfaceDefFree(obj->def);
|
|
} else {
|
|
if (!(obj = virInterfaceObjNew()))
|
|
goto error;
|
|
|
|
if (virHashAddEntry(interfaces->objsName, (*def)->name, obj) < 0)
|
|
goto error;
|
|
virObjectRef(obj);
|
|
}
|
|
|
|
obj->def = g_steal_pointer(def);
|
|
virObjectRWUnlock(interfaces);
|
|
|
|
return obj;
|
|
|
|
error:
|
|
virInterfaceObjEndAPI(&obj);
|
|
virObjectRWUnlock(interfaces);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
virInterfaceObjListRemove(virInterfaceObjList *interfaces,
|
|
virInterfaceObj *obj)
|
|
{
|
|
if (!obj)
|
|
return;
|
|
|
|
virObjectRef(obj);
|
|
virObjectUnlock(obj);
|
|
virObjectRWLockWrite(interfaces);
|
|
virObjectLock(obj);
|
|
virHashRemoveEntry(interfaces->objsName, obj->def->name);
|
|
virInterfaceObjEndAPI(&obj);
|
|
virObjectRWUnlock(interfaces);
|
|
}
|
|
|
|
|
|
struct _virInterfaceObjNumOfInterfacesData {
|
|
bool wantActive;
|
|
int count;
|
|
};
|
|
|
|
static int
|
|
virInterfaceObjListNumOfInterfacesCb(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virInterfaceObj *obj = payload;
|
|
struct _virInterfaceObjNumOfInterfacesData *data = opaque;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->wantActive == virInterfaceObjIsActive(obj))
|
|
data->count++;
|
|
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virInterfaceObjListNumOfInterfaces(virInterfaceObjList *interfaces,
|
|
bool wantActive)
|
|
{
|
|
struct _virInterfaceObjNumOfInterfacesData data = {
|
|
.wantActive = wantActive, .count = 0 };
|
|
|
|
virObjectRWLockRead(interfaces);
|
|
virHashForEach(interfaces->objsName, virInterfaceObjListNumOfInterfacesCb,
|
|
&data);
|
|
virObjectRWUnlock(interfaces);
|
|
|
|
return data.count;
|
|
}
|
|
|
|
|
|
struct _virInterfaceObjGetNamesData {
|
|
bool wantActive;
|
|
bool error;
|
|
int nnames;
|
|
int maxnames;
|
|
char **const names;
|
|
};
|
|
|
|
static int
|
|
virInterfaceObjListGetNamesCb(void *payload,
|
|
const char *name G_GNUC_UNUSED,
|
|
void *opaque)
|
|
{
|
|
virInterfaceObj *obj = payload;
|
|
struct _virInterfaceObjGetNamesData *data = opaque;
|
|
|
|
if (data->error)
|
|
return 0;
|
|
|
|
if (data->maxnames >= 0 && data->nnames == data->maxnames)
|
|
return 0;
|
|
|
|
virObjectLock(obj);
|
|
|
|
if (data->wantActive != virInterfaceObjIsActive(obj))
|
|
goto cleanup;
|
|
|
|
data->names[data->nnames] = g_strdup(obj->def->name);
|
|
|
|
data->nnames++;
|
|
|
|
cleanup:
|
|
virObjectUnlock(obj);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
virInterfaceObjListGetNames(virInterfaceObjList *interfaces,
|
|
bool wantActive,
|
|
char **const names,
|
|
int maxnames)
|
|
{
|
|
struct _virInterfaceObjGetNamesData data = {
|
|
.wantActive = wantActive, .error = false, .nnames = 0,
|
|
.maxnames = maxnames, .names = names };
|
|
|
|
virObjectRWLockRead(interfaces);
|
|
virHashForEach(interfaces->objsName, virInterfaceObjListGetNamesCb, &data);
|
|
virObjectRWUnlock(interfaces);
|
|
|
|
if (data.error)
|
|
goto error;
|
|
|
|
return data.nnames;
|
|
|
|
error:
|
|
while (--data.nnames >= 0)
|
|
VIR_FREE(data.names[data.nnames]);
|
|
|
|
return -1;
|
|
}
|