--- po/POTFILES.in | 1 + src/Makefile.am | 4 +- src/libvirt_private.syms | 7 + src/qemu/qemu_conf.c | 295 +------------------------------------- src/qemu/qemu_conf.h | 25 +--- src/qemu/qemu_driver.c | 4 +- src/qemu/qemu_migration.c | 18 +-- src/qemu/qemu_migration.h | 2 +- src/qemu/qemu_process.c | 14 +- src/util/virclosecallbacks.c | 332 +++++++++++++++++++++++++++++++++++++++++++ src/util/virclosecallbacks.h | 53 +++++++ 11 files changed, 418 insertions(+), 337 deletions(-) create mode 100644 src/util/virclosecallbacks.c create mode 100644 src/util/virclosecallbacks.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 0b65765..2e4ebc8 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -146,6 +146,7 @@ src/util/viraudit.c src/util/virauth.c src/util/virauthconfig.c src/util/vircgroup.c +src/util/virclosecallbacks.c src/util/vircommand.c src/util/virconf.c src/util/virdbus.c diff --git a/src/Makefile.am b/src/Makefile.am index d9e703f..8fa8680 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -83,6 +83,7 @@ UTIL_SOURCES = \ util/virbitmap.c util/virbitmap.h \ util/virbuffer.c util/virbuffer.h \ util/vircgroup.c util/vircgroup.h util/vircgrouppriv.h \ + util/virclosecallbacks.c util/virclosecallbacks.h \ util/vircommand.c util/vircommand.h \ util/virconf.c util/virconf.h \ util/virdbus.c util/virdbus.h \ @@ -882,7 +883,8 @@ libvirt_util_la_SOURCES = \ $(UTIL_SOURCES) libvirt_util_la_CFLAGS = $(CAPNG_CFLAGS) $(YAJL_CFLAGS) $(LIBNL_CFLAGS) \ $(AM_CFLAGS) $(AUDIT_CFLAGS) $(DEVMAPPER_CFLAGS) \ - $(DBUS_CFLAGS) $(LDEXP_LIBM) $(NUMACTL_CFLAGS) + $(DBUS_CFLAGS) $(LDEXP_LIBM) $(NUMACTL_CFLAGS) \ + -I$(top_srcdir)/src/conf libvirt_util_la_LIBADD = $(CAPNG_LIBS) $(YAJL_LIBS) $(LIBNL_LIBS) \ $(THREAD_LIBS) $(AUDIT_LIBS) $(DEVMAPPER_LIBS) \ $(LIB_CLOCK_GETTIME) $(DBUS_LIBS) $(MSCOM_LIBS) $(LIBXML_LIBS) \ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index fc4e750..53b1153 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1204,6 +1204,13 @@ virCgroupSetMemorySoftLimit; virCgroupSetMemSwapHardLimit; +# util/virclosecallbacks.h +virCloseCallbacksGet; +virCloseCallbacksNew; +virCloseCallbacksRun; +virCloseCallbacksSet; +virCloseCallbacksUnset; + # util/vircommand.h virCommandAbort; virCommandAddArg; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index c91551f..64214b9 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -56,25 +56,8 @@ #define VIR_FROM_THIS VIR_FROM_QEMU -typedef struct _qemuDriverCloseDef qemuDriverCloseDef; -typedef qemuDriverCloseDef *qemuDriverCloseDefPtr; -struct _qemuDriverCloseDef { - virConnectPtr conn; - virQEMUCloseCallback cb; -}; - -struct _virQEMUCloseCallbacks { - virObjectLockable parent; - - /* UUID string to qemuDriverCloseDef mapping */ - virHashTablePtr list; -}; - - static virClassPtr virQEMUDriverConfigClass; -static virClassPtr virQEMUCloseCallbacksClass; static void virQEMUDriverConfigDispose(void *obj); -static void virQEMUCloseCallbacksDispose(void *obj); static int virQEMUConfigOnceInit(void) { @@ -83,12 +66,7 @@ static int virQEMUConfigOnceInit(void) sizeof(virQEMUDriverConfig), virQEMUDriverConfigDispose); - virQEMUCloseCallbacksClass = virClassNew(virClassForObjectLockable(), - "virQEMUCloseCallbacks", - sizeof(virQEMUCloseCallbacks), - virQEMUCloseCallbacksDispose); - - if (!virQEMUDriverConfigClass || !virQEMUCloseCallbacksClass) + if (!virQEMUDriverConfigClass) return -1; else return 0; @@ -662,277 +640,6 @@ virCapsPtr virQEMUDriverGetCapabilities(virQEMUDriverPtr driver, return ret; } - -static void -virQEMUCloseCallbacksFreeData(void *payload, - const void *name ATTRIBUTE_UNUSED) -{ - VIR_FREE(payload); -} - -virQEMUCloseCallbacksPtr -virQEMUCloseCallbacksNew(void) -{ - virQEMUCloseCallbacksPtr closeCallbacks; - - if (virQEMUConfigInitialize() < 0) - return NULL; - - if (!(closeCallbacks = virObjectLockableNew(virQEMUCloseCallbacksClass))) - return NULL; - - closeCallbacks->list = virHashCreate(5, virQEMUCloseCallbacksFreeData); - if (!closeCallbacks->list) { - virObjectUnref(closeCallbacks); - return NULL; - } - - return closeCallbacks; -} - -static void -virQEMUCloseCallbacksDispose(void *obj) -{ - virQEMUCloseCallbacksPtr closeCallbacks = obj; - - virHashFree(closeCallbacks->list); -} - -int -virQEMUCloseCallbacksSet(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virConnectPtr conn, - virQEMUCloseCallback cb) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - qemuDriverCloseDefPtr closeDef; - int ret = -1; - - virUUIDFormat(vm->def->uuid, uuidstr); - VIR_DEBUG("vm=%s, uuid=%s, conn=%p, cb=%p", - vm->def->name, uuidstr, conn, cb); - - virObjectLock(closeCallbacks); - - closeDef = virHashLookup(closeCallbacks->list, uuidstr); - if (closeDef) { - if (closeDef->conn != conn) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Close callback for domain %s already registered" - " with another connection %p"), - vm->def->name, closeDef->conn); - goto cleanup; - } - if (closeDef->cb && closeDef->cb != cb) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Another close callback is already defined for" - " domain %s"), vm->def->name); - goto cleanup; - } - - closeDef->cb = cb; - } else { - if (VIR_ALLOC(closeDef) < 0) - goto cleanup; - - closeDef->conn = conn; - closeDef->cb = cb; - if (virHashAddEntry(closeCallbacks->list, uuidstr, closeDef) < 0) { - VIR_FREE(closeDef); - goto cleanup; - } - } - - ret = 0; -cleanup: - virObjectUnlock(closeCallbacks); - return ret; -} - -int -virQEMUCloseCallbacksUnset(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virQEMUCloseCallback cb) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - qemuDriverCloseDefPtr closeDef; - int ret = -1; - - virUUIDFormat(vm->def->uuid, uuidstr); - VIR_DEBUG("vm=%s, uuid=%s, cb=%p", - vm->def->name, uuidstr, cb); - - virObjectLock(closeCallbacks); - - closeDef = virHashLookup(closeCallbacks->list, uuidstr); - if (!closeDef) - goto cleanup; - - if (closeDef->cb && closeDef->cb != cb) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Trying to remove mismatching close callback for" - " domain %s"), vm->def->name); - goto cleanup; - } - - ret = virHashRemoveEntry(closeCallbacks->list, uuidstr); -cleanup: - virObjectUnlock(closeCallbacks); - return ret; -} - -virQEMUCloseCallback -virQEMUCloseCallbacksGet(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virConnectPtr conn) -{ - char uuidstr[VIR_UUID_STRING_BUFLEN]; - qemuDriverCloseDefPtr closeDef; - virQEMUCloseCallback cb = NULL; - - virUUIDFormat(vm->def->uuid, uuidstr); - VIR_DEBUG("vm=%s, uuid=%s, conn=%p", - vm->def->name, uuidstr, conn); - - virObjectLock(closeCallbacks); - - closeDef = virHashLookup(closeCallbacks->list, uuidstr); - if (closeDef && (!conn || closeDef->conn == conn)) - cb = closeDef->cb; - - virObjectUnlock(closeCallbacks); - - VIR_DEBUG("cb=%p", cb); - return cb; -} - - -typedef struct _virQEMUCloseCallbacksListEntry virQEMUCloseCallbacksListEntry; -typedef virQEMUCloseCallbacksListEntry *virQEMUCloseCallbacksListEntryPtr; -struct _virQEMUCloseCallbacksListEntry { - unsigned char uuid[VIR_UUID_BUFLEN]; - virQEMUCloseCallback callback; -}; - - -typedef struct _virQEMUCloseCallbacksList virQEMUCloseCallbacksList; -typedef virQEMUCloseCallbacksList *virQEMUCloseCallbacksListPtr; -struct _virQEMUCloseCallbacksList { - size_t nentries; - virQEMUCloseCallbacksListEntryPtr entries; -}; - - -struct virQEMUCloseCallbacksData { - virConnectPtr conn; - virQEMUCloseCallbacksListPtr list; - bool oom; -}; - - -static void -virQEMUCloseCallbacksGetOne(void *payload, - const void *key, - void *opaque) -{ - struct virQEMUCloseCallbacksData *data = opaque; - qemuDriverCloseDefPtr closeDef = payload; - const char *uuidstr = key; - unsigned char uuid[VIR_UUID_BUFLEN]; - - if (virUUIDParse(uuidstr, uuid) < 0) - return; - - VIR_DEBUG("conn=%p, thisconn=%p, uuid=%s, cb=%p", - closeDef->conn, data->conn, uuidstr, closeDef->cb); - - if (data->conn != closeDef->conn || !closeDef->cb) - return; - - if (VIR_EXPAND_N(data->list->entries, - data->list->nentries, 1) < 0) { - data->oom = true; - return; - } - - memcpy(data->list->entries[data->list->nentries - 1].uuid, - uuid, VIR_UUID_BUFLEN); - data->list->entries[data->list->nentries - 1].callback = closeDef->cb; -} - - -static virQEMUCloseCallbacksListPtr -virQEMUCloseCallbacksGetForConn(virQEMUCloseCallbacksPtr closeCallbacks, - virConnectPtr conn) -{ - virQEMUCloseCallbacksListPtr list = NULL; - struct virQEMUCloseCallbacksData data; - - if (VIR_ALLOC(list) < 0) - return NULL; - - data.conn = conn; - data.list = list; - data.oom = false; - - virHashForEach(closeCallbacks->list, virQEMUCloseCallbacksGetOne, &data); - - if (data.oom) { - VIR_FREE(list->entries); - VIR_FREE(list); - virReportOOMError(); - return NULL; - } - - return list; -} - - -void -virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks, - virConnectPtr conn, - virQEMUDriverPtr driver) -{ - virQEMUCloseCallbacksListPtr list; - size_t i; - - VIR_DEBUG("conn=%p", conn); - - /* We must not hold the lock while running the callbacks, - * so first we obtain the list of callbacks, then remove - * them all from the hash. At that point we can release - * the lock and run the callbacks safely. */ - - virObjectLock(closeCallbacks); - list = virQEMUCloseCallbacksGetForConn(closeCallbacks, conn); - if (!list) - return; - - for (i = 0; i < list->nentries; i++) { - virHashRemoveEntry(closeCallbacks->list, - list->entries[i].uuid); - } - virObjectUnlock(closeCallbacks); - - for (i = 0; i < list->nentries; i++) { - virDomainObjPtr vm; - - if (!(vm = virDomainObjListFindByUUID(driver->domains, - list->entries[i].uuid))) { - char uuidstr[VIR_UUID_STRING_BUFLEN]; - virUUIDFormat(list->entries[i].uuid, uuidstr); - VIR_DEBUG("No domain object with UUID %s", uuidstr); - continue; - } - - vm = list->entries[i].callback(driver, vm, conn); - if (vm) - virObjectUnlock(vm); - } - VIR_FREE(list->entries); - VIR_FREE(list); -} - struct _qemuSharedDeviceEntry { size_t ref; char **domains; /* array of domain names */ diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 19893c8..8229cfc 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -42,6 +42,7 @@ # include "virthreadpool.h" # include "locking/lock_manager.h" # include "qemu_capabilities.h" +# include "virclosecallbacks.h" # ifdef CPU_SETSIZE /* Linux */ # define QEMUD_CPUMASK_LEN CPU_SETSIZE @@ -51,9 +52,6 @@ # error "Port me" # endif -typedef struct _virQEMUCloseCallbacks virQEMUCloseCallbacks; -typedef virQEMUCloseCallbacks *virQEMUCloseCallbacksPtr; - typedef struct _virQEMUDriver virQEMUDriver; typedef virQEMUDriver *virQEMUDriverPtr; @@ -229,7 +227,7 @@ struct _virQEMUDriver { virLockManagerPluginPtr lockManager; /* Immutable pointer, self-clocking APIs */ - virQEMUCloseCallbacksPtr closeCallbacks; + virCloseCallbacksPtr closeCallbacks; }; typedef struct _qemuDomainCmdlineDef qemuDomainCmdlineDef; @@ -266,25 +264,6 @@ struct qemuDomainDiskInfo { int io_status; }; -typedef virDomainObjPtr (*virQEMUCloseCallback)(virQEMUDriverPtr driver, - virDomainObjPtr vm, - virConnectPtr conn); -virQEMUCloseCallbacksPtr virQEMUCloseCallbacksNew(void); -int virQEMUCloseCallbacksSet(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virConnectPtr conn, - virQEMUCloseCallback cb); -int virQEMUCloseCallbacksUnset(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virQEMUCloseCallback cb); -virQEMUCloseCallback -virQEMUCloseCallbacksGet(virQEMUCloseCallbacksPtr closeCallbacks, - virDomainObjPtr vm, - virConnectPtr conn); -void virQEMUCloseCallbacksRun(virQEMUCloseCallbacksPtr closeCallbacks, - virConnectPtr conn, - virQEMUDriverPtr driver); - typedef struct _qemuSharedDeviceEntry qemuSharedDeviceEntry; typedef qemuSharedDeviceEntry *qemuSharedDeviceEntryPtr; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b7b066d..dab0513 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -776,7 +776,7 @@ qemuStateInitialize(bool privileged, cfg->hugepagePath = mempath; } - if (!(qemu_driver->closeCallbacks = virQEMUCloseCallbacksNew())) + if (!(qemu_driver->closeCallbacks = virCloseCallbacksNew())) goto error; /* Get all the running persistent or transient configs first */ @@ -1076,7 +1076,7 @@ static int qemuConnectClose(virConnectPtr conn) virQEMUDriverPtr driver = conn->privateData; /* Get rid of callbacks registered for this conn */ - virQEMUCloseCallbacksRun(driver->closeCallbacks, conn, driver); + virCloseCallbacksRun(driver->closeCallbacks, conn, driver->domains, driver); conn->privateData = NULL; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 62e0cbc..19343a6 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1897,7 +1897,7 @@ cleanup: * qemuDomainMigratePerform3 and qemuDomainMigrateConfirm3. */ virDomainObjPtr -qemuMigrationCleanup(virQEMUDriverPtr driver, +qemuMigrationCleanup(void *driver, virDomainObjPtr vm, virConnectPtr conn) { @@ -2100,8 +2100,8 @@ qemuMigrationBegin(virConnectPtr conn, * This prevents any other APIs being invoked while migration is taking * place. */ - if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn, - qemuMigrationCleanup) < 0) + if (virCloseCallbacksSet(driver->closeCallbacks, vm, conn, + qemuMigrationCleanup) < 0) goto endjob; if (qemuMigrationJobContinue(vm) == 0) { vm = NULL; @@ -2750,8 +2750,8 @@ qemuMigrationConfirm(virConnectPtr conn, phase = QEMU_MIGRATION_PHASE_CONFIRM3; qemuMigrationJobStartPhase(driver, vm, phase); - virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm, - qemuMigrationCleanup); + virCloseCallbacksUnset(driver->closeCallbacks, vm, + qemuMigrationCleanup); ret = qemuMigrationConfirmPhase(driver, conn, vm, cookiein, cookieinlen, @@ -4123,8 +4123,8 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver, } qemuMigrationJobStartPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM3); - virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm, - qemuMigrationCleanup); + virCloseCallbacksUnset(driver->closeCallbacks, vm, + qemuMigrationCleanup); resume = virDomainObjGetState(vm, NULL) == VIR_DOMAIN_RUNNING; ret = doNativeMigrate(driver, vm, uri, cookiein, cookieinlen, @@ -4155,8 +4155,8 @@ qemuMigrationPerformPhase(virQEMUDriverPtr driver, qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PERFORM3_DONE); - if (virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn, - qemuMigrationCleanup) < 0) + if (virCloseCallbacksSet(driver->closeCallbacks, vm, conn, + qemuMigrationCleanup) < 0) goto endjob; endjob: diff --git a/src/qemu/qemu_migration.h b/src/qemu/qemu_migration.h index 43b26de..bf962b1 100644 --- a/src/qemu/qemu_migration.h +++ b/src/qemu/qemu_migration.h @@ -90,7 +90,7 @@ bool qemuMigrationJobFinish(virQEMUDriverPtr driver, virDomainObjPtr obj) int qemuMigrationSetOffline(virQEMUDriverPtr driver, virDomainObjPtr vm); -virDomainObjPtr qemuMigrationCleanup(virQEMUDriverPtr driver, +virDomainObjPtr qemuMigrationCleanup(void *driver, virDomainObjPtr vm, virConnectPtr conn); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 3d5e8f6..9b4a448 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -4468,7 +4468,7 @@ cleanup: static virDomainObjPtr -qemuProcessAutoDestroy(virQEMUDriverPtr driver, +qemuProcessAutoDestroy(void *driver, virDomainObjPtr dom, virConnectPtr conn) { @@ -4515,23 +4515,23 @@ int qemuProcessAutoDestroyAdd(virQEMUDriverPtr driver, virConnectPtr conn) { VIR_DEBUG("vm=%s, conn=%p", vm->def->name, conn); - return virQEMUCloseCallbacksSet(driver->closeCallbacks, vm, conn, - qemuProcessAutoDestroy); + return virCloseCallbacksSet(driver->closeCallbacks, vm, conn, + qemuProcessAutoDestroy); } int qemuProcessAutoDestroyRemove(virQEMUDriverPtr driver, virDomainObjPtr vm) { VIR_DEBUG("vm=%s", vm->def->name); - return virQEMUCloseCallbacksUnset(driver->closeCallbacks, vm, - qemuProcessAutoDestroy); + return virCloseCallbacksUnset(driver->closeCallbacks, vm, + qemuProcessAutoDestroy); } bool qemuProcessAutoDestroyActive(virQEMUDriverPtr driver, virDomainObjPtr vm) { - virQEMUCloseCallback cb; + virCloseCallback cb; VIR_DEBUG("vm=%s", vm->def->name); - cb = virQEMUCloseCallbacksGet(driver->closeCallbacks, vm, NULL); + cb = virCloseCallbacksGet(driver->closeCallbacks, vm, NULL); return cb == qemuProcessAutoDestroy; } diff --git a/src/util/virclosecallbacks.c b/src/util/virclosecallbacks.c new file mode 100644 index 0000000..a926456 --- /dev/null +++ b/src/util/virclosecallbacks.c @@ -0,0 +1,332 @@ +/* + * virclosecallbacks.c: Connection close callbacks routines + * + * Copyright (C) 2013 Red Hat, Inc. + * + * 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/>. + * + * Authors: + * Daniel P. Berrange <berrange@xxxxxxxxxx> + * Michal Privoznik <mprivozn@xxxxxxxxxx> + */ + +#include <config.h> + +#include "viralloc.h" +#include "virclosecallbacks.h" +#include "virlog.h" +#include "virobject.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +typedef struct _virDriverCloseDef virDriverCloseDef; +typedef virDriverCloseDef *virDriverCloseDefPtr; +struct _virDriverCloseDef { + virConnectPtr conn; + virCloseCallback cb; +}; + +struct _virCloseCallbacks { + virObjectLockable parent; + + /* UUID string to qemuDriverCloseDef mapping */ + virHashTablePtr list; +}; + + +static virClassPtr virCloseCallbacksClass; +static void virCloseCallbacksDispose(void *obj); + +static int virCloseCallbacksOnceInit(void) +{ + virCloseCallbacksClass = virClassNew(virClassForObjectLockable(), + "virCloseCallbacks", + sizeof(virCloseCallbacks), + virCloseCallbacksDispose); + + if (!virCloseCallbacksClass) + return -1; + else + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virCloseCallbacks) + + +static void +virCloseCallbacksFreeData(void *payload, + const void *name ATTRIBUTE_UNUSED) +{ + VIR_FREE(payload); +} + +virCloseCallbacksPtr +virCloseCallbacksNew(void) +{ + virCloseCallbacksPtr closeCallbacks; + + if (virCloseCallbacksInitialize() < 0) + return NULL; + + if (!(closeCallbacks = virObjectLockableNew(virCloseCallbacksClass))) + return NULL; + + closeCallbacks->list = virHashCreate(5, virCloseCallbacksFreeData); + if (!closeCallbacks->list) { + virObjectUnref(closeCallbacks); + return NULL; + } + + return closeCallbacks; +} + +static void +virCloseCallbacksDispose(void *obj) +{ + virCloseCallbacksPtr closeCallbacks = obj; + + virHashFree(closeCallbacks->list); +} + +int +virCloseCallbacksSet(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virConnectPtr conn, + virCloseCallback cb) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDriverCloseDefPtr closeDef; + int ret = -1; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, conn=%p, cb=%p", + vm->def->name, uuidstr, conn, cb); + + virObjectLock(closeCallbacks); + + closeDef = virHashLookup(closeCallbacks->list, uuidstr); + if (closeDef) { + if (closeDef->conn != conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Close callback for domain %s already registered" + " with another connection %p"), + vm->def->name, closeDef->conn); + goto cleanup; + } + if (closeDef->cb && closeDef->cb != cb) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Another close callback is already defined for" + " domain %s"), vm->def->name); + goto cleanup; + } + + closeDef->cb = cb; + } else { + if (VIR_ALLOC(closeDef) < 0) + goto cleanup; + + closeDef->conn = conn; + closeDef->cb = cb; + if (virHashAddEntry(closeCallbacks->list, uuidstr, closeDef) < 0) { + VIR_FREE(closeDef); + goto cleanup; + } + } + + ret = 0; +cleanup: + virObjectUnlock(closeCallbacks); + return ret; +} + +int +virCloseCallbacksUnset(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virCloseCallback cb) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDriverCloseDefPtr closeDef; + int ret = -1; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, cb=%p", + vm->def->name, uuidstr, cb); + + virObjectLock(closeCallbacks); + + closeDef = virHashLookup(closeCallbacks->list, uuidstr); + if (!closeDef) + goto cleanup; + + if (closeDef->cb && closeDef->cb != cb) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Trying to remove mismatching close callback for" + " domain %s"), vm->def->name); + goto cleanup; + } + + ret = virHashRemoveEntry(closeCallbacks->list, uuidstr); +cleanup: + virObjectUnlock(closeCallbacks); + return ret; +} + +virCloseCallback +virCloseCallbacksGet(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virConnectPtr conn) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virDriverCloseDefPtr closeDef; + virCloseCallback cb = NULL; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, conn=%p", + vm->def->name, uuidstr, conn); + + virObjectLock(closeCallbacks); + + closeDef = virHashLookup(closeCallbacks->list, uuidstr); + if (closeDef && (!conn || closeDef->conn == conn)) + cb = closeDef->cb; + + virObjectUnlock(closeCallbacks); + + VIR_DEBUG("cb=%p", cb); + return cb; +} + +typedef struct _virCloseCallbacksListEntry virCloseCallbacksListEntry; +typedef virCloseCallbacksListEntry *virCloseCallbacksListEntryPtr; +struct _virCloseCallbacksListEntry { + unsigned char uuid[VIR_UUID_BUFLEN]; + virCloseCallback callback; +}; + +typedef struct _virCloseCallbacksList virCloseCallbacksList; +typedef virCloseCallbacksList *virCloseCallbacksListPtr; +struct _virCloseCallbacksList { + size_t nentries; + virCloseCallbacksListEntryPtr entries; +}; + +struct virCloseCallbacksData { + virConnectPtr conn; + virCloseCallbacksListPtr list; + bool oom; +}; + +static void +virCloseCallbacksGetOne(void *payload, + const void *key, + void *opaque) +{ + struct virCloseCallbacksData *data = opaque; + virDriverCloseDefPtr closeDef = payload; + const char *uuidstr = key; + unsigned char uuid[VIR_UUID_BUFLEN]; + + if (virUUIDParse(uuidstr, uuid) < 0) + return; + + VIR_DEBUG("conn=%p, thisconn=%p, uuid=%s, cb=%p", + closeDef->conn, data->conn, uuidstr, closeDef->cb); + + if (data->conn != closeDef->conn || !closeDef->cb) + return; + + if (VIR_EXPAND_N(data->list->entries, + data->list->nentries, 1) < 0) { + data->oom = true; + return; + } + + memcpy(data->list->entries[data->list->nentries - 1].uuid, + uuid, VIR_UUID_BUFLEN); + data->list->entries[data->list->nentries - 1].callback = closeDef->cb; +} + +static virCloseCallbacksListPtr +virCloseCallbacksGetForConn(virCloseCallbacksPtr closeCallbacks, + virConnectPtr conn) +{ + virCloseCallbacksListPtr list = NULL; + struct virCloseCallbacksData data; + + if (VIR_ALLOC(list) < 0) + return NULL; + + data.conn = conn; + data.list = list; + data.oom = false; + + virHashForEach(closeCallbacks->list, virCloseCallbacksGetOne, &data); + + if (data.oom) { + VIR_FREE(list->entries); + VIR_FREE(list); + virReportOOMError(); + return NULL; + } + + return list; +} + + +void +virCloseCallbacksRun(virCloseCallbacksPtr closeCallbacks, + virConnectPtr conn, + virDomainObjListPtr domains, + void *driver) +{ + virCloseCallbacksListPtr list; + size_t i; + + VIR_DEBUG("conn=%p", conn); + + /* We must not hold the lock while running the callbacks, + * so first we obtain the list of callbacks, then remove + * them all from the hash. At that point we can release + * the lock and run the callbacks safely. */ + + virObjectLock(closeCallbacks); + list = virCloseCallbacksGetForConn(closeCallbacks, conn); + if (!list) + return; + + for (i = 0; i < list->nentries; i++) { + virHashRemoveEntry(closeCallbacks->list, + list->entries[i].uuid); + } + virObjectUnlock(closeCallbacks); + + for (i = 0; i < list->nentries; i++) { + virDomainObjPtr vm; + + if (!(vm = virDomainObjListFindByUUID(domains, + list->entries[i].uuid))) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(list->entries[i].uuid, uuidstr); + VIR_DEBUG("No domain object with UUID %s", uuidstr); + continue; + } + + vm = list->entries[i].callback(driver, vm, conn); + if (vm) + virObjectUnlock(vm); + } + VIR_FREE(list->entries); + VIR_FREE(list); +} diff --git a/src/util/virclosecallbacks.h b/src/util/virclosecallbacks.h new file mode 100644 index 0000000..9db641f --- /dev/null +++ b/src/util/virclosecallbacks.h @@ -0,0 +1,53 @@ +/* + * virclosecallbacks.h: Connection close callbacks routines + * + * Copyright (C) 2013 Red Hat, Inc. + * + * 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/>. + * + * Authors: + * Daniel P. Berrange <berrange@xxxxxxxxxx> + * Michal Privoznik <mprivozn@xxxxxxxxxx> + */ + +#ifndef __VIR_CLOSE_CALLBACKS__ +# define __VIR_CLOSE_CALLBACKS__ + +# include "domain_conf.h" + +typedef struct _virCloseCallbacks virCloseCallbacks; +typedef virCloseCallbacks *virCloseCallbacksPtr; + +typedef virDomainObjPtr (*virCloseCallback)(void *driver, + virDomainObjPtr vm, + virConnectPtr conn); +virCloseCallbacksPtr virCloseCallbacksNew(void); +int virCloseCallbacksSet(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virConnectPtr conn, + virCloseCallback cb); +int virCloseCallbacksUnset(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virCloseCallback cb); +virCloseCallback +virCloseCallbacksGet(virCloseCallbacksPtr closeCallbacks, + virDomainObjPtr vm, + virConnectPtr conn); +void +virCloseCallbacksRun(virCloseCallbacksPtr closeCallbacks, + virConnectPtr conn, + virDomainObjListPtr domains, + void *driver); +#endif /* __VIR_CLOSE_CALLBACKS__ */ -- 1.8.1.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list