2013-07-15 14:53:13 +00:00
|
|
|
/*
|
|
|
|
* virclosecallbacks.c: Connection close callbacks routines
|
|
|
|
*
|
2014-04-04 23:36:25 +00:00
|
|
|
* Copyright (C) 2013-2014 Red Hat, Inc.
|
2013-07-15 14:53:13 +00:00
|
|
|
*
|
|
|
|
* 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 "viralloc.h"
|
|
|
|
#include "virclosecallbacks.h"
|
|
|
|
#include "virlog.h"
|
|
|
|
#include "virobject.h"
|
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
|
|
|
|
2014-02-28 12:16:17 +00:00
|
|
|
VIR_LOG_INIT("util.closecallbacks");
|
|
|
|
|
2022-10-06 13:20:13 +00:00
|
|
|
|
|
|
|
struct _virCloseCallbacksDomainData {
|
|
|
|
virConnectPtr conn;
|
|
|
|
virCloseCallback cb;
|
|
|
|
};
|
|
|
|
typedef struct _virCloseCallbacksDomainData virCloseCallbacksDomainData;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
virCloseCallbacksDomainDataFree(virCloseCallbacksDomainData* data)
|
|
|
|
{
|
|
|
|
g_free(data);
|
|
|
|
}
|
|
|
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC(virCloseCallbacksDomainData, virCloseCallbacksDomainDataFree);
|
|
|
|
|
|
|
|
|
|
|
|
virClass *virCloseCallbacksDomainListClass;
|
|
|
|
|
|
|
|
struct _virCloseCallbacksDomainList {
|
|
|
|
virObjectLockable parent;
|
|
|
|
|
|
|
|
GList *callbacks;
|
|
|
|
};
|
|
|
|
typedef struct _virCloseCallbacksDomainList virCloseCallbacksDomainList;
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
virCloseCallbacksDomainListDispose(void *obj G_GNUC_UNUSED)
|
|
|
|
{
|
|
|
|
virCloseCallbacksDomainList *cc = obj;
|
|
|
|
|
|
|
|
g_list_free_full(cc->callbacks, (GDestroyNotify) virCloseCallbacksDomainDataFree);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
virCloseCallbacksDomainListOnceInit(void)
|
|
|
|
{
|
|
|
|
if (!(VIR_CLASS_NEW(virCloseCallbacksDomainList, virClassForObjectLockable())))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_ONCE_GLOBAL_INIT(virCloseCallbacksDomainList);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainAlloc:
|
|
|
|
*
|
|
|
|
* Allocates and returns a data structure for holding close callback data in
|
|
|
|
* a virDomainObj.
|
|
|
|
*/
|
|
|
|
virObject *
|
|
|
|
virCloseCallbacksDomainAlloc(void)
|
|
|
|
{
|
|
|
|
if (virCloseCallbacksDomainListInitialize() < 0)
|
|
|
|
abort();
|
|
|
|
|
|
|
|
return virObjectNew(virCloseCallbacksDomainListClass);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainAdd:
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: pointer to the connection which should trigger the close callback
|
|
|
|
* @cb: pointer to the callback function
|
|
|
|
*
|
|
|
|
* Registers @cb as a connection close callback for the @conn connection with
|
|
|
|
* the @vm domain. Duplicate registrations are ignored.
|
|
|
|
*
|
|
|
|
* Caller must hold lock on @vm.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virCloseCallbacksDomainAdd(virDomainObj *vm,
|
|
|
|
virConnectPtr conn,
|
|
|
|
virCloseCallback cb)
|
|
|
|
{
|
|
|
|
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
|
|
|
|
|
|
|
if (!conn || !cb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
|
|
|
virCloseCallbacksDomainData *data;
|
|
|
|
GList *n;
|
|
|
|
|
|
|
|
for (n = cc->callbacks; n; n = n->next) {
|
|
|
|
data = n->data;
|
|
|
|
|
|
|
|
if (data->cb == cb && data->conn == conn)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = g_new0(virCloseCallbacksDomainData, 1);
|
|
|
|
data->conn = conn;
|
|
|
|
data->cb = cb;
|
|
|
|
|
|
|
|
cc->callbacks = g_list_prepend(cc->callbacks, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainMatch:
|
|
|
|
* @data: pointer to a close callback data structure
|
|
|
|
* @conn: connection pointer matched against @data
|
|
|
|
* @cb: callback pointer matched against @data
|
|
|
|
*
|
|
|
|
* Returns true if the @data callback structure matches the requested @conn
|
|
|
|
* and/or @cb parameters. If either of @conn/@cb is NULL it is interpreted as
|
|
|
|
* a wildcard.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
virCloseCallbacksDomainMatch(virCloseCallbacksDomainData *data,
|
|
|
|
virConnectPtr conn,
|
|
|
|
virCloseCallback cb)
|
|
|
|
{
|
|
|
|
if (conn && cb)
|
|
|
|
return data->conn == conn && data->cb == cb;
|
|
|
|
|
|
|
|
if (conn)
|
|
|
|
return data->conn == conn;
|
|
|
|
|
|
|
|
if (cb)
|
|
|
|
return data->cb == cb;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainIsRegistered:
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: connection pointer
|
|
|
|
* @cb: callback pointer
|
|
|
|
*
|
|
|
|
* Returns true if @vm has one or more matching (see virCloseCallbacksDomainMatch)
|
|
|
|
* callback(s) registered. Caller must hold lock on @vm.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
virCloseCallbacksDomainIsRegistered(virDomainObj *vm,
|
|
|
|
virConnectPtr conn,
|
|
|
|
virCloseCallback cb)
|
|
|
|
{
|
|
|
|
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
|
|
|
GList *n;
|
|
|
|
|
|
|
|
for (n = cc->callbacks; n; n = n->next) {
|
|
|
|
virCloseCallbacksDomainData *data = n->data;
|
|
|
|
|
|
|
|
if (virCloseCallbacksDomainMatch(data, conn, cb))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainRemove:
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: connection pointer
|
|
|
|
* @cb: callback pointer
|
|
|
|
*
|
|
|
|
* Removes all the registered matching (see virCloseCallbacksDomainMatch)
|
|
|
|
* callbacks for @vm. Caller must hold lock on @vm.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virCloseCallbacksDomainRemove(virDomainObj *vm,
|
|
|
|
virConnectPtr conn,
|
|
|
|
virCloseCallback cb)
|
|
|
|
{
|
|
|
|
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
|
|
|
GList *n = cc->callbacks;
|
|
|
|
|
|
|
|
while (n) {
|
|
|
|
GList *cur = n;
|
|
|
|
|
|
|
|
n = n->next;
|
|
|
|
|
|
|
|
if (virCloseCallbacksDomainMatch(cur->data, conn, cb)) {
|
|
|
|
cc->callbacks = g_list_remove_link(cc->callbacks, cur);
|
|
|
|
g_list_free_full(cur, (GDestroyNotify) virCloseCallbacksDomainDataFree);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainFetchForConn:
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: pointer to connection being closed
|
|
|
|
*
|
|
|
|
* Fetches connection close callbacks for @conn from @vm. The fetched close
|
|
|
|
* callbacks are removed from the list of callbacks of @vm. This function
|
|
|
|
* must be called with lock on @vm held. Caller is responsible for freeing the
|
|
|
|
* returned list.
|
|
|
|
*/
|
|
|
|
static GList *
|
|
|
|
virCloseCallbacksDomainFetchForConn(virDomainObj *vm,
|
|
|
|
virConnectPtr conn)
|
|
|
|
{
|
|
|
|
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
|
|
|
GList *conncallbacks = NULL;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
|
|
|
GList *n;
|
|
|
|
|
|
|
|
for (n = cc->callbacks; n;) {
|
|
|
|
virCloseCallbacksDomainData *data = n->data;
|
|
|
|
GList *cur = n;
|
|
|
|
|
|
|
|
n = n->next;
|
|
|
|
|
|
|
|
if (data->conn == conn) {
|
|
|
|
cc->callbacks = g_list_remove_link(cc->callbacks, cur);
|
|
|
|
conncallbacks = g_list_concat(cur, conncallbacks);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return conncallbacks;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainRun
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: pointer to connection being closed
|
|
|
|
*
|
|
|
|
* Fetches and sequentially calls all connection close callbacks for @conn from
|
|
|
|
* @vm. This function must be called with lock on @vm held.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virCloseCallbacksDomainRun(virDomainObj *vm,
|
|
|
|
virConnectPtr conn)
|
|
|
|
{
|
|
|
|
g_autolist(virCloseCallbacksDomainData) callbacks = NULL;
|
|
|
|
GList *n;
|
|
|
|
|
|
|
|
callbacks = virCloseCallbacksDomainFetchForConn(vm, conn);
|
|
|
|
|
|
|
|
for (n = callbacks; n; n = n->next) {
|
|
|
|
virCloseCallbacksDomainData *data = n->data;
|
|
|
|
|
|
|
|
VIR_DEBUG("vm='%s' cb='%p'", vm->def->name, data->cb);
|
|
|
|
|
|
|
|
(data->cb)(vm, conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainHasCallbackForConn:
|
|
|
|
* @vm: domain object
|
|
|
|
* @conn: connection being closed
|
|
|
|
*
|
|
|
|
* Returns true if @vm has a callback registered for the @conn connection. This
|
|
|
|
* function doesn't require a lock being held on @vm.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
virCloseCallbacksDomainHasCallbackForConn(virDomainObj *vm,
|
|
|
|
virConnectPtr conn)
|
|
|
|
{
|
|
|
|
/* we can access vm->closecallbacks as it's a immutable pointer */
|
|
|
|
virCloseCallbacksDomainList *cc = (virCloseCallbacksDomainList *) vm->closecallbacks;
|
|
|
|
|
|
|
|
if (!cc)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(cc) {
|
|
|
|
GList *n;
|
|
|
|
|
|
|
|
for (n = cc->callbacks; n; n = n->next) {
|
|
|
|
virCloseCallbacksDomainData *data = n->data;
|
|
|
|
|
|
|
|
if (data->conn == conn)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virCloseCallbacksDomainRunForConn:
|
|
|
|
* @domains: domain list object
|
|
|
|
* @conn: connection being closed
|
|
|
|
*
|
|
|
|
* Finds all domains in @domains which registered one or more connection close
|
|
|
|
* callbacks for @conn and calls the callbacks. This function is designed to
|
|
|
|
* be called from virDrvConnectClose function of individual drivers.
|
|
|
|
*
|
|
|
|
* To minimize lock contention the function first fetches a list of all domain
|
|
|
|
* objects, then checks whether a connect close callback is actually registered
|
|
|
|
* for the domain object and just then acquires the lock on the VM object.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virCloseCallbacksDomainRunForConn(virDomainObjList *domains,
|
|
|
|
virConnectPtr conn)
|
|
|
|
{
|
|
|
|
virDomainObj **vms = NULL;
|
|
|
|
size_t nvms;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
VIR_DEBUG("conn=%p", conn);
|
|
|
|
|
|
|
|
virDomainObjListCollectAll(domains, &vms, &nvms);
|
|
|
|
|
|
|
|
for (i = 0; i < nvms; i++) {
|
|
|
|
virDomainObj *vm = vms[i];
|
|
|
|
|
|
|
|
if (!virCloseCallbacksDomainHasCallbackForConn(vm, conn))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VIR_WITH_OBJECT_LOCK_GUARD(vm) {
|
|
|
|
/* VIR_WITH_OBJECT_LOCK_GUARD is a for loop, so this break applies to that */
|
|
|
|
if (vm->removing)
|
|
|
|
break;
|
|
|
|
|
|
|
|
virCloseCallbacksDomainRun(vm, conn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virObjectListFreeCount(vms, nvms);
|
|
|
|
}
|