diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 7271b0f1f41f5546d7bd0dcdd73dcb019388b411..cc91266b70e4bfc71741d79484a091b6375a089d 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -60,6 +60,8 @@ struct daemonClientPrivate { size_t nnetworkEventCallbacks; daemonClientEventCallbackPtr *qemuEventCallbacks; size_t nqemuEventCallbacks; + daemonClientEventCallbackPtr *storageEventCallbacks; + size_t nstorageEventCallbacks; bool closeRegistered; # if WITH_SASL diff --git a/daemon/remote.c b/daemon/remote.c index b2a420bc0021e937ed052e4aea19fbbb9d90a9f6..4e2aff87b48cde1d7955e04fbb7fbc33935597bc 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -181,6 +181,32 @@ remoteRelayNetworkEventCheckACL(virNetServerClientPtr client, return ret; } +static bool +remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client, + virConnectPtr conn, + virStoragePoolPtr pool) +{ + virStoragePoolDef def; + virIdentityPtr identity = NULL; + bool ret = false; + + /* For now, we just create a virStoragePoolDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + def.name = pool->name; + memcpy(def.uuid, pool->uuid, VIR_UUID_BUFLEN); + + if (!(identity = virNetServerClientGetIdentity(client))) + goto cleanup; + if (virIdentitySetCurrent(identity) < 0) + goto cleanup; + ret = virConnectStoragePoolEventRegisterAnyCheckACL(conn, &def); + + cleanup: + ignore_value(virIdentitySetCurrent(NULL)); + virObjectUnref(identity); + return ret; +} static bool remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client, @@ -1236,6 +1262,44 @@ static virConnectNetworkEventGenericCallback networkEventCallbacks[] = { verify(ARRAY_CARDINALITY(networkEventCallbacks) == VIR_NETWORK_EVENT_ID_LAST); +static int +remoteRelayStoragePoolEventLifecycle(virConnectPtr conn, + virStoragePoolPtr pool, + int event, + int detail, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_storage_pool_event_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayStoragePoolEventCheckACL(callback->client, conn, pool)) + return -1; + + VIR_DEBUG("Relaying storage pool lifecycle event %d, detail %d, callback %d", + event, detail, callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_storage_pool(&data.pool, pool); + data.callbackID = callback->callbackID; + data.event = event; + data.detail = detail; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, + (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg, + &data); + + return 0; +} + +static virConnectStoragePoolEventGenericCallback storageEventCallbacks[] = { + VIR_STORAGE_POOL_EVENT_CALLBACK(remoteRelayStoragePoolEventLifecycle), +}; + +verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST); + static void remoteRelayDomainQemuMonitorEvent(virConnectPtr conn, virDomainPtr dom, @@ -1343,6 +1407,21 @@ void remoteClientFreeFunc(void *data) } VIR_FREE(priv->networkEventCallbacks); + for (i = 0; i < priv->nstorageEventCallbacks; i++) { + int callbackID = priv->storageEventCallbacks[i]->callbackID; + if (callbackID < 0) { + VIR_WARN("unexpected incomplete storage pool callback %zu", i); + continue; + } + VIR_DEBUG("Deregistering remote storage pool event relay %d", + callbackID); + priv->storageEventCallbacks[i]->callbackID = -1; + if (virConnectStoragePoolEventDeregisterAny(priv->conn, + callbackID) < 0) + VIR_WARN("unexpected storage pool event deregister failure"); + } + VIR_FREE(priv->storageEventCallbacks); + for (i = 0; i < priv->nqemuEventCallbacks; i++) { int callbackID = priv->qemuEventCallbacks[i]->callbackID; if (callbackID < 0) { @@ -3528,8 +3607,10 @@ remoteDispatchConnectDomainEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED * to our array, but on OOM append failure, we'd have to then hope * deregister works to undo our register. So instead we append an * incomplete callback to our array, then register, then fix up - * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on - * success, we use 'ref' to save a copy of the pointer. */ + * our callback; or you can use VIR_APPEND_ELEMENT_COPY to avoid + * clearing 'callback' and having to juggle the pointer + * between 'ref' and 'callback'. + */ if (VIR_ALLOC(callback) < 0) goto cleanup; callback->client = client; @@ -5434,6 +5515,127 @@ remoteDispatchConnectNetworkEventDeregisterAny(virNetServerPtr server ATTRIBUTE_ return rv; } +static int +remoteDispatchConnectStoragePoolEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_storage_pool_event_register_any_args *args, + remote_connect_storage_pool_event_register_any_ret *ret) +{ + int callbackID; + int rv = -1; + daemonClientEventCallbackPtr callback = NULL; + daemonClientEventCallbackPtr ref; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + virStoragePoolPtr pool = NULL; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + if (args->pool && + !(pool = get_nonnull_storage_pool(priv->conn, *args->pool))) + goto cleanup; + + if (args->eventID >= VIR_STORAGE_POOL_EVENT_ID_LAST || args->eventID < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported storage pool event ID %d"), args->eventID); + goto cleanup; + } + + /* If we call register first, we could append a complete callback + * to our array, but on OOM append failure, we'd have to then hope + * deregister works to undo our register. So instead we append an + * incomplete callback to our array, then register, then fix up + * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on + * success, we use 'ref' to save a copy of the pointer. */ + if (VIR_ALLOC(callback) < 0) + goto cleanup; + callback->client = client; + callback->eventID = args->eventID; + callback->callbackID = -1; + ref = callback; + if (VIR_APPEND_ELEMENT(priv->storageEventCallbacks, + priv->nstorageEventCallbacks, + callback) < 0) + goto cleanup; + + if ((callbackID = virConnectStoragePoolEventRegisterAny(priv->conn, + pool, + args->eventID, + storageEventCallbacks[args->eventID], + ref, + remoteEventCallbackFree)) < 0) { + VIR_SHRINK_N(priv->storageEventCallbacks, + priv->nstorageEventCallbacks, 1); + callback = ref; + goto cleanup; + } + + ref->callbackID = callbackID; + ret->callbackID = callbackID; + + rv = 0; + + cleanup: + VIR_FREE(callback); + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(pool); + virMutexUnlock(&priv->lock); + return rv; +} + +static int +remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_storage_pool_event_deregister_any_args *args) +{ + int rv = -1; + size_t i; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + for (i = 0; i < priv->nstorageEventCallbacks; i++) { + if (priv->storageEventCallbacks[i]->callbackID == args->callbackID) + break; + } + if (i == priv->nstorageEventCallbacks) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("storage pool event callback %d not registered"), + args->callbackID); + goto cleanup; + } + + if (virConnectStoragePoolEventDeregisterAny(priv->conn, args->callbackID) < 0) + goto cleanup; + + VIR_DELETE_ELEMENT(priv->storageEventCallbacks, i, + priv->nstorageEventCallbacks); + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + static int qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f494cbf3a300a8707a2c17e9fea758f6133eb622..84b6d58c1131536e994ae798c6584fc23906e0c6 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -34,6 +34,7 @@ #include "datatypes.h" #include "domain_event.h" #include "network_event.h" +#include "storage_event.h" #include "driver.h" #include "virbuffer.h" #include "remote_driver.h" @@ -355,6 +356,11 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); +static void +remoteStoragePoolBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); + static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -534,6 +540,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteDomainBuildEventCallbackDeviceRemovalFailed, sizeof(remote_domain_event_callback_device_removal_failed_msg), (xdrproc_t)xdr_remote_domain_event_callback_device_removal_failed_msg }, + { REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, + remoteStoragePoolBuildEventLifecycle, + sizeof(remote_storage_pool_event_lifecycle_msg), + (xdrproc_t)xdr_remote_storage_pool_event_lifecycle_msg }, }; static void @@ -3040,6 +3050,101 @@ remoteConnectNetworkEventDeregisterAny(virConnectPtr conn, return rv; } +static int +remoteConnectStoragePoolEventRegisterAny(virConnectPtr conn, + virStoragePoolPtr pool, + int eventID, + virConnectStoragePoolEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + remote_connect_storage_pool_event_register_any_args args; + remote_connect_storage_pool_event_register_any_ret ret; + int callbackID; + int count; + remote_nonnull_storage_pool storage_pool; + + remoteDriverLock(priv); + + if ((count = virStoragePoolEventStateRegisterClient(conn, priv->eventState, + pool, eventID, callback, + opaque, freecb, + &callbackID)) < 0) + goto done; + + /* If this is the first callback for this eventID, we need to enable + * events on the server */ + if (count == 1) { + args.eventID = eventID; + if (pool) { + make_nonnull_storage_pool(&storage_pool, pool); + args.pool = &storage_pool; + } else { + args.pool = NULL; + } + + memset(&ret, 0, sizeof(ret)); + if (call(conn, priv, 0, REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY, + (xdrproc_t) xdr_remote_connect_storage_pool_event_register_any_args, (char *) &args, + (xdrproc_t) xdr_remote_connect_storage_pool_event_register_any_ret, (char *) &ret) == -1) { + virObjectEventStateDeregisterID(conn, priv->eventState, + callbackID); + goto done; + } + + virObjectEventStateSetRemote(conn, priv->eventState, callbackID, + ret.callbackID); + } + + rv = callbackID; + + done: + remoteDriverUnlock(priv); + return rv; +} + +static int +remoteConnectStoragePoolEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + remote_connect_storage_pool_event_deregister_any_args args; + int eventID; + int remoteID; + int count; + + remoteDriverLock(priv); + + if ((eventID = virObjectEventStateEventID(conn, priv->eventState, + callbackID, &remoteID)) < 0) + goto done; + + if ((count = virObjectEventStateDeregisterID(conn, priv->eventState, + callbackID)) < 0) + goto done; + + /* If that was the last callback for this eventID, we need to disable + * events on the server */ + if (count == 0) { + args.callbackID = remoteID; + + if (call(conn, priv, 0, REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY, + (xdrproc_t) xdr_remote_connect_storage_pool_event_deregister_any_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + + } + + rv = 0; + + done: + remoteDriverUnlock(priv); + return rv; +} + static int remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn, @@ -5013,6 +5118,27 @@ remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, remoteEventQueue(priv, event, msg->callbackID); } +static void +remoteStoragePoolBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_storage_pool_event_lifecycle_msg *msg = evdata; + virStoragePoolPtr pool; + virObjectEventPtr event = NULL; + + pool = get_nonnull_storage_pool(conn, msg->pool); + if (!pool) + return; + + event = virStoragePoolEventLifecycleNew(pool->name, pool->uuid, msg->event, + msg->detail); + virObjectUnref(pool); + + remoteEventQueue(priv, event, msg->callbackID); +} static void remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, @@ -7908,6 +8034,8 @@ static virStorageDriver storage_driver = { .connectListDefinedStoragePools = remoteConnectListDefinedStoragePools, /* 0.4.1 */ .connectListAllStoragePools = remoteConnectListAllStoragePools, /* 0.10.2 */ .connectFindStoragePoolSources = remoteConnectFindStoragePoolSources, /* 0.4.5 */ + .connectStoragePoolEventDeregisterAny = remoteConnectStoragePoolEventDeregisterAny, /* 2.0.0 */ + .connectStoragePoolEventRegisterAny = remoteConnectStoragePoolEventRegisterAny, /* 2.0.0 */ .storagePoolLookupByName = remoteStoragePoolLookupByName, /* 0.4.1 */ .storagePoolLookupByUUID = remoteStoragePoolLookupByUUID, /* 0.4.1 */ .storagePoolLookupByVolume = remoteStoragePoolLookupByVolume, /* 0.4.1 */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index bab8ef2304874d39d2e4ba963d3f62c0a685fd5d..0170c0c182111b92a4cadb2fe25b00660944e796 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3098,6 +3098,26 @@ struct remote_network_event_lifecycle_msg { int detail; }; +struct remote_connect_storage_pool_event_register_any_args { + int eventID; + remote_storage_pool pool; +}; + +struct remote_connect_storage_pool_event_register_any_ret { + int callbackID; +}; + +struct remote_connect_storage_pool_event_deregister_any_args { + int callbackID; +}; + +struct remote_storage_pool_event_lifecycle_msg { + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; +}; + struct remote_domain_fsfreeze_args { remote_nonnull_domain dom; remote_nonnull_string mountpoints; /* (const char **) */ @@ -5793,5 +5813,26 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367 + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367, + + /** + * @generate: none + * @priority: high + * @acl: connect:search_storage_pools + * @aclfilter: storage_pool:getattr + */ + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY = 368, + + /** + * @generate: none + * @priority: high + * @acl: connect:read + */ + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY = 369, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE = 370 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index fe1b8a8adfda13357617c27044ef03564e87f9e2..3934e0751b283b4891bf7e4b96c90564eefa99c0 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2551,6 +2551,22 @@ struct remote_network_event_lifecycle_msg { int event; int detail; }; +struct remote_connect_storage_pool_event_register_any_args { + int eventID; + remote_storage_pool pool; +}; +struct remote_connect_storage_pool_event_register_any_ret { + int callbackID; +}; +struct remote_connect_storage_pool_event_deregister_any_args { + int callbackID; +}; +struct remote_storage_pool_event_lifecycle_msg { + int callbackID; + remote_nonnull_storage_pool pool; + int event; + int detail; +}; struct remote_domain_fsfreeze_args { remote_nonnull_domain dom; struct { @@ -3103,4 +3119,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_PERF_EVENTS = 365, REMOTE_PROC_DOMAIN_SET_PERF_EVENTS = 366, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_DEVICE_REMOVAL_FAILED = 367, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_REGISTER_ANY = 368, + REMOTE_PROC_CONNECT_STORAGE_POOL_EVENT_DEREGISTER_ANY = 369, + REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE = 370, };