diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index a709cbf1253a20b3f3d95b86be2d983b461b95a8..88a04bc06fb7220bdf2011951c4f32467172a87d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -56,6 +56,11 @@ #define VIR_FROM_THIS VIR_FROM_QEMU +struct _qemuDriverCloseDef { + virConnectPtr conn; + qemuDriverCloseCallback cb; +}; + void qemuDriverLock(struct qemud_driver *driver) { virMutexLock(&driver->lock); @@ -490,3 +495,170 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, virConfFree (conf); return 0; } + +static void +qemuDriverCloseCallbackFree(void *payload, + const void *name ATTRIBUTE_UNUSED) +{ + VIR_FREE(payload); +} + +int +qemuDriverCloseCallbackInit(struct qemud_driver *driver) +{ + driver->closeCallbacks = virHashCreate(5, qemuDriverCloseCallbackFree); + if (!driver->closeCallbacks) + return -1; + + return 0; +} + +void +qemuDriverCloseCallbackShutdown(struct qemud_driver *driver) +{ + virHashFree(driver->closeCallbacks); +} + +int +qemuDriverCloseCallbackSet(struct qemud_driver *driver, + virDomainObjPtr vm, + virConnectPtr conn, + qemuDriverCloseCallback cb) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + qemuDriverCloseDefPtr closeDef; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, conn=%p, cb=%p", + vm->def->name, uuidstr, conn, cb); + + closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + if (closeDef) { + if (closeDef->conn != conn) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Close callback for domain %s already registered" + " with another connection %p"), + vm->def->name, closeDef->conn); + return -1; + } + if (closeDef->cb && closeDef->cb != cb) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Another close callback is already defined for" + " domain %s"), vm->def->name); + return -1; + } + + closeDef->cb = cb; + } else { + if (VIR_ALLOC(closeDef) < 0) { + virReportOOMError(); + return -1; + } + + closeDef->conn = conn; + closeDef->cb = cb; + if (virHashAddEntry(driver->closeCallbacks, uuidstr, closeDef) < 0) { + VIR_FREE(closeDef); + return -1; + } + } + return 0; +} + +int +qemuDriverCloseCallbackUnset(struct qemud_driver *driver, + virDomainObjPtr vm, + qemuDriverCloseCallback cb) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + qemuDriverCloseDefPtr closeDef; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, cb=%p", + vm->def->name, uuidstr, cb); + + closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + if (!closeDef) + return -1; + + if (closeDef->cb && closeDef->cb != cb) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("Trying to remove mismatching close callback for" + " domain %s"), vm->def->name); + return -1; + } + + return virHashRemoveEntry(driver->closeCallbacks, uuidstr); +} + +qemuDriverCloseCallback +qemuDriverCloseCallbackGet(struct qemud_driver *driver, + virDomainObjPtr vm, + virConnectPtr conn) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + qemuDriverCloseDefPtr closeDef; + qemuDriverCloseCallback cb = NULL; + + virUUIDFormat(vm->def->uuid, uuidstr); + VIR_DEBUG("vm=%s, uuid=%s, conn=%p", + vm->def->name, uuidstr, conn); + + closeDef = virHashLookup(driver->closeCallbacks, uuidstr); + if (closeDef && (!conn || closeDef->conn == conn)) + cb = closeDef->cb; + + VIR_DEBUG("cb=%p", cb); + return cb; +} + +struct qemuDriverCloseCallbackData { + struct qemud_driver *driver; + virConnectPtr conn; +}; + +static void +qemuDriverCloseCallbackRun(void *payload, + const void *name, + void *opaque) +{ + struct qemuDriverCloseCallbackData *data = opaque; + qemuDriverCloseDefPtr closeDef = payload; + const char *uuidstr = name; + unsigned char uuid[VIR_UUID_BUFLEN]; + virDomainObjPtr dom; + + 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 (virUUIDParse(uuidstr, uuid) < 0) { + VIR_WARN("Failed to parse %s", uuidstr); + return; + } + + if (!(dom = virDomainFindByUUID(&data->driver->domains, uuid))) { + VIR_DEBUG("No domain object with UUID %s", uuidstr); + return; + } + + dom = closeDef->cb(data->driver, dom, data->conn); + if (dom) + virDomainObjUnlock(dom); + + virHashRemoveEntry(data->driver->closeCallbacks, uuidstr); +} + +void +qemuDriverCloseCallbackRunAll(struct qemud_driver *driver, + virConnectPtr conn) +{ + struct qemuDriverCloseCallbackData data = { + driver, conn + }; + VIR_DEBUG("conn=%p", conn); + + virHashForEach(driver->closeCallbacks, qemuDriverCloseCallbackRun, &data); +} diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 36f1c4c4b622db7572e6305d39429ada5dd7f680..4fc65972aa09c0ab1a3fb28abf950a1efb843779 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -46,6 +46,8 @@ # define QEMUD_CPUMASK_LEN CPU_SETSIZE +typedef struct _qemuDriverCloseDef qemuDriverCloseDef; +typedef qemuDriverCloseDef *qemuDriverCloseDefPtr; /* Main driver state */ struct qemud_driver { @@ -144,6 +146,13 @@ struct qemud_driver { * when the virConnectPtr is closed*/ virHashTablePtr autodestroy; + /* Mapping of 'char *uuidstr' -> qemuDriverCloseDefPtr of domains + * which want a specific cleanup to be done when a connection is + * closed. Such cleanup may be to automatically destroy the + * domain or abort a particular job running on it. + */ + virHashTablePtr closeCallbacks; + int keepAliveInterval; unsigned int keepAliveCount; }; @@ -180,4 +189,22 @@ struct qemuDomainDiskInfo { int io_status; }; +typedef virDomainObjPtr (*qemuDriverCloseCallback)(struct qemud_driver *driver, + virDomainObjPtr vm, + virConnectPtr conn); +int qemuDriverCloseCallbackInit(struct qemud_driver *driver); +void qemuDriverCloseCallbackShutdown(struct qemud_driver *driver); +int qemuDriverCloseCallbackSet(struct qemud_driver *driver, + virDomainObjPtr vm, + virConnectPtr conn, + qemuDriverCloseCallback cb); +int qemuDriverCloseCallbackUnset(struct qemud_driver *driver, + virDomainObjPtr vm, + qemuDriverCloseCallback cb); +qemuDriverCloseCallback qemuDriverCloseCallbackGet(struct qemud_driver *driver, + virDomainObjPtr vm, + virConnectPtr conn); +void qemuDriverCloseCallbackRunAll(struct qemud_driver *driver, + virConnectPtr conn); + #endif /* __QEMUD_CONF_H */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 85f8cf74505dbbfff70a6188712554e31801fef9..7b721ad6ed26d88c237e1dfd609bd7213bc142f1 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -662,6 +662,9 @@ qemudStartup(int privileged) { if (qemuProcessAutoDestroyInit(qemu_driver) < 0) goto error; + if (qemuDriverCloseCallbackInit(qemu_driver) < 0) + goto error; + /* Get all the running persistent or transient configs first */ if (virDomainLoadAllConfigs(qemu_driver->caps, &qemu_driver->domains, @@ -801,6 +804,7 @@ qemudShutdown(void) { virSysinfoDefFree(qemu_driver->hostsysinfo); qemuProcessAutoDestroyShutdown(qemu_driver); + qemuDriverCloseCallbackShutdown(qemu_driver); VIR_FREE(qemu_driver->configDir); VIR_FREE(qemu_driver->autostartDir); @@ -922,6 +926,7 @@ static int qemudClose(virConnectPtr conn) { virDomainEventStateDeregisterConn(conn, driver->domainEventState); qemuProcessAutoDestroyRun(driver, conn); + qemuDriverCloseCallbackRunAll(driver, conn); qemuDriverUnlock(driver); conn->privateData = NULL;