diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 09d505d530f88f557d84279f0ed17082dceb2348..b5707461feec23c888033c668cc05d7cd1010ab7 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -64,6 +64,8 @@ struct daemonClientPrivate { size_t nstorageEventCallbacks; daemonClientEventCallbackPtr *nodeDeviceEventCallbacks; size_t nnodeDeviceEventCallbacks; + daemonClientEventCallbackPtr *secretEventCallbacks; + size_t nsecretEventCallbacks; bool closeRegistered; # if WITH_SASL diff --git a/daemon/remote.c b/daemon/remote.c index 3d837d85901623b277de76ed070eb50f4bf43c04..3a7ee9e6bbd427fca8040b7405d488de543af972 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -235,6 +235,34 @@ remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client, return ret; } +static bool +remoteRelaySecretEventCheckACL(virNetServerClientPtr client, + virConnectPtr conn, + virSecretPtr secret) +{ + virSecretDef def; + virIdentityPtr identity = NULL; + bool ret = false; + + /* For now, we just create a virSecretDef with enough contents to + * satisfy what viraccessdriverpolkit.c references. This is a bit + * fragile, but I don't know of anything better. */ + memcpy(def.uuid, secret->uuid, VIR_UUID_BUFLEN); + def.usage_type = secret->usageType; + def.usage_id = secret->usageID; + + if (!(identity = virNetServerClientGetIdentity(client))) + goto cleanup; + if (virIdentitySetCurrent(identity) < 0) + goto cleanup; + ret = virConnectSecretEventRegisterAnyCheckACL(conn, &def); + + cleanup: + ignore_value(virIdentitySetCurrent(NULL)); + virObjectUnref(identity); + return ret; +} + static bool remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client, virConnectPtr conn, virDomainPtr dom) @@ -1468,6 +1496,44 @@ static virConnectNodeDeviceEventGenericCallback nodeDeviceEventCallbacks[] = { verify(ARRAY_CARDINALITY(nodeDeviceEventCallbacks) == VIR_NODE_DEVICE_EVENT_ID_LAST); +static int +remoteRelaySecretEventLifecycle(virConnectPtr conn, + virSecretPtr secret, + int event, + int detail, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_secret_event_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelaySecretEventCheckACL(callback->client, conn, secret)) + return -1; + + VIR_DEBUG("Relaying node secretice lifecycle event %d, detail %d, callback %d", + event, detail, callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_secret(&data.secret, secret); + data.callbackID = callback->callbackID; + data.event = event; + data.detail = detail; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_SECRET_EVENT_LIFECYCLE, + (xdrproc_t)xdr_remote_secret_event_lifecycle_msg, + &data); + + return 0; +} + +static virConnectSecretEventGenericCallback secretEventCallbacks[] = { + VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventLifecycle), +}; + +verify(ARRAY_CARDINALITY(secretEventCallbacks) == VIR_SECRET_EVENT_ID_LAST); + static void remoteRelayDomainQemuMonitorEvent(virConnectPtr conn, virDomainPtr dom, @@ -1605,6 +1671,21 @@ void remoteClientFreeFunc(void *data) } VIR_FREE(priv->nodeDeviceEventCallbacks); + for (i = 0; i < priv->nsecretEventCallbacks; i++) { + int callbackID = priv->secretEventCallbacks[i]->callbackID; + if (callbackID < 0) { + VIR_WARN("unexpected incomplete secret callback %zu", i); + continue; + } + VIR_DEBUG("Deregistering remote secret event relay %d", + callbackID); + priv->secretEventCallbacks[i]->callbackID = -1; + if (virConnectSecretEventDeregisterAny(priv->conn, + callbackID) < 0) + VIR_WARN("unexpected secret event deregister failure"); + } + VIR_FREE(priv->secretEventCallbacks); + for (i = 0; i < priv->nqemuEventCallbacks; i++) { int callbackID = priv->qemuEventCallbacks[i]->callbackID; if (callbackID < 0) { @@ -5937,6 +6018,127 @@ remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server ATTRIBU return rv; } +static int +remoteDispatchConnectSecretEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_secret_event_register_any_args *args, + remote_connect_secret_event_register_any_ret *ret) +{ + int callbackID; + int rv = -1; + daemonClientEventCallbackPtr callback = NULL; + daemonClientEventCallbackPtr ref; + struct daemonClientPrivate *priv = + virNetServerClientGetPrivateData(client); + virSecretPtr secret = NULL; + + if (!priv->conn) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not open")); + goto cleanup; + } + + virMutexLock(&priv->lock); + + if (args->secret && + !(secret = get_nonnull_secret(priv->conn, *args->secret))) + goto cleanup; + + if (args->eventID >= VIR_SECRET_EVENT_ID_LAST || args->eventID < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported secret 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->secretEventCallbacks, + priv->nsecretEventCallbacks, + callback) < 0) + goto cleanup; + + if ((callbackID = virConnectSecretEventRegisterAny(priv->conn, + secret, + args->eventID, + secretEventCallbacks[args->eventID], + ref, + remoteEventCallbackFree)) < 0) { + VIR_SHRINK_N(priv->secretEventCallbacks, + priv->nsecretEventCallbacks, 1); + callback = ref; + goto cleanup; + } + + ref->callbackID = callbackID; + ret->callbackID = callbackID; + + rv = 0; + + cleanup: + VIR_FREE(callback); + if (rv < 0) + virNetMessageSaveError(rerr); + virObjectUnref(secret); + virMutexUnlock(&priv->lock); + return rv; +} + +static int +remoteDispatchConnectSecretEventDeregisterAny(virNetServerPtr server ATTRIBUTE_UNUSED, + virNetServerClientPtr client, + virNetMessagePtr msg ATTRIBUTE_UNUSED, + virNetMessageErrorPtr rerr ATTRIBUTE_UNUSED, + remote_connect_secret_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->nsecretEventCallbacks; i++) { + if (priv->secretEventCallbacks[i]->callbackID == args->callbackID) + break; + } + if (i == priv->nsecretEventCallbacks) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("node device event callback %d not registered"), + args->callbackID); + goto cleanup; + } + + if (virConnectSecretEventDeregisterAny(priv->conn, args->callbackID) < 0) + goto cleanup; + + VIR_DELETE_ELEMENT(priv->secretEventCallbacks, i, + priv->nsecretEventCallbacks); + + rv = 0; + + cleanup: + if (rv < 0) + virNetMessageSaveError(rerr); + virMutexUnlock(&priv->lock); + return rv; +} + static int qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED, virNetServerClientPtr client, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c161a561987a256ff4462e1c2ba3c0de9196ff82..e68931617503948df30ce9cdd8dc235e2ad4d376 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -36,6 +36,7 @@ #include "network_event.h" #include "storage_event.h" #include "node_device_event.h" +#include "secret_event.h" #include "driver.h" #include "virbuffer.h" #include "remote_driver.h" @@ -384,6 +385,11 @@ remoteNodeDeviceBuildEventUpdate(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); +static void +remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); + static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -583,6 +589,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteNodeDeviceBuildEventUpdate, sizeof(remote_node_device_event_update_msg), (xdrproc_t)xdr_remote_node_device_event_update_msg }, + { REMOTE_PROC_SECRET_EVENT_LIFECYCLE, + remoteSecretBuildEventLifecycle, + sizeof(remote_secret_event_lifecycle_msg), + (xdrproc_t)xdr_remote_secret_event_lifecycle_msg }, }; static void @@ -3314,6 +3324,103 @@ remoteConnectNodeDeviceEventDeregisterAny(virConnectPtr conn, } +static int +remoteConnectSecretEventRegisterAny(virConnectPtr conn, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + remote_connect_secret_event_register_any_args args; + remote_connect_secret_event_register_any_ret ret; + int callbackID; + int count; + remote_nonnull_secret sec; + + remoteDriverLock(priv); + + if ((count = virSecretEventStateRegisterClient(conn, priv->eventState, + secret, 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 (secret) { + make_nonnull_secret(&sec, secret); + args.secret = &sec; + } else { + args.secret = NULL; + } + + memset(&ret, 0, sizeof(ret)); + if (call(conn, priv, 0, REMOTE_PROC_CONNECT_SECRET_EVENT_REGISTER_ANY, + (xdrproc_t) xdr_remote_connect_secret_event_register_any_args, (char *) &args, + (xdrproc_t) xdr_remote_connect_secret_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 +remoteConnectSecretEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + remote_connect_secret_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_SECRET_EVENT_DEREGISTER_ANY, + (xdrproc_t) xdr_remote_connect_secret_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, virDomainPtr dom, @@ -5387,6 +5494,28 @@ remoteNodeDeviceBuildEventUpdate(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, remoteEventQueue(priv, event, msg->callbackID); } +static void +remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_secret_event_lifecycle_msg *msg = evdata; + virSecretPtr secret; + virObjectEventPtr event = NULL; + + secret = get_nonnull_secret(conn, msg->secret); + if (!secret) + return; + + event = virSecretEventLifecycleNew(secret->uuid, secret->usageType, secret->usageID, + msg->event, msg->detail); + virObjectUnref(secret); + + remoteEventQueue(priv, event, msg->callbackID); +} + static void remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, @@ -8375,7 +8504,9 @@ static virSecretDriver secret_driver = { .secretGetXMLDesc = remoteSecretGetXMLDesc, /* 0.7.1 */ .secretSetValue = remoteSecretSetValue, /* 0.7.1 */ .secretGetValue = remoteSecretGetValue, /* 0.7.1 */ - .secretUndefine = remoteSecretUndefine /* 0.7.1 */ + .secretUndefine = remoteSecretUndefine, /* 0.7.1 */ + .connectSecretEventDeregisterAny = remoteConnectSecretEventDeregisterAny, /* 3.0.0 */ + .connectSecretEventRegisterAny = remoteConnectSecretEventRegisterAny, /* 3.0.0 */ }; static virNodeDeviceDriver node_device_driver = { diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f268e1c0d54d811bf035f14ec58d9d082105dd64..ce4c4ef36e36cee7875e05f13db016ed472c1be3 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -319,6 +319,7 @@ typedef remote_nonnull_nwfilter *remote_nwfilter; typedef remote_nonnull_storage_pool *remote_storage_pool; typedef remote_nonnull_storage_vol *remote_storage_vol; typedef remote_nonnull_node_device *remote_node_device; +typedef remote_nonnull_secret *remote_secret; /* Error message. See for explanation of fields. */ @@ -3360,6 +3361,26 @@ struct remote_domain_event_callback_metadata_change_msg { remote_string nsuri; }; +struct remote_connect_secret_event_register_any_args { + int eventID; + remote_secret secret; +}; + +struct remote_connect_secret_event_register_any_ret { + int callbackID; +}; + +struct remote_connect_secret_event_deregister_any_args { + int callbackID; +}; + +struct remote_secret_event_lifecycle_msg { + int callbackID; + remote_nonnull_secret secret; + int event; + int detail; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -5965,5 +5986,27 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379 + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379, + + /** + * @generate: none + * @priority: high + * @acl: connect:search_secrets + * @aclfilter: secret:getattr + */ + REMOTE_PROC_CONNECT_SECRET_EVENT_REGISTER_ANY = 380, + + /** + * @generate: none + * @priority: high + * @acl: connect:read + */ + REMOTE_PROC_CONNECT_SECRET_EVENT_DEREGISTER_ANY = 381, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382 + }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 2fc9e460f6c51b1c54e31bbb1332e470585936b5..0e8291a927e33b741314dd7109e1cb412f5fe7d6 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2806,6 +2806,22 @@ struct remote_domain_event_callback_metadata_change_msg { int type; remote_string nsuri }; +struct remote_connect_secret_event_register_any_args { + int eventID; + remote_secret secret; +}; +struct remote_connect_secret_event_register_any_ret { + int callbackID; +}; +struct remote_connect_secret_event_deregister_any_args { + int callbackID; +}; +struct remote_secret_event_lifecycle_msg { + int callbackID; + remote_nonnull_secret secret; + int event; + int detail; +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3186,4 +3202,7 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378, REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379, + REMOTE_PROC_CONNECT_SECRET_EVENT_REGISTER_ANY = 380, + REMOTE_PROC_CONNECT_SECRET_EVENT_DEREGISTER_ANY = 381, + REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382, };