[libvirt] [PATCH 0/9] More event handling support

This series adds suppoort for more events - Event when <metadata> in a domain changes - Event when virSecret are created/deleted - Event when virSecret value is changed The latter two of course required wiring up the secret object for event support, since we've not had secret events before now. Daniel P. Berrange (9): Add domain event for metadata changes Introduce secret lifecycle event APIs conf: add secret event handling conf: simplify internal virSecretDef handling of usage remote: implement secret lifecycle event APIs secret: add support for lifecycle events examples: add secret events to event demo program virsh: add secret lifecycle event handling secret: add support for value change events daemon/libvirtd.h | 2 + daemon/remote.c | 276 ++++++++++++++++++++++++++++++++ examples/object-events/event-test.c | 107 +++++++++++++ include/libvirt/libvirt-domain.h | 20 +++ include/libvirt/libvirt-secret.h | 92 +++++++++++ src/Makefile.am | 5 + src/access/viraccessdriverpolkit.c | 8 +- src/bhyve/bhyve_driver.c | 7 + src/conf/domain_event.c | 86 ++++++++++ src/conf/domain_event.h | 10 ++ src/conf/secret_conf.c | 74 ++------- src/conf/secret_conf.h | 9 +- src/conf/secret_event.c | 311 ++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 65 ++++++++ src/conf/virsecretobj.c | 42 +---- src/datatypes.c | 3 +- src/datatypes.h | 14 ++ src/driver-secret.h | 14 ++ src/libvirt-secret.c | 129 +++++++++++++++ src/libvirt_private.syms | 9 +- src/libvirt_public.syms | 2 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_driver.c | 6 + src/remote/remote_driver.c | 196 ++++++++++++++++++++++- src/remote/remote_protocol.x | 69 +++++++- src/remote_protocol-structs | 26 +++ src/secret/secret_driver.c | 82 +++++++++- src/storage/storage_backend.c | 2 +- src/test/test_driver.c | 6 + tools/virsh-domain.c | 25 +++ tools/virsh-secret.c | 225 ++++++++++++++++++++++++++ tools/virsh.pod | 18 +++ 32 files changed, 1829 insertions(+), 117 deletions(-) create mode 100644 src/conf/secret_event.c create mode 100644 src/conf/secret_event.h -- 2.9.3

When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 45 +++++++++++++++++++ examples/object-events/event-test.c | 35 +++++++++++++++ include/libvirt/libvirt-domain.h | 20 +++++++++ src/bhyve/bhyve_driver.c | 7 +++ src/conf/domain_event.c | 86 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 10 +++++ src/libvirt_private.syms | 2 + src/lxc/lxc_driver.c | 6 +++ src/qemu/qemu_driver.c | 6 +++ src/remote/remote_driver.c | 33 +++++++++++++- src/remote/remote_protocol.x | 15 ++++++- src/remote_protocol-structs | 7 +++ src/test/test_driver.c | 6 +++ tools/virsh-domain.c | 25 +++++++++++ 14 files changed, 301 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index 23c9de4..3d837d8 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1223,6 +1223,50 @@ remoteRelayDomainEventDeviceRemovalFailed(virConnectPtr conn, } +static int +remoteRelayDomainEventMetadataChange(virConnectPtr conn, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_metadata_change_msg data; + char **nsurip; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain metadata change %s %d %d %s, callback %d", + dom->name, dom->id, type, NULLSTR(nsuri), callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + + data.type = type; + if (nsuri) { + if (VIR_ALLOC(nsurip) < 0) + return -1; + if (VIR_STRDUP(*nsurip, nsuri) < 0) { + VIR_FREE(nsurip); + return -1; + } + data.nsuri = nsurip; + } + + make_nonnull_domain(&data.dom, dom); + data.callbackID = callback->callbackID; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE, + (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg, + &data); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), @@ -1248,6 +1292,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMetadataChange), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index 730cb8b..f2316d7 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -917,6 +917,40 @@ myDomainEventDeviceRemovalFailedCallback(virConnectPtr conn ATTRIBUTE_UNUSED, } +static const char * +metadataTypeToStr(int status) +{ + switch ((virDomainMetadataType) status) { + case VIR_DOMAIN_METADATA_DESCRIPTION: + return "description"; + + case VIR_DOMAIN_METADATA_TITLE: + return "title"; + + case VIR_DOMAIN_METADATA_ELEMENT: + return "element"; + + case VIR_DOMAIN_METADATA_LAST: + break; + } + + return "unknown"; +} + +static int +myDomainEventMetadataChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque ATTRIBUTE_UNUSED) +{ + const char *typestr = metadataTypeToStr(type); + printf("%s EVENT: Domain %s(%d) metadata type: %s (%s)\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), typestr, nsuri ? nsuri : "n/a"); + return 0; +} + + static void myFreeFunc(void *opaque) @@ -971,6 +1005,7 @@ struct domainEventData domainEvents[] = { DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION, myDomainEventMigrationIterationCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_JOB_COMPLETED, myDomainEventJobCompletedCallback), DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED, myDomainEventDeviceRemovalFailedCallback), + DOMAIN_EVENT(VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, myDomainEventMetadataChangeCallback), }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 6362947..e303140 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3770,6 +3770,25 @@ typedef void (*virConnectDomainEventDeviceRemovalFailedCallback)(virConnectPtr c const char *devAlias, void *opaque); +/** + * virConnectDomainEventMetadataChangeCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @type: a value from virDomainMetadataTypea + * @nsuri: XML namespace URI + * @opaque: application specified data + * + * This callback is triggered when the domain XML metadata is changed + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_METADATA_CHANGE with virConnectDomainEventRegisterAny(). + */ +typedef void (*virConnectDomainEventMetadataChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque); + /** * virConnectDomainEventMigrationIterationCallback: @@ -4195,6 +4214,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_MIGRATION_ITERATION = 20, /* virConnectDomainEventMigrationIterationCallback */ VIR_DOMAIN_EVENT_ID_JOB_COMPLETED = 21, /* virConnectDomainEventJobCompletedCallback */ VIR_DOMAIN_EVENT_ID_DEVICE_REMOVAL_FAILED = 22, /* virConnectDomainEventDeviceRemovalFailedCallback */ + VIR_DOMAIN_EVENT_ID_METADATA_CHANGE = 23, /* virConnectDomainEventMetadataChangeCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/bhyve/bhyve_driver.c b/src/bhyve/bhyve_driver.c index 17f8524..e42ec47 100644 --- a/src/bhyve/bhyve_driver.c +++ b/src/bhyve/bhyve_driver.c @@ -1124,6 +1124,13 @@ bhyveDomainSetMetadata(virDomainPtr dom, privconn->xmlopt, BHYVE_STATE_DIR, BHYVE_CONFIG_DIR, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + virObjectEventStateQueue(privconn->domainEventState, ev); + } + + cleanup: virObjectUnref(caps); virObjectUnlock(vm); diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 63ae9e1..da503f3 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -59,6 +59,7 @@ static virClassPtr virDomainEventDeviceAddedClass; static virClassPtr virDomainEventMigrationIterationClass; static virClassPtr virDomainEventJobCompletedClass; static virClassPtr virDomainEventDeviceRemovalFailedClass; +static virClassPtr virDomainEventMetadataChangeClass; static void virDomainEventDispose(void *obj); static void virDomainEventLifecycleDispose(void *obj); @@ -79,6 +80,7 @@ static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); static void virDomainEventDeviceRemovalFailedDispose(void *obj); +static void virDomainEventMetadataChangeDispose(void *obj); static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, @@ -266,6 +268,15 @@ struct _virDomainEventDeviceRemovalFailed { typedef struct _virDomainEventDeviceRemovalFailed virDomainEventDeviceRemovalFailed; typedef virDomainEventDeviceRemovalFailed *virDomainEventDeviceRemovalFailedPtr; +struct _virDomainEventMetadataCange { + virDomainEvent parent; + + int type; + char *nsuri; +}; +typedef struct _virDomainEventMetadataCange virDomainEventMetadataChange; +typedef virDomainEventMetadataChange *virDomainEventMetadataChangePtr; + static int @@ -385,6 +396,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventDeviceRemovalFailed), virDomainEventDeviceRemovalFailedDispose))) return -1; + if (!(virDomainEventMetadataChangeClass = + virClassNew(virDomainEventClass, + "virDomainEventMetadataChange", + sizeof(virDomainEventMetadataChange), + virDomainEventMetadataChangeDispose))) + return -1; return 0; } @@ -573,6 +590,16 @@ virDomainEventJobCompletedDispose(void *obj) } +static void +virDomainEventMetadataChangeDispose(void *obj) +{ + virDomainEventMetadataChangePtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->nsuri); +} + + static void * virDomainEventNew(virClassPtr klass, int eventID, @@ -1600,6 +1627,53 @@ virDomainEventTunableNewFromDom(virDomainPtr dom, } +static virObjectEventPtr +virDomainEventMetadataChangeNew(int id, + const char *name, + unsigned char *uuid, + int type, + const char *nsuri) +{ + virDomainEventMetadataChangePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventMetadataChangeClass, + VIR_DOMAIN_EVENT_ID_METADATA_CHANGE, + id, name, uuid))) + return NULL; + + ev->type = type; + if (nsuri && VIR_STRDUP(ev->nsuri, nsuri) < 0) + goto error; + + return (virObjectEventPtr)ev; + + error: + virObjectUnref(ev); + return NULL; +} + +virObjectEventPtr +virDomainEventMetadataChangeNewFromObj(virDomainObjPtr obj, + int type, + const char *nsuri) +{ + return virDomainEventMetadataChangeNew(obj->def->id, obj->def->name, + obj->def->uuid, type, nsuri); +} + +virObjectEventPtr +virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, + int type, + const char *nsuri) +{ + return virDomainEventMetadataChangeNew(dom->id, dom->name, dom->uuid, + type, nsuri); +} + + static void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, @@ -1857,6 +1931,18 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_METADATA_CHANGE: + { + virDomainEventMetadataChangePtr metadataChangeEvent; + + metadataChangeEvent = (virDomainEventMetadataChangePtr)event; + ((virConnectDomainEventMetadataChangeCallback)cb)(conn, dom, + metadataChangeEvent->type, + metadataChangeEvent->nsuri, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_LAST: break; } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 54fa879..1933f47 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -234,6 +234,16 @@ virDomainEventJobCompletedNewFromDom(virDomainPtr dom, virTypedParameterPtr params, int nparams); +virObjectEventPtr +virDomainEventMetadataChangeNewFromObj(virDomainObjPtr obj, + int type, + const char *nsuri); + +virObjectEventPtr +virDomainEventMetadataChangeNewFromDom(virDomainPtr dom, + int type, + const char *nsuri); + int virDomainEventStateRegister(virConnectPtr conn, virObjectEventStatePtr state, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2d23e46..3b8d227 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -558,6 +558,8 @@ virDomainEventLifecycleNew; virDomainEventLifecycleNewFromDef; virDomainEventLifecycleNewFromDom; virDomainEventLifecycleNewFromObj; +virDomainEventMetadataChangeNewFromDom; +virDomainEventMetadataChangeNewFromObj; virDomainEventMigrationIterationNewFromDom; virDomainEventMigrationIterationNewFromObj; virDomainEventPMSuspendDiskNewFromDom; diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index a7bc9f0..04a4b8c 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -5379,6 +5379,12 @@ lxcDomainSetMetadata(virDomainPtr dom, driver->xmlopt, cfg->stateDir, cfg->configDir, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + virObjectEventStateQueue(driver->domainEventState, ev); + } + virLXCDomainObjEndJob(driver, vm); cleanup: diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b359e77..2c9ef95 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -18069,6 +18069,12 @@ qemuDomainSetMetadata(virDomainPtr dom, driver->xmlopt, cfg->stateDir, cfg->configDir, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(vm, type, uri); + qemuDomainEventQueue(driver, ev); + } + qemuDomainObjEndJob(driver, vm); cleanup: diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 46da06f..c161a56 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -355,6 +355,11 @@ remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog, void *evdata, void *opaque); static void +remoteDomainBuildEventCallbackMetadataChange(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); @@ -558,6 +563,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_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE, + remoteDomainBuildEventCallbackMetadataChange, + sizeof(remote_domain_event_callback_metadata_change_msg), + (xdrproc_t)xdr_remote_domain_event_callback_metadata_change_msg }, { REMOTE_PROC_STORAGE_POOL_EVENT_LIFECYCLE, remoteStoragePoolBuildEventLifecycle, sizeof(remote_storage_pool_event_lifecycle_msg), @@ -5121,7 +5130,7 @@ remoteDomainBuildEventCallbackDeviceRemovalFailed(virNetClientProgramPtr prog AT void *evdata, void *opaque) { virConnectPtr conn = opaque; - remote_domain_event_callback_device_added_msg *msg = evdata; + remote_domain_event_callback_device_removal_failed_msg *msg = evdata; struct private_data *priv = conn->privateData; virDomainPtr dom; virObjectEventPtr event = NULL; @@ -5249,6 +5258,28 @@ remoteDomainBuildEventCallbackJobCompleted(virNetClientProgramPtr prog ATTRIBUTE static void +remoteDomainBuildEventCallbackMetadataChange(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_metadata_change_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventMetadataChangeNewFromDom(dom, msg->type, msg->nsuri ? *msg->nsuri : NULL); + + virObjectUnref(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + + +static void remoteNetworkBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index b846ef2..f268e1c 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3353,6 +3353,13 @@ struct remote_domain_set_guest_vcpus_args { }; +struct remote_domain_event_callback_metadata_change_msg { + int callbackID; + remote_nonnull_domain dom; + int type; + remote_string nsuri; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -5952,5 +5959,11 @@ enum remote_procedure { * @priority: high * @acl: storage_vol:read */ - REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378 + REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index 41bc3bd..2fc9e46 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2800,6 +2800,12 @@ struct remote_domain_set_guest_vcpus_args { int state; u_int flags; }; +struct remote_domain_event_callback_metadata_change_msg { + int callbackID; + remote_nonnull_domain dom; + int type; + remote_string nsuri +}; enum remote_procedure { REMOTE_PROC_CONNECT_OPEN = 1, REMOTE_PROC_CONNECT_CLOSE = 2, @@ -3179,4 +3185,5 @@ enum remote_procedure { REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376, REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, REMOTE_PROC_STORAGE_VOL_GET_INFO_FLAGS = 378, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_METADATA_CHANGE = 379, }; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index de92a01..6820298 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -2721,6 +2721,12 @@ static int testDomainSetMetadata(virDomainPtr dom, privconn->caps, privconn->xmlopt, NULL, NULL, flags); + if (ret == 0) { + virObjectEventPtr ev = NULL; + ev = virDomainEventMetadataChangeNewFromObj(privdom, type, uri); + testObjectEventQueue(privconn, ev); + } + virDomainObjEndAPI(&privdom); return ret; } diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 3a6fa5c..f8f5d9c 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12750,6 +12750,29 @@ virshEventDeviceRemovalFailedPrint(virConnectPtr conn ATTRIBUTE_UNUSED, virshEventPrint(opaque, &buf); } +VIR_ENUM_DECL(virshEventMetadataChangeType) +VIR_ENUM_IMPL(virshEventMetadataChangeType, + VIR_DOMAIN_METADATA_LAST, + N_("description"), + N_("title"), + N_("element")) + +static void +virshEventMetadataChangePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int type, + const char *nsuri, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'metdata-change' for domain %s: %s %s\n"), + virDomainGetName(dom), + UNKNOWNSTR(virshEventMetadataChangeTypeTypeToString(type)), + NULLSTR(nsuri)); + virshEventPrint(opaque, &buf); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", @@ -12796,6 +12819,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, { "device-removal-failed", VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, + { "metadata-change", + VIR_DOMAIN_EVENT_CALLBACK(virshEventMetadataChangePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 2.9.3

On Thu, Jan 05, 2017 at 13:59:19 +0000, Daniel Berrange wrote:
When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
I remember that we rejected when oVirt wanted to have this so that libvirtd would not become a new messaging service for apps using libvirt. Also this patch does not really provide the justification you wanted on a separate occurence of a similar request: https://www.redhat.com/archives/libvir-list/2015-March/msg00286.html

On Thu, Jan 05, 2017 at 03:54:52PM +0100, Peter Krempa wrote:
On Thu, Jan 05, 2017 at 13:59:19 +0000, Daniel Berrange wrote:
When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
I remember that we rejected when oVirt wanted to have this so that libvirtd would not become a new messaging service for apps using libvirt.
Also this patch does not really provide the justification you wanted on a separate occurence of a similar request: https://www.redhat.com/archives/libvir-list/2015-March/msg00286.html
So we have a libvirt-console-proxy app which is able to expose guest VNC/SPICE/serial consoles over a websockets proxy. It watches domain lifecycle events to see when guests start/stop in order to detect what consoles should be exposed at any time. It reads the console port/address info from the <graphics> or <serial> XML, but there's also need for an authentication token. The details of the auth token are stored in a metadata blob whose format is defined by libvirt-console-proxy. Tokens are only valid for a finite lifetime though and so they may be added/deleted/changed while a guest is running. The mgmt application (oVirt/OpenStack/Kubernetes) which launches the virtual machine is responsible for setting the token info in the metadata blob - they have no direct connection/commnuication with the console proxy daemons which are running elsewhere. So the console proxy needs to be able to detect these changes, and currently it just has to poll on libirt querying the metdata over and over to see if it changes which is horribly inefficient. Being able to be notified of changes to metadata fields makes the process far more efficient. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On 01/05/2017 03:54 PM, Peter Krempa wrote:
On Thu, Jan 05, 2017 at 13:59:19 +0000, Daniel Berrange wrote:
When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> ---
I remember that we rejected when oVirt wanted to have this so that libvirtd would not become a new messaging service for apps using libvirt.
I think it's worth having an event whenever domain definition changes. Just like we want qemu folks to emit events instead of us having to poll all the time, our users want just the same. I haven't looked into the patches yet though, will do tomorrow. Michal

On 01/05/2017 02:59 PM, Daniel P. Berrange wrote:
When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 45 +++++++++++++++++++ examples/object-events/event-test.c | 35 +++++++++++++++ include/libvirt/libvirt-domain.h | 20 +++++++++ src/bhyve/bhyve_driver.c | 7 +++ src/conf/domain_event.c | 86 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 10 +++++ src/libvirt_private.syms | 2 + src/lxc/lxc_driver.c | 6 +++ src/qemu/qemu_driver.c | 6 +++ src/remote/remote_driver.c | 33 +++++++++++++- src/remote/remote_protocol.x | 15 ++++++- src/remote_protocol-structs | 7 +++ src/test/test_driver.c | 6 +++ tools/virsh-domain.c | 25 +++++++++++ 14 files changed, 301 insertions(+), 2 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 46da06f..c161a56 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c
@@ -5121,7 +5130,7 @@ remoteDomainBuildEventCallbackDeviceRemovalFailed(virNetClientProgramPtr prog AT void *evdata, void *opaque) { virConnectPtr conn = opaque; - remote_domain_event_callback_device_added_msg *msg = evdata; + remote_domain_event_callback_device_removal_failed_msg *msg = evdata; struct private_data *priv = conn->privateData; virDomainPtr dom; virObjectEventPtr event = NULL;
Some purists on the list might tell you to put this into a separate commit. I'm not one of them :-) Michal

On Fri, Jan 06, 2017 at 04:02:44PM +0100, Michal Privoznik wrote:
On 01/05/2017 02:59 PM, Daniel P. Berrange wrote:
When changing the metadata via virDomainSetMetadata, we now emit an event to notify the app of changes. This is useful when co-ordinating different applications read/write of custom metadata.
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 45 +++++++++++++++++++ examples/object-events/event-test.c | 35 +++++++++++++++ include/libvirt/libvirt-domain.h | 20 +++++++++ src/bhyve/bhyve_driver.c | 7 +++ src/conf/domain_event.c | 86 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 10 +++++ src/libvirt_private.syms | 2 + src/lxc/lxc_driver.c | 6 +++ src/qemu/qemu_driver.c | 6 +++ src/remote/remote_driver.c | 33 +++++++++++++- src/remote/remote_protocol.x | 15 ++++++- src/remote_protocol-structs | 7 +++ src/test/test_driver.c | 6 +++ tools/virsh-domain.c | 25 +++++++++++ 14 files changed, 301 insertions(+), 2 deletions(-)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 46da06f..c161a56 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c
@@ -5121,7 +5130,7 @@ remoteDomainBuildEventCallbackDeviceRemovalFailed(virNetClientProgramPtr prog AT void *evdata, void *opaque) { virConnectPtr conn = opaque; - remote_domain_event_callback_device_added_msg *msg = evdata; + remote_domain_event_callback_device_removal_failed_msg *msg = evdata; struct private_data *priv = conn->privateData; virDomainPtr dom; virObjectEventPtr event = NULL;
Some purists on the list might tell you to put this into a separate commit. I'm not one of them :-)
Actually I did mean todo that, but forgot Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

Add public APIs to allow applications to watch for define and undefine of secret objects. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- include/libvirt/libvirt-secret.h | 91 +++++++++++++++++++++++++++ src/datatypes.h | 14 +++++ src/driver-secret.h | 14 +++++ src/libvirt-secret.c | 129 +++++++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + 5 files changed, 250 insertions(+) diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-secret.h index 2ae36f6..1bbbf3f 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -110,5 +110,96 @@ int virSecretUndefine (virSecretPtr secret); int virSecretRef (virSecretPtr secret); int virSecretFree (virSecretPtr secret); +/** + * VIR_SECRET_EVENT_CALLBACK: + * + * Used to cast the event specific callback into the generic one + * for use for virConnectSecretEventRegisterAny() + */ +# define VIR_SECRET_EVENT_CALLBACK(cb)((virConnectSecretEventGenericCallback)(cb)) + +/** + * virSecretEventID: + * + * An enumeration of supported eventId parameters for + * virConnectSecretEventRegisterAny(). Each event id determines which + * signature of callback function will be used. + */ +typedef enum { + VIR_SECRET_EVENT_ID_LIFECYCLE = 0, /* virConnectSecretEventLifecycleCallback */ + +# ifdef VIR_ENUM_SENTINELS + VIR_SECRET_EVENT_ID_LAST + /* + * NB: this enum value will increase over time as new events are + * added to the libvirt API. It reflects the last event ID supported + * by this version of the libvirt API. + */ +# endif +} virSecretEventID; + +/** + * virConnectSecretEventGenericCallback: + * @conn: the connection pointer + * @secret: the secret pointer + * @opaque: application specified data + * + * A generic secret event callback handler, for use with + * virConnectSecretEventRegisterAny(). Specific events usually + * have a customization with extra parameters, often with @opaque being + * passed in a different parameter position; use + * VIR_SECRET_EVENT_CALLBACK() when registering an appropriate handler. + */ +typedef void (*virConnectSecretEventGenericCallback)(virConnectPtr conn, + virSecretPtr secret, + void *opaque); + +/* Use VIR_SECRET_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectSecretEventRegisterAny(virConnectPtr conn, + virSecretPtr secret, /* optional, to filter */ + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectSecretEventDeregisterAny(virConnectPtr conn, + int callbackID); + +/** + * virSecretEventLifecycleType: + * + * a virSecretEventLifecycleType is emitted during secret + * lifecycle events + */ +typedef enum { + VIR_SECRET_EVENT_DEFINED = 0, + VIR_SECRET_EVENT_UNDEFINED = 1, + +# ifdef VIR_ENUM_SENTINELS + VIR_SECRET_EVENT_LAST +# endif +} virSecretEventLifecycleType; + +/** + * virConnectSecretEventLifecycleCallback: + * @conn: connection object + * @secret: secret on which the event occurred + * @event: The specific virSecretEventLifeCycleType which occurred + * @detail: contains some details on the reason of the event. + * @opaque: application specified data + * + * This callback is called when a secret lifecycle action is performed, + * like added or removed. + * + * The callback signature to use when registering for an event of type + * VIR_SECRET_EVENT_ID_LIFECYCLE with + * virConnectSecretEventRegisterAny() + */ +typedef void (*virConnectSecretEventLifecycleCallback)(virConnectPtr conn, + virSecretPtr secret, + int event, + int detail, + void *opaque); + #endif /* __VIR_LIBVIRT_SECRET_H__ */ diff --git a/src/datatypes.h b/src/datatypes.h index 9a5fbbc..5830482 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -223,6 +223,20 @@ extern virClassPtr virAdmClientClass; } \ } while (0) +# define virCheckSecretGoto(obj, label) \ + do { \ + virSecretPtr _secret = (obj); \ + if (!virObjectIsClass(_secret, virSecretClass) || \ + !virObjectIsClass(_secret->conn, virConnectClass)) { \ + virReportErrorHelper(VIR_FROM_SECRET, \ + VIR_ERR_INVALID_SECRET, \ + __FILE__, __FUNCTION__, __LINE__, \ + __FUNCTION__); \ + virDispatchError(NULL); \ + goto label; \ + } \ + } while (0) + # define virCheckStreamReturn(obj, retval) \ do { \ virStreamPtr _st = (obj); \ diff --git a/src/driver-secret.h b/src/driver-secret.h index c39e0d7..3cd9034 100644 --- a/src/driver-secret.h +++ b/src/driver-secret.h @@ -77,6 +77,18 @@ typedef int virSecretPtr **secrets, unsigned int flags); +typedef int +(*virDrvConnectSecretEventRegisterAny)(virConnectPtr conn, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int +(*virDrvConnectSecretEventDeregisterAny)(virConnectPtr conn, + int callbackID); + typedef struct _virSecretDriver virSecretDriver; typedef virSecretDriver *virSecretDriverPtr; @@ -98,6 +110,8 @@ struct _virSecretDriver { virDrvSecretSetValue secretSetValue; virDrvSecretGetValue secretGetValue; virDrvSecretUndefine secretUndefine; + virDrvConnectSecretEventRegisterAny connectSecretEventRegisterAny; + virDrvConnectSecretEventDeregisterAny connectSecretEventDeregisterAny; }; diff --git a/src/libvirt-secret.c b/src/libvirt-secret.c index db42aec..8a99c8c 100644 --- a/src/libvirt-secret.c +++ b/src/libvirt-secret.c @@ -693,3 +693,132 @@ virSecretFree(virSecretPtr secret) virObjectUnref(secret); return 0; } + + +/** + * virConnectSecretEventRegisterAny: + * @conn: pointer to the connection + * @secret: pointer to the secret + * @eventID: the event type to receive + * @cb: callback to the function handling secret events + * @opaque: opaque data to pass on to the callback + * @freecb: optional function to deallocate opaque when not used anymore + * + * Adds a callback to receive notifications of arbitrary secret events + * occurring on a secret. This function requires that an event loop + * has been previously registered with virEventRegisterImpl() or + * virEventRegisterDefaultImpl(). + * + * If @secret is NULL, then events will be monitored for any secret. + * If @secret is non-NULL, then only the specific secret will be monitored. + * + * Most types of events have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus necessary to use + * the VIR_SECRET_EVENT_CALLBACK() macro to cast the + * supplied function pointer to match the signature of this method. + * + * The virSecretPtr object handle passed into the callback upon delivery + * of an event is only valid for the duration of execution of the callback. + * If the callback wishes to keep the secret object after the callback + * returns, it shall take a reference to it, by calling virSecretRef(). + * The reference can be released once the object is no longer required + * by calling virSecretFree(). + * + * The return value from this method is a positive integer identifier + * for the callback. To unregister a callback, this callback ID should + * be passed to the virConnectSecretEventDeregisterAny() method. + * + * Returns a callback identifier on success, -1 on failure. + */ +int +virConnectSecretEventRegisterAny(virConnectPtr conn, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p, secret=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p", + conn, secret, eventID, cb, opaque, freecb); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + if (secret) { + virCheckSecretGoto(secret, error); + if (secret->conn != conn) { + char uuidstr[VIR_UUID_STRING_BUFLEN]; + virUUIDFormat(secret->uuid, uuidstr); + virReportInvalidArg(secret, + _("secret '%s' in %s must match connection"), + uuidstr, __FUNCTION__); + goto error; + } + } + virCheckNonNullArgGoto(cb, error); + virCheckNonNegativeArgGoto(eventID, error); + + if (eventID >= VIR_SECRET_EVENT_ID_LAST) { + virReportInvalidArg(eventID, + _("eventID in %s must be less than %d"), + __FUNCTION__, VIR_SECRET_EVENT_ID_LAST); + goto error; + } + + if (conn->secretDriver && + conn->secretDriver->connectSecretEventRegisterAny) { + int ret; + ret = conn->secretDriver->connectSecretEventRegisterAny(conn, + secret, + eventID, + cb, + opaque, + freecb); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} + + +/** + * virConnectSecretEventDeregisterAny: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * value obtained from a previous virConnectSecretEventRegisterAny() method. + * + * Returns 0 on success, -1 on failure. + */ +int +virConnectSecretEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + VIR_DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + virCheckConnectReturn(conn, -1); + virCheckNonNegativeArgGoto(callbackID, error); + + if (conn->secretDriver && + conn->secretDriver->connectSecretEventDeregisterAny) { + int ret; + ret = conn->secretDriver->connectSecretEventDeregisterAny(conn, + callbackID); + if (ret < 0) + goto error; + return ret; + } + + virReportUnsupportedError(); + error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 12ef085..62885ac 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -749,6 +749,8 @@ LIBVIRT_2.2.0 { LIBVIRT_3.0.0 { global: virStorageVolGetInfoFlags; + virConnectSecretEventRegisterAny; + virConnectSecretEventDeregisterAny; } LIBVIRT_2.2.0; # .... define new API here using predicted next version number .... -- 2.9.3

Add helper APIs / objects for managing secret events Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/Makefile.am | 5 + src/conf/secret_event.c | 252 +++++++++++++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 61 ++++++++++++ src/libvirt_private.syms | 5 + 4 files changed, 323 insertions(+) create mode 100644 src/conf/secret_event.c create mode 100644 src/conf/secret_event.h diff --git a/src/Makefile.am b/src/Makefile.am index 0dd2faa..ae5562b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -354,6 +354,9 @@ STORAGE_EVENT_SOURCES = \ NODE_DEVICE_EVENT_SOURCES = \ conf/node_device_event.c conf/node_device_event.h +SECRET_EVENT_SOURCES = \ + conf/secret_event.c conf/secret_event.h + # Network driver generic impl APIs NETWORK_CONF_SOURCES = \ conf/network_conf.c conf/network_conf.h \ @@ -406,6 +409,7 @@ CONF_SOURCES = \ $(NETWORK_EVENT_SOURCES) \ $(STORAGE_EVENT_SOURCES) \ $(NODE_DEVICE_EVENT_SOURCES) \ + $(SECRET_EVENT_SOURCES) \ $(NETWORK_CONF_SOURCES) \ $(NWFILTER_CONF_SOURCES) \ $(NODE_DEVICE_CONF_SOURCES) \ @@ -2390,6 +2394,7 @@ libvirt_setuid_rpc_client_la_SOURCES = \ conf/object_event.c \ conf/storage_event.c \ conf/node_device_event.c \ + conf/secret_event.c \ rpc/virnetsocket.c \ rpc/virnetsocket.h \ rpc/virnetmessage.h \ diff --git a/src/conf/secret_event.c b/src/conf/secret_event.c new file mode 100644 index 0000000..9d578f0 --- /dev/null +++ b/src/conf/secret_event.c @@ -0,0 +1,252 @@ +/* + * secret_event.c: node device event queue processing helpers + * + * Copyright (C) 2010-2014 Red Hat, Inc. + * Copyright (C) 2008 VirtualIron + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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 "secret_event.h" +#include "object_event.h" +#include "object_event_private.h" +#include "datatypes.h" +#include "virlog.h" + +VIR_LOG_INIT("conf.secret_event"); + +struct _virSecretEvent { + virObjectEvent parent; + + /* Unused attribute to allow for subclass creation */ + bool dummy; +}; +typedef struct _virSecretEvent virSecretEvent; +typedef virSecretEvent *virSecretEventPtr; + +struct _virSecretEventLifecycle { + virSecretEvent parent; + + int type; + int detail; +}; +typedef struct _virSecretEventLifecycle virSecretEventLifecycle; +typedef virSecretEventLifecycle *virSecretEventLifecyclePtr; + +static virClassPtr virSecretEventClass; +static virClassPtr virSecretEventLifecycleClass; +static void virSecretEventDispose(void *obj); +static void virSecretEventLifecycleDispose(void *obj); + +static int +virSecretEventsOnceInit(void) +{ + if (!(virSecretEventClass = + virClassNew(virClassForObjectEvent(), + "virSecretEvent", + sizeof(virSecretEvent), + virSecretEventDispose))) + return -1; + if (!(virSecretEventLifecycleClass = + virClassNew(virSecretEventClass, + "virSecretEventLifecycle", + sizeof(virSecretEventLifecycle), + virSecretEventLifecycleDispose))) + return -1; + return 0; +} + +VIR_ONCE_GLOBAL_INIT(virSecretEvents) + +static void +virSecretEventDispose(void *obj) +{ + virSecretEventPtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void +virSecretEventLifecycleDispose(void *obj) +{ + virSecretEventLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void +virSecretEventDispatchDefaultFunc(virConnectPtr conn, + virObjectEventPtr event, + virConnectObjectEventGenericCallback cb, + void *cbopaque) +{ + virSecretPtr secret = virGetSecret(conn, + event->meta.uuid, + event->meta.id, + event->meta.name); + + if (!secret) + return; + + switch ((virSecretEventID)event->eventID) { + case VIR_SECRET_EVENT_ID_LIFECYCLE: + { + virSecretEventLifecyclePtr secretLifecycleEvent; + + secretLifecycleEvent = (virSecretEventLifecyclePtr)event; + ((virConnectSecretEventLifecycleCallback)cb)(conn, secret, + secretLifecycleEvent->type, + secretLifecycleEvent->detail, + cbopaque); + goto cleanup; + } + + case VIR_SECRET_EVENT_ID_LAST: + break; + } + VIR_WARN("Unexpected event ID %d", event->eventID); + + cleanup: + virObjectUnref(secret); +} + + +/** + * virSecretEventStateRegisterID: + * @conn: connection to associate with callback + * @state: object event state + * @secret: secret to filter on or NULL for all node secrets + * @eventID: ID of the event type to register for + * @cb: function to invoke when event occurs + * @opaque: data blob to pass to @callback + * @freecb: callback to free @opaque + * @callbackID: filled with callback ID + * + * Register the function @cb with connection @conn, from @state, for + * events of type @eventID, and return the registration handle in + * @callbackID. + * + * Returns: the number of callbacks now registered, or -1 on error + */ +int +virSecretEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virSecretEventsInitialize() < 0) + return -1; + + if (secret) + virUUIDFormat(secret->uuid, uuidstr); + return virObjectEventStateRegisterID(conn, state, secret ? uuidstr : NULL, + NULL, NULL, + virSecretEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, + false, callbackID, false); +} + + +/** + * virSecretEventStateRegisterClient: + * @conn: connection to associate with callback + * @state: object event state + * @secret: secret to filter on or NULL for all node secrets + * @eventID: ID of the event type to register for + * @cb: function to invoke when event occurs + * @opaque: data blob to pass to @callback + * @freecb: callback to free @opaque + * @callbackID: filled with callback ID + * + * Register the function @cb with connection @conn, from @state, for + * events of type @eventID, and return the registration handle in + * @callbackID. This version is intended for use on the client side + * of RPC. + * + * Returns: the number of callbacks now registered, or -1 on error + */ +int +virSecretEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) +{ + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virSecretEventsInitialize() < 0) + return -1; + + if (secret) + virUUIDFormat(secret->uuid, uuidstr); + + return virObjectEventStateRegisterID(conn, state, secret ? uuidstr : NULL, + NULL, NULL, + virSecretEventClass, eventID, + VIR_OBJECT_EVENT_CALLBACK(cb), + opaque, freecb, + false, callbackID, true); +} + + +/** + * virSecretEventLifecycleNew: + * @uuid: UUID of the secret object the event describes + * @usage_type: type of usage for the secret + * @usage_id: usage specific identifier for the secret + * @type: type of lifecycle event + * @detail: more details about @type + * + * Create a new secret lifecycle event. + */ +virObjectEventPtr +virSecretEventLifecycleNew(const unsigned char *uuid, + int usage_type, + const char *usage_id, + int type, + int detail) +{ + virSecretEventLifecyclePtr event; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virSecretEventsInitialize() < 0) + return NULL; + + virUUIDFormat(uuid, uuidstr); + VIR_DEBUG("Event %s %d %s %d %d", uuidstr, usage_type, usage_id, type, detail); + if (!(event = virObjectEventNew(virSecretEventLifecycleClass, + virSecretEventDispatchDefaultFunc, + VIR_SECRET_EVENT_ID_LIFECYCLE, + usage_type, usage_id, uuid, uuidstr))) + return NULL; + + event->type = type; + event->detail = detail; + + return (virObjectEventPtr)event; +} diff --git a/src/conf/secret_event.h b/src/conf/secret_event.h new file mode 100644 index 0000000..4649d94 --- /dev/null +++ b/src/conf/secret_event.h @@ -0,0 +1,61 @@ +/* + * secret_event.h: secret event queue processing helpers + * + * Copyright (C) 2010-2014 Red Hat, Inc. + * Copyright (C) 2008 VirtualIron + * Copyright (C) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany. + * + * 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 "internal.h" +#include "object_event.h" +#include "object_event_private.h" + +#ifndef __SECRET_EVENT_H__ +# define __SECRET_EVENT_H__ + +int +virSecretEventStateRegisterID(virConnectPtr conn, + virObjectEventStatePtr state, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); + +int +virSecretEventStateRegisterClient(virConnectPtr conn, + virObjectEventStatePtr state, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback cb, + void *opaque, + virFreeCallback freecb, + int *callbackID) + ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(5) + ATTRIBUTE_NONNULL(8); + +virObjectEventPtr +virSecretEventLifecycleNew(const unsigned char *uuid, + int usage_type, + const char *usage_id, + int type, + int detail); + +#endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3b8d227..bb84488 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -840,6 +840,11 @@ virSecretUsageTypeFromString; virSecretUsageTypeToString; +# conf/secret_event.h +virSecretEventLifecycleNew; +virSecretEventStateRegisterID; + + # conf/snapshot_conf.h virDomainListSnapshots; virDomainSnapshotAlignDisks; -- 2.9.3

The public virSecret object has a single "usage_id" field but the virSecretDef object has a different 'char *' field for each usage type, but the code all assumes every usage type has a corresponding single string. Get rid of the pointless union in virSecretDef and just use "usage_id" everywhere. This doesn't impact public XML format, only the internal handling. Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/access/viraccessdriverpolkit.c | 8 ++--- src/conf/secret_conf.c | 74 +++++++------------------------------- src/conf/secret_conf.h | 9 +---- src/conf/virsecretobj.c | 42 +++++----------------- src/datatypes.c | 3 +- src/libvirt_private.syms | 1 - src/secret/secret_driver.c | 6 ++-- src/storage/storage_backend.c | 2 +- 8 files changed, 31 insertions(+), 114 deletions(-) diff --git a/src/access/viraccessdriverpolkit.c b/src/access/viraccessdriverpolkit.c index 0d9e0a1..246af2f 100644 --- a/src/access/viraccessdriverpolkit.c +++ b/src/access/viraccessdriverpolkit.c @@ -303,7 +303,7 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, const char *attrs[] = { "connect_driver", driverName, "secret_uuid", uuidstr, - "secret_usage_volume", secret->usage.volume, + "secret_usage_volume", secret->usage_id, NULL, }; @@ -316,7 +316,7 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, const char *attrs[] = { "connect_driver", driverName, "secret_uuid", uuidstr, - "secret_usage_ceph", secret->usage.ceph, + "secret_usage_ceph", secret->usage_id, NULL, }; @@ -329,7 +329,7 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, const char *attrs[] = { "connect_driver", driverName, "secret_uuid", uuidstr, - "secret_usage_target", secret->usage.target, + "secret_usage_target", secret->usage_id, NULL, }; @@ -342,7 +342,7 @@ virAccessDriverPolkitCheckSecret(virAccessManagerPtr manager, const char *attrs[] = { "connect_driver", driverName, "secret_uuid", uuidstr, - "secret_usage_name", secret->usage.name, + "secret_usage_name", secret->usage_id, NULL, }; diff --git a/src/conf/secret_conf.c b/src/conf/secret_conf.c index e662455..985bae4 100644 --- a/src/conf/secret_conf.c +++ b/src/conf/secret_conf.c @@ -41,31 +41,6 @@ VIR_LOG_INIT("conf.secret_conf"); VIR_ENUM_IMPL(virSecretUsage, VIR_SECRET_USAGE_TYPE_LAST, "none", "volume", "ceph", "iscsi", "tls") -const char * -virSecretUsageIDForDef(virSecretDefPtr def) -{ - switch (def->usage_type) { - case VIR_SECRET_USAGE_TYPE_NONE: - return ""; - - case VIR_SECRET_USAGE_TYPE_VOLUME: - return def->usage.volume; - - case VIR_SECRET_USAGE_TYPE_CEPH: - return def->usage.ceph; - - case VIR_SECRET_USAGE_TYPE_ISCSI: - return def->usage.target; - - case VIR_SECRET_USAGE_TYPE_TLS: - return def->usage.name; - - default: - return NULL; - } -} - - void virSecretDefFree(virSecretDefPtr def) { @@ -73,30 +48,7 @@ virSecretDefFree(virSecretDefPtr def) return; VIR_FREE(def->description); - switch (def->usage_type) { - case VIR_SECRET_USAGE_TYPE_NONE: - break; - - case VIR_SECRET_USAGE_TYPE_VOLUME: - VIR_FREE(def->usage.volume); - break; - - case VIR_SECRET_USAGE_TYPE_CEPH: - VIR_FREE(def->usage.ceph); - break; - - case VIR_SECRET_USAGE_TYPE_ISCSI: - VIR_FREE(def->usage.target); - break; - - case VIR_SECRET_USAGE_TYPE_TLS: - VIR_FREE(def->usage.name); - break; - - default: - VIR_ERROR(_("unexpected secret usage type %d"), def->usage_type); - break; - } + VIR_FREE(def->usage_id); VIR_FREE(def); } @@ -127,8 +79,8 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, break; case VIR_SECRET_USAGE_TYPE_VOLUME: - def->usage.volume = virXPathString("string(./usage/volume)", ctxt); - if (!def->usage.volume) { + def->usage_id = virXPathString("string(./usage/volume)", ctxt); + if (!def->usage_id) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("volume usage specified, but volume path is missing")); return -1; @@ -136,8 +88,8 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, break; case VIR_SECRET_USAGE_TYPE_CEPH: - def->usage.ceph = virXPathString("string(./usage/name)", ctxt); - if (!def->usage.ceph) { + def->usage_id = virXPathString("string(./usage/name)", ctxt); + if (!def->usage_id) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Ceph usage specified, but name is missing")); return -1; @@ -145,8 +97,8 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, break; case VIR_SECRET_USAGE_TYPE_ISCSI: - def->usage.target = virXPathString("string(./usage/target)", ctxt); - if (!def->usage.target) { + def->usage_id = virXPathString("string(./usage/target)", ctxt); + if (!def->usage_id) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("iSCSI usage specified, but target is missing")); return -1; @@ -154,8 +106,8 @@ virSecretDefParseUsage(xmlXPathContextPtr ctxt, break; case VIR_SECRET_USAGE_TYPE_TLS: - def->usage.name = virXPathString("string(./usage/name)", ctxt); - if (!def->usage.name) { + def->usage_id = virXPathString("string(./usage/name)", ctxt); + if (!def->usage_id) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("TLS usage specified, but name is missing")); return -1; @@ -303,19 +255,19 @@ virSecretDefFormatUsage(virBufferPtr buf, break; case VIR_SECRET_USAGE_TYPE_VOLUME: - virBufferEscapeString(buf, "<volume>%s</volume>\n", def->usage.volume); + virBufferEscapeString(buf, "<volume>%s</volume>\n", def->usage_id); break; case VIR_SECRET_USAGE_TYPE_CEPH: - virBufferEscapeString(buf, "<name>%s</name>\n", def->usage.ceph); + virBufferEscapeString(buf, "<name>%s</name>\n", def->usage_id); break; case VIR_SECRET_USAGE_TYPE_ISCSI: - virBufferEscapeString(buf, "<target>%s</target>\n", def->usage.target); + virBufferEscapeString(buf, "<target>%s</target>\n", def->usage_id); break; case VIR_SECRET_USAGE_TYPE_TLS: - virBufferEscapeString(buf, "<name>%s</name>\n", def->usage.name); + virBufferEscapeString(buf, "<name>%s</name>\n", def->usage_id); break; default: diff --git a/src/conf/secret_conf.h b/src/conf/secret_conf.h index c34880f..e0d9465 100644 --- a/src/conf/secret_conf.h +++ b/src/conf/secret_conf.h @@ -36,16 +36,9 @@ struct _virSecretDef { unsigned char uuid[VIR_UUID_BUFLEN]; char *description; /* May be NULL */ int usage_type; /* virSecretUsageType */ - union { - char *volume; /* May be NULL */ - char *ceph; - char *target; - char *name; - } usage; + char *usage_id; /* May be NULL */ }; -const char *virSecretUsageIDForDef(virSecretDefPtr def); - void virSecretDefFree(virSecretDefPtr def); virSecretDefPtr virSecretDefParseString(const char *xml); virSecretDefPtr virSecretDefParseFile(const char *filename); diff --git a/src/conf/virsecretobj.c b/src/conf/virsecretobj.c index 1351f18..049cab3 100644 --- a/src/conf/virsecretobj.c +++ b/src/conf/virsecretobj.c @@ -218,31 +218,9 @@ virSecretObjSearchName(const void *payload, if (secret->def->usage_type != data->usageType) goto cleanup; - switch (data->usageType) { - case VIR_SECRET_USAGE_TYPE_NONE: - /* never match this */ - break; - - case VIR_SECRET_USAGE_TYPE_VOLUME: - if (STREQ(secret->def->usage.volume, data->usageID)) - found = 1; - break; - - case VIR_SECRET_USAGE_TYPE_CEPH: - if (STREQ(secret->def->usage.ceph, data->usageID)) - found = 1; - break; - - case VIR_SECRET_USAGE_TYPE_ISCSI: - if (STREQ(secret->def->usage.target, data->usageID)) - found = 1; - break; - - case VIR_SECRET_USAGE_TYPE_TLS: - if (STREQ(secret->def->usage.name, data->usageID)) - found = 1; - break; - } + if (data->usageType != VIR_SECRET_USAGE_TYPE_NONE && + STREQ(secret->def->usage_id, data->usageID)) + found = 1; cleanup: virObjectUnlock(secret); @@ -352,7 +330,6 @@ virSecretObjListAddLocked(virSecretObjListPtr secrets, { virSecretObjPtr secret; virSecretObjPtr ret = NULL; - const char *newUsageID = virSecretUsageIDForDef(def); char uuidstr[VIR_UUID_STRING_BUFLEN]; char *configFile = NULL, *base64File = NULL; @@ -361,17 +338,14 @@ virSecretObjListAddLocked(virSecretObjListPtr secrets, /* Is there a secret already matching this UUID */ if ((secret = virSecretObjListFindByUUIDLocked(secrets, def->uuid))) { - const char *oldUsageID; - virObjectLock(secret); - oldUsageID = virSecretUsageIDForDef(secret->def); - if (STRNEQ(oldUsageID, newUsageID)) { + if (STRNEQ_NULLABLE(secret->def->usage_id, def->usage_id)) { virUUIDFormat(secret->def->uuid, uuidstr); virReportError(VIR_ERR_INTERNAL_ERROR, _("a secret with UUID %s is already defined for " "use with %s"), - uuidstr, oldUsageID); + uuidstr, secret->def->usage_id); goto cleanup; } @@ -391,13 +365,13 @@ virSecretObjListAddLocked(virSecretObjListPtr secrets, * try look for matching usage instead */ if ((secret = virSecretObjListFindByUsageLocked(secrets, def->usage_type, - newUsageID))) { + def->usage_id))) { virObjectLock(secret); virUUIDFormat(secret->def->uuid, uuidstr); virReportError(VIR_ERR_INTERNAL_ERROR, _("a secret with UUID %s already defined for " "use with %s"), - uuidstr, newUsageID); + uuidstr, def->usage_id); goto cleanup; } @@ -577,7 +551,7 @@ virSecretObjListPopulate(void *payload, if (!(secret = virGetSecret(data->conn, obj->def->uuid, obj->def->usage_type, - virSecretUsageIDForDef(obj->def)))) { + obj->def->usage_id))) { data->error = true; goto cleanup; } diff --git a/src/datatypes.c b/src/datatypes.c index c8a46b0..fe8b9c2 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -678,14 +678,13 @@ virGetSecret(virConnectPtr conn, const unsigned char *uuid, virCheckConnectGoto(conn, error); virCheckNonNullArgGoto(uuid, error); - virCheckNonNullArgGoto(usageID, error); if (!(ret = virObjectNew(virSecretClass))) return NULL; memcpy(&(ret->uuid[0]), uuid, VIR_UUID_BUFLEN); ret->usageType = usageType; - if (VIR_STRDUP(ret->usageID, usageID) < 0) + if (VIR_STRDUP(ret->usageID, usageID ? usageID : "") < 0) goto error; ret->conn = virObjectRef(conn); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb84488..2c78ed6 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -835,7 +835,6 @@ virSecretDefFormat; virSecretDefFree; virSecretDefParseFile; virSecretDefParseString; -virSecretUsageIDForDef; virSecretUsageTypeFromString; virSecretUsageTypeToString; diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index b5b0bea..70e4dd7 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -170,7 +170,7 @@ secretLookupByUUID(virConnectPtr conn, ret = virGetSecret(conn, def->uuid, def->usage_type, - virSecretUsageIDForDef(def)); + def->usage_id); cleanup: virSecretObjEndAPI(&secret); @@ -201,7 +201,7 @@ secretLookupByUsage(virConnectPtr conn, ret = virGetSecret(conn, def->uuid, def->usage_type, - virSecretUsageIDForDef(def)); + def->usage_id); cleanup: virSecretObjEndAPI(&secret); @@ -259,7 +259,7 @@ secretDefineXML(virConnectPtr conn, ret = virGetSecret(conn, new_attrs->uuid, new_attrs->usage_type, - virSecretUsageIDForDef(new_attrs)); + new_attrs->usage_id); new_attrs = NULL; goto cleanup; diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c index f2fc038..116eb80 100644 --- a/src/storage/storage_backend.c +++ b/src/storage/storage_backend.c @@ -630,7 +630,7 @@ virStorageGenerateQcowEncryption(virConnectPtr conn, goto cleanup; def->usage_type = VIR_SECRET_USAGE_TYPE_VOLUME; - if (VIR_STRDUP(def->usage.volume, vol->target.path) < 0) + if (VIR_STRDUP(def->usage_id, vol->target.path) < 0) goto cleanup; xml = virSecretDefFormat(def); virSecretDefFree(def); -- 2.9.3

Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/libvirtd.h | 2 + daemon/remote.c | 202 +++++++++++++++++++++++++++++++++++++++++++ src/remote/remote_driver.c | 133 +++++++++++++++++++++++++++- src/remote/remote_protocol.x | 45 +++++++++- src/remote_protocol-structs | 19 ++++ 5 files changed, 399 insertions(+), 2 deletions(-) diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index 09d505d..b570746 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 3d837d8..3a7ee9e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -236,6 +236,34 @@ remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client, } 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) { @@ -5938,6 +6019,127 @@ remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server ATTRIBU } 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, virNetMessagePtr msg ATTRIBUTE_UNUSED, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index c161a56..e689316 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" @@ -385,6 +386,11 @@ remoteNodeDeviceBuildEventUpdate(virNetClientProgramPtr prog 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, void *evdata, void *opaque); @@ -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 @@ -3315,6 +3325,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, const char *event, @@ -5388,6 +5495,28 @@ remoteNodeDeviceBuildEventUpdate(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, } 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, void *evdata, void *opaque) @@ -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 f268e1c..ce4c4ef 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 <virterror.h> 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 2fc9e46..0e8291a 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, }; -- 2.9.3

Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- src/secret/secret_driver.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 70e4dd7..0e3d97e 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -43,6 +43,7 @@ #include "configmake.h" #include "virstring.h" #include "viraccessapicheck.h" +#include "secret_event.h" #define VIR_FROM_THIS VIR_FROM_SECRET @@ -58,6 +59,9 @@ struct _virSecretDriverState { virMutex lock; virSecretObjListPtr secrets; char *configDir; + + /* Immutable pointer, self-locking APIs */ + virObjectEventStatePtr secretEventState; }; static virSecretDriverStatePtr driver; @@ -218,6 +222,7 @@ secretDefineXML(virConnectPtr conn, virSecretObjPtr secret = NULL; virSecretDefPtr backup = NULL; virSecretDefPtr new_attrs; + virObjectEventPtr event = NULL; virCheckFlags(0, NULL); @@ -256,6 +261,12 @@ secretDefineXML(virConnectPtr conn, /* Saved successfully - drop old values */ virSecretDefFree(backup); + event = virSecretEventLifecycleNew(new_attrs->uuid, + new_attrs->usage_type, + new_attrs->usage_id, + VIR_SECRET_EVENT_DEFINED, + 0); + ret = virGetSecret(conn, new_attrs->uuid, new_attrs->usage_type, @@ -276,6 +287,8 @@ secretDefineXML(virConnectPtr conn, cleanup: virSecretDefFree(new_attrs); virSecretObjEndAPI(&secret); + if (event) + virObjectEventStateQueue(driver->secretEventState, event); return ret; } @@ -381,6 +394,7 @@ secretUndefine(virSecretPtr obj) int ret = -1; virSecretObjPtr secret; virSecretDefPtr def; + virObjectEventPtr event = NULL; if (!(secret = secretObjFromSecret(obj))) goto cleanup; @@ -392,6 +406,12 @@ secretUndefine(virSecretPtr obj) if (virSecretObjDeleteConfig(secret) < 0) goto cleanup; + event = virSecretEventLifecycleNew(def->uuid, + def->usage_type, + def->usage_id, + VIR_SECRET_EVENT_UNDEFINED, + 0); + virSecretObjDeleteData(secret); virSecretObjListRemove(driver->secrets, secret); @@ -400,6 +420,8 @@ secretUndefine(virSecretPtr obj) cleanup: virSecretObjEndAPI(&secret); + if (event) + virObjectEventStateQueue(driver->secretEventState, event); return ret; } @@ -415,6 +437,8 @@ secretStateCleanup(void) virObjectUnref(driver->secrets); VIR_FREE(driver->configDir); + virObjectUnref(driver->secretEventState); + secretDriverUnlock(); virMutexDestroy(&driver->lock); VIR_FREE(driver); @@ -438,6 +462,8 @@ secretStateInitialize(bool privileged, } secretDriverLock(); + driver->secretEventState = virObjectEventStateNew(); + if (privileged) { if (VIR_STRDUP(base, SYSCONFDIR "/libvirt") < 0) goto error; @@ -479,6 +505,48 @@ secretStateReload(void) return 0; } +static int +secretConnectSecretEventRegisterAny(virConnectPtr conn, + virSecretPtr secret, + int eventID, + virConnectSecretEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int callbackID = -1; + + if (virConnectSecretEventRegisterAnyEnsureACL(conn) < 0) + goto cleanup; + + if (virSecretEventStateRegisterID(conn, driver->secretEventState, + secret, eventID, callback, + opaque, freecb, &callbackID) < 0) + callbackID = -1; + cleanup: + return callbackID; +} + +static int +secretConnectSecretEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + int ret = -1; + + if (virConnectSecretEventDeregisterAnyEnsureACL(conn) < 0) + goto cleanup; + + if (virObjectEventStateDeregisterID(conn, + driver->secretEventState, + callbackID) < 0) + goto cleanup; + + ret = 0; + + cleanup: + return ret; +} + + static virSecretDriver secretDriver = { .name = "secret", .connectNumOfSecrets = secretConnectNumOfSecrets, /* 0.7.1 */ @@ -491,6 +559,8 @@ static virSecretDriver secretDriver = { .secretSetValue = secretSetValue, /* 0.7.1 */ .secretGetValue = secretGetValue, /* 0.7.1 */ .secretUndefine = secretUndefine, /* 0.7.1 */ + .connectSecretEventRegisterAny = secretConnectSecretEventRegisterAny, /* 3.0.0 */ + .connectSecretEventDeregisterAny = secretConnectSecretEventDeregisterAny, /* 3.0.0 */ }; static virStateDriver stateDriver = { -- 2.9.3

Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- examples/object-events/event-test.c | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/examples/object-events/event-test.c b/examples/object-events/event-test.c index f2316d7..cf8c1bc 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -370,6 +370,24 @@ nodeDeviceEventToString(int event) } +static const char * +secretEventToString(int event) +{ + switch ((virSecretEventLifecycleType) event) { + case VIR_SECRET_EVENT_DEFINED: + return "Defined"; + + case VIR_SECRET_EVENT_UNDEFINED: + return "Undefined"; + + case VIR_SECRET_EVENT_LAST: + break; + } + + return "unknown"; +} + + static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, @@ -728,6 +746,23 @@ myNodeDeviceEventUpdateCallback(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int +mySecretEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + int event, + int detail, + void *opaque ATTRIBUTE_UNUSED) +{ + char uuid[VIR_UUID_STRING_BUFLEN]; + virSecretGetUUIDString(secret, uuid); + printf("%s EVENT: Secret %s %s %d\n", __func__, + uuid, + secretEventToString(event), + detail); + return 0; +} + + static void eventTypedParamsPrint(virTypedParameterPtr params, int nparams) @@ -1038,10 +1073,25 @@ struct nodeDeviceEventData nodeDeviceEvents[] = { NODE_DEVICE_EVENT(VIR_NODE_DEVICE_EVENT_ID_UPDATE, myNodeDeviceEventUpdateCallback), }; +struct secretEventData { + int event; + int id; + virConnectSecretEventGenericCallback cb; + const char *name; +}; + +#define SECRET_EVENT(event, callback) \ + {event, -1, VIR_SECRET_EVENT_CALLBACK(callback), #event} + +struct secretEventData secretEvents[] = { + SECRET_EVENT(VIR_SECRET_EVENT_ID_LIFECYCLE, mySecretEventCallback), +}; + /* make sure that the events are kept in sync */ verify(ARRAY_CARDINALITY(domainEvents) == VIR_DOMAIN_EVENT_ID_LAST); verify(ARRAY_CARDINALITY(storagePoolEvents) == VIR_STORAGE_POOL_EVENT_ID_LAST); verify(ARRAY_CARDINALITY(nodeDeviceEvents) == VIR_NODE_DEVICE_EVENT_ID_LAST); +verify(ARRAY_CARDINALITY(secretEvents) == VIR_SECRET_EVENT_ID_LAST); int main(int argc, char **argv) @@ -1149,6 +1199,22 @@ main(int argc, char **argv) } } + /* register common secret callbacks */ + for (i = 0; i < ARRAY_CARDINALITY(secretEvents); i++) { + struct secretEventData *event = secretEvents + i; + + event->id = virConnectSecretEventRegisterAny(dconn, NULL, + event->event, + event->cb, + strdup(event->name), + myFreeFunc); + + if (event->id < 0) { + fprintf(stderr, "Failed to register event '%s'\n", event->name); + goto cleanup; + } + } + if ((callback1ret == -1) || (callback16ret == -1)) goto cleanup; @@ -1191,6 +1257,12 @@ main(int argc, char **argv) virConnectNodeDeviceEventDeregisterAny(dconn, nodeDeviceEvents[i].id); } + printf("Deregistering secret event callbacks\n"); + for (i = 0; i < ARRAY_CARDINALITY(secretEvents); i++) { + if (secretEvents[i].id > 0) + virConnectSecretEventDeregisterAny(dconn, secretEvents[i].id); + } + virConnectUnregisterCloseCallback(dconn, connectClose); ret = EXIT_SUCCESS; -- 2.9.3

Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- tools/virsh-secret.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virsh.pod | 18 +++++ 2 files changed, 208 insertions(+) diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c index d868eb3..f7197f4 100644 --- a/tools/virsh-secret.c +++ b/tools/virsh-secret.c @@ -33,6 +33,7 @@ #include "virfile.h" #include "virutil.h" #include "virstring.h" +#include "virtime.h" #include "conf/secret_conf.h" static virSecretPtr @@ -550,6 +551,189 @@ cmdSecretList(vshControl *ctl, const vshCmd *cmd ATTRIBUTE_UNUSED) return ret; } +/* + * "Secret-event" command + */ +VIR_ENUM_DECL(virshSecretEvent) +VIR_ENUM_IMPL(virshSecretEvent, + VIR_SECRET_EVENT_LAST, + N_("Defined"), + N_("Undefined")) + +static const char * +virshSecretEventToString(int event) +{ + const char *str = virshSecretEventTypeToString(event); + return str ? _(str) : _("unknown"); +} + +struct vshEventCallback { + const char *name; + virConnectSecretEventGenericCallback cb; +}; +typedef struct vshEventCallback vshEventCallback; + +struct virshSecretEventData { + vshControl *ctl; + bool loop; + bool timestamp; + int count; + vshEventCallback *cb; +}; +typedef struct virshSecretEventData virshSecretEventData; + +static void +vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + int event, + int detail ATTRIBUTE_UNUSED, + void *opaque) +{ + virshSecretEventData *data = opaque; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!data->loop && data->count) + return; + + virSecretGetUUIDString(secret, uuid); + if (data->timestamp) { + char timestamp[VIR_TIME_STRING_BUFLEN]; + + if (virTimeStringNowRaw(timestamp) < 0) + timestamp[0] = '\0'; + + vshPrint(data->ctl, _("%s: event 'lifecycle' for secret %s: %s\n"), + timestamp, uuid, virshSecretEventToString(event)); + } else { + vshPrint(data->ctl, _("event 'lifecycle' for secret %s: %s\n"), + uuid, virshSecretEventToString(event)); + } + + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + +static vshEventCallback vshEventCallbacks[] = { + { "lifecycle", + VIR_SECRET_EVENT_CALLBACK(vshEventLifecyclePrint), }, +}; + +static const vshCmdInfo info_secret_event[] = { + {.name = "help", + .data = N_("Secret Events") + }, + {.name = "desc", + .data = N_("List event types, or wait for secret events to occur") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_secret_event[] = { + {.name = "secret", + .type = VSH_OT_STRING, + .help = N_("filter by secret name or uuid") + }, + {.name = "event", + .type = VSH_OT_STRING, + .help = N_("which event type to wait for") + }, + {.name = "loop", + .type = VSH_OT_BOOL, + .help = N_("loop until timeout or interrupt, rather than one-shot") + }, + {.name = "timeout", + .type = VSH_OT_INT, + .help = N_("timeout seconds") + }, + {.name = "list", + .type = VSH_OT_BOOL, + .help = N_("list valid event types") + }, + {.name = "timestamp", + .type = VSH_OT_BOOL, + .help = N_("show timestamp for each printed event") + }, + {.name = NULL} +}; + +static bool +cmdSecretEvent(vshControl *ctl, const vshCmd *cmd) +{ + virSecretPtr secret = NULL; + bool ret = false; + int eventId = -1; + int timeout = 0; + virshSecretEventData data; + const char *eventName = NULL; + int event; + virshControlPtr priv = ctl->privData; + + if (vshCommandOptBool(cmd, "list")) { + size_t i; + + for (i = 0; i < VIR_SECRET_EVENT_ID_LAST; i++) + vshPrint(ctl, "%s\n", vshEventCallbacks[i].name); + return true; + } + + if (vshCommandOptStringReq(ctl, cmd, "event", &eventName) < 0) + return false; + if (!eventName) { + vshError(ctl, "%s", _("either --list or --event <type> is required")); + return false; + } + for (event = 0; event < VIR_SECRET_EVENT_ID_LAST; event++) + if (STREQ(eventName, vshEventCallbacks[event].name)) + break; + if (event == VIR_SECRET_EVENT_ID_LAST) { + vshError(ctl, _("unknown event type %s"), eventName); + return false; + } + + data.ctl = ctl; + data.loop = vshCommandOptBool(cmd, "loop"); + data.timestamp = vshCommandOptBool(cmd, "timestamp"); + data.count = 0; + data.cb = &vshEventCallbacks[event]; + if (vshCommandOptTimeoutToMs(ctl, cmd, &timeout) < 0) + return false; + + if (vshCommandOptBool(cmd, "secret")) + secret = virshCommandOptSecret(ctl, cmd, NULL); + if (vshEventStart(ctl, timeout) < 0) + goto cleanup; + + if ((eventId = virConnectSecretEventRegisterAny(priv->conn, secret, event, + data.cb->cb, + &data, NULL)) < 0) + goto cleanup; + switch (vshEventWait(ctl)) { + case VSH_EVENT_INTERRUPT: + vshPrint(ctl, "%s", _("event loop interrupted\n")); + break; + case VSH_EVENT_TIMEOUT: + vshPrint(ctl, "%s", _("event loop timed out\n")); + break; + case VSH_EVENT_DONE: + break; + default: + goto cleanup; + } + vshPrint(ctl, _("events received: %d\n"), data.count); + if (data.count) + ret = true; + + cleanup: + vshEventCleanup(ctl); + if (eventId >= 0 && + virConnectSecretEventDeregisterAny(priv->conn, eventId) < 0) + ret = false; + if (secret) + virSecretFree(secret); + return ret; +} + const vshCmdDef secretCmds[] = { {.name = "secret-define", .handler = cmdSecretDefine, @@ -563,6 +747,12 @@ const vshCmdDef secretCmds[] = { .info = info_secret_dumpxml, .flags = 0 }, + {.name = "secret-event", + .handler = cmdSecretEvent, + .opts = opts_secret_event, + .info = info_secret_event, + .flags = 0 + }, {.name = "secret-get-value", .handler = cmdSecretGetValue, .opts = opts_secret_get_value, diff --git a/tools/virsh.pod b/tools/virsh.pod index ef91223..1cb7c4d 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -3964,6 +3964,24 @@ properties defined in I<file>, without affecting the secret value. Output properties of I<secret> (specified by its UUID) as an XML dump to stdout. +=item B<secret-event> {[I<secret>] I<event> [I<--loop>] [I<--timeout> +I<seconds>] [I<--timestamp>] | I<--list>} + +Wait for a class of secret events to occur, and print appropriate details +of events as they happen. The events can optionally be filtered by +I<secret>. Using I<--list> as the only argument will provide a list +of possible I<event> values known by this client, although the connection +might not allow registering for all these events. + +By default, this command is one-shot, and returns success once an event +occurs; you can send SIGINT (usually via C<Ctrl-C>) to quit immediately. +If I<--timeout> is specified, the command gives up waiting for events +after I<seconds> have elapsed. With I<--loop>, the command prints all +events until a timeout or interrupt key. + +When I<--timestamp> is used, a human-readable timestamp will be printed +before the event. + =item B<secret-set-value> I<secret> I<base64> Set the value associated with I<secret> (specified by its UUID) to the value -- 2.9.3

Emit an event whenever a secret value changes Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 29 ++++++++++++++++++++ include/libvirt/libvirt-secret.h | 1 + src/conf/secret_event.c | 59 ++++++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 4 +++ src/libvirt_private.syms | 1 + src/remote/remote_driver.c | 30 ++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/secret/secret_driver.c | 6 ++++ tools/virsh-secret.c | 35 ++++++++++++++++++++++++ 9 files changed, 177 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index 3a7ee9e..f2b9b9a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1528,8 +1528,37 @@ remoteRelaySecretEventLifecycle(virConnectPtr conn, return 0; } +static int +remoteRelaySecretEventValueChanged(virConnectPtr conn, + virSecretPtr secret, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_secret_event_value_changed_msg data; + + if (callback->callbackID < 0 || + !remoteRelaySecretEventCheckACL(callback->client, conn, secret)) + return -1; + + VIR_DEBUG("Relaying node secret value changed callback %d", + callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_secret(&data.secret, secret); + data.callbackID = callback->callbackID; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED, + (xdrproc_t)xdr_remote_secret_event_value_changed_msg, + &data); + + return 0; +} + static virConnectSecretEventGenericCallback secretEventCallbacks[] = { VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventLifecycle), + VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventValueChanged), }; verify(ARRAY_CARDINALITY(secretEventCallbacks) == VIR_SECRET_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-secret.h index 1bbbf3f..5df0b8f 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -127,6 +127,7 @@ int virSecretFree (virSecretPtr secret); */ typedef enum { VIR_SECRET_EVENT_ID_LIFECYCLE = 0, /* virConnectSecretEventLifecycleCallback */ + VIR_SECRET_EVENT_ID_VALUE_CHANGED = 1, /* virConnectSecretEventGenericCallback */ # ifdef VIR_ENUM_SENTINELS VIR_SECRET_EVENT_ID_LAST diff --git a/src/conf/secret_event.c b/src/conf/secret_event.c index 9d578f0..c130909 100644 --- a/src/conf/secret_event.c +++ b/src/conf/secret_event.c @@ -48,10 +48,19 @@ struct _virSecretEventLifecycle { typedef struct _virSecretEventLifecycle virSecretEventLifecycle; typedef virSecretEventLifecycle *virSecretEventLifecyclePtr; +struct _virSecretEventValueChanged { + virSecretEvent parent; + bool dummy; +}; +typedef struct _virSecretEventValueChanged virSecretEventValueChanged; +typedef virSecretEventValueChanged *virSecretEventValueChangedPtr; + static virClassPtr virSecretEventClass; static virClassPtr virSecretEventLifecycleClass; +static virClassPtr virSecretEventValueChangedClass; static void virSecretEventDispose(void *obj); static void virSecretEventLifecycleDispose(void *obj); +static void virSecretEventValueChangedDispose(void *obj); static int virSecretEventsOnceInit(void) @@ -68,6 +77,12 @@ virSecretEventsOnceInit(void) sizeof(virSecretEventLifecycle), virSecretEventLifecycleDispose))) return -1; + if (!(virSecretEventValueChangedClass = + virClassNew(virSecretEventClass, + "virSecretEventValueChanged", + sizeof(virSecretEventValueChanged), + virSecretEventValueChangedDispose))) + return -1; return 0; } @@ -90,6 +105,14 @@ virSecretEventLifecycleDispose(void *obj) static void +virSecretEventValueChangedDispose(void *obj) +{ + virSecretEventValueChangedPtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void virSecretEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, virConnectObjectEventGenericCallback cb, @@ -116,6 +139,13 @@ virSecretEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_SECRET_EVENT_ID_VALUE_CHANGED: + { + ((virConnectSecretEventGenericCallback)cb)(conn, secret, + cbopaque); + goto cleanup; + } + case VIR_SECRET_EVENT_ID_LAST: break; } @@ -250,3 +280,32 @@ virSecretEventLifecycleNew(const unsigned char *uuid, return (virObjectEventPtr)event; } + + +/** + * virSecretEventValueChangedNew: + * @uuid: UUID of the secret object the event describes + * + * Create a new secret lifecycle event. + */ +virObjectEventPtr +virSecretEventValueChangedNew(const unsigned char *uuid, + int usage_type, + const char *usage_id) +{ + virSecretEventValueChangedPtr event; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virSecretEventsInitialize() < 0) + return NULL; + + virUUIDFormat(uuid, uuidstr); + VIR_DEBUG("Event %s %d %s", uuidstr, usage_type, usage_id); + if (!(event = virObjectEventNew(virSecretEventValueChangedClass, + virSecretEventDispatchDefaultFunc, + VIR_SECRET_EVENT_ID_VALUE_CHANGED, + usage_type, usage_id, uuid, uuidstr))) + return NULL; + + return (virObjectEventPtr)event; +} diff --git a/src/conf/secret_event.h b/src/conf/secret_event.h index 4649d94..5fd52f3 100644 --- a/src/conf/secret_event.h +++ b/src/conf/secret_event.h @@ -57,5 +57,9 @@ virSecretEventLifecycleNew(const unsigned char *uuid, const char *usage_id, int type, int detail); +virObjectEventPtr +virSecretEventValueChangedNew(const unsigned char *uuid, + int usage_type, + const char *usage_id); #endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2c78ed6..3d98780 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -842,6 +842,7 @@ virSecretUsageTypeToString; # conf/secret_event.h virSecretEventLifecycleNew; virSecretEventStateRegisterID; +virSecretEventValueChangedNew; # conf/snapshot_conf.h diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e689316..59d74a2 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -391,6 +391,11 @@ remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, void *evdata, void *opaque); static void +remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); + +static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); @@ -593,6 +598,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteSecretBuildEventLifecycle, sizeof(remote_secret_event_lifecycle_msg), (xdrproc_t)xdr_remote_secret_event_lifecycle_msg }, + { REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED, + remoteSecretBuildEventValueChanged, + sizeof(remote_secret_event_value_changed_msg), + (xdrproc_t)xdr_remote_secret_event_value_changed_msg }, }; static void @@ -5517,6 +5526,27 @@ remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, } static void +remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_secret_event_value_changed_msg *msg = evdata; + virSecretPtr secret; + virObjectEventPtr event = NULL; + + secret = get_nonnull_secret(conn, msg->secret); + if (!secret) + return; + + event = virSecretEventValueChangedNew(secret->uuid, secret->usageType, secret->usageID); + virObjectUnref(secret); + + remoteEventQueue(priv, event, msg->callbackID); +} + +static void remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ce4c4ef..6445685 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3381,6 +3381,11 @@ struct remote_secret_event_lifecycle_msg { int detail; }; +struct remote_secret_event_value_changed_msg { + int callbackID; + remote_nonnull_secret secret; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -6007,6 +6012,12 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382 + REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383 }; diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 0e3d97e..2a371b6 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -327,6 +327,7 @@ secretSetValue(virSecretPtr obj, int ret = -1; virSecretObjPtr secret; virSecretDefPtr def; + virObjectEventPtr event = NULL; virCheckFlags(0, -1); @@ -343,10 +344,15 @@ secretSetValue(virSecretPtr obj, if (virSecretObjSetValue(secret, value, value_size) < 0) goto cleanup; + event = virSecretEventValueChangedNew(def->uuid, + def->usage_type, + def->usage_id); ret = 0; cleanup: virSecretObjEndAPI(&secret); + if (event) + virObjectEventStateQueue(driver->secretEventState, event); return ret; } diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c index f7197f4..cd788b6 100644 --- a/tools/virsh-secret.c +++ b/tools/virsh-secret.c @@ -614,9 +614,44 @@ vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); } +static void +vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + void *opaque) +{ + virshSecretEventData *data = opaque; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!data->loop && data->count) + return; + + virSecretGetUUIDString(secret, uuid); + + if (data->timestamp) { + char timestamp[VIR_TIME_STRING_BUFLEN]; + + if (virTimeStringNowRaw(timestamp) < 0) + timestamp[0] = '\0'; + + vshPrint(data->ctl, _("%s: event '%s' for secret %s\n"), + timestamp, + data->cb->name, + uuid); + } else { + vshPrint(data->ctl, _("event '%s' for secret %s\n"), + data->cb->name, + uuid); + } + + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_SECRET_EVENT_CALLBACK(vshEventLifecyclePrint), }, + { "value-changed", vshEventGenericPrint, }, }; static const vshCmdInfo info_secret_event[] = { -- 2.9.3

On 01/05/2017 02:59 PM, Daniel P. Berrange wrote:
Emit an event whenever a secret value changes
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 29 ++++++++++++++++++++ include/libvirt/libvirt-secret.h | 1 + src/conf/secret_event.c | 59 ++++++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 4 +++ src/libvirt_private.syms | 1 + src/remote/remote_driver.c | 30 ++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/secret/secret_driver.c | 6 ++++ tools/virsh-secret.c | 35 ++++++++++++++++++++++++ 9 files changed, 177 insertions(+), 1 deletion(-)
diff --git a/daemon/remote.c b/daemon/remote.c index 3a7ee9e..f2b9b9a 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1528,8 +1528,37 @@ remoteRelaySecretEventLifecycle(virConnectPtr conn, return 0; }
+static int +remoteRelaySecretEventValueChanged(virConnectPtr conn, + virSecretPtr secret, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_secret_event_value_changed_msg data; + + if (callback->callbackID < 0 || + !remoteRelaySecretEventCheckACL(callback->client, conn, secret)) + return -1; + + VIR_DEBUG("Relaying node secret value changed callback %d", + callback->callbackID); + + /* build return data */ + memset(&data, 0, sizeof(data)); + make_nonnull_secret(&data.secret, secret); + data.callbackID = callback->callbackID; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED, + (xdrproc_t)xdr_remote_secret_event_value_changed_msg, + &data); + + return 0; +} + static virConnectSecretEventGenericCallback secretEventCallbacks[] = { VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventLifecycle), + VIR_SECRET_EVENT_CALLBACK(remoteRelaySecretEventValueChanged), };
verify(ARRAY_CARDINALITY(secretEventCallbacks) == VIR_SECRET_EVENT_ID_LAST); diff --git a/include/libvirt/libvirt-secret.h b/include/libvirt/libvirt-secret.h index 1bbbf3f..5df0b8f 100644 --- a/include/libvirt/libvirt-secret.h +++ b/include/libvirt/libvirt-secret.h @@ -127,6 +127,7 @@ int virSecretFree (virSecretPtr secret); */ typedef enum { VIR_SECRET_EVENT_ID_LIFECYCLE = 0, /* virConnectSecretEventLifecycleCallback */ + VIR_SECRET_EVENT_ID_VALUE_CHANGED = 1, /* virConnectSecretEventGenericCallback */
# ifdef VIR_ENUM_SENTINELS VIR_SECRET_EVENT_ID_LAST diff --git a/src/conf/secret_event.c b/src/conf/secret_event.c index 9d578f0..c130909 100644 --- a/src/conf/secret_event.c +++ b/src/conf/secret_event.c @@ -48,10 +48,19 @@ struct _virSecretEventLifecycle { typedef struct _virSecretEventLifecycle virSecretEventLifecycle; typedef virSecretEventLifecycle *virSecretEventLifecyclePtr;
+struct _virSecretEventValueChanged { + virSecretEvent parent; + bool dummy; +}; +typedef struct _virSecretEventValueChanged virSecretEventValueChanged; +typedef virSecretEventValueChanged *virSecretEventValueChangedPtr; + static virClassPtr virSecretEventClass; static virClassPtr virSecretEventLifecycleClass; +static virClassPtr virSecretEventValueChangedClass; static void virSecretEventDispose(void *obj); static void virSecretEventLifecycleDispose(void *obj); +static void virSecretEventValueChangedDispose(void *obj);
static int virSecretEventsOnceInit(void) @@ -68,6 +77,12 @@ virSecretEventsOnceInit(void) sizeof(virSecretEventLifecycle), virSecretEventLifecycleDispose))) return -1; + if (!(virSecretEventValueChangedClass = + virClassNew(virSecretEventClass, + "virSecretEventValueChanged", + sizeof(virSecretEventValueChanged), + virSecretEventValueChangedDispose))) + return -1; return 0; }
@@ -90,6 +105,14 @@ virSecretEventLifecycleDispose(void *obj)
static void +virSecretEventValueChangedDispose(void *obj) +{ + virSecretEventValueChangedPtr event = obj; + VIR_DEBUG("obj=%p", event); +} + + +static void virSecretEventDispatchDefaultFunc(virConnectPtr conn, virObjectEventPtr event, virConnectObjectEventGenericCallback cb, @@ -116,6 +139,13 @@ virSecretEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; }
+ case VIR_SECRET_EVENT_ID_VALUE_CHANGED: + { + ((virConnectSecretEventGenericCallback)cb)(conn, secret, + cbopaque); + goto cleanup; + } + case VIR_SECRET_EVENT_ID_LAST: break; } @@ -250,3 +280,32 @@ virSecretEventLifecycleNew(const unsigned char *uuid,
return (virObjectEventPtr)event; } + + +/** + * virSecretEventValueChangedNew: + * @uuid: UUID of the secret object the event describes + * + * Create a new secret lifecycle event. + */ +virObjectEventPtr +virSecretEventValueChangedNew(const unsigned char *uuid, + int usage_type, + const char *usage_id) +{ + virSecretEventValueChangedPtr event; + char uuidstr[VIR_UUID_STRING_BUFLEN]; + + if (virSecretEventsInitialize() < 0) + return NULL; + + virUUIDFormat(uuid, uuidstr); + VIR_DEBUG("Event %s %d %s", uuidstr, usage_type, usage_id); + if (!(event = virObjectEventNew(virSecretEventValueChangedClass, + virSecretEventDispatchDefaultFunc, + VIR_SECRET_EVENT_ID_VALUE_CHANGED, + usage_type, usage_id, uuid, uuidstr))) + return NULL; + + return (virObjectEventPtr)event; +} diff --git a/src/conf/secret_event.h b/src/conf/secret_event.h index 4649d94..5fd52f3 100644 --- a/src/conf/secret_event.h +++ b/src/conf/secret_event.h @@ -57,5 +57,9 @@ virSecretEventLifecycleNew(const unsigned char *uuid, const char *usage_id, int type, int detail); +virObjectEventPtr +virSecretEventValueChangedNew(const unsigned char *uuid, + int usage_type, + const char *usage_id);
#endif diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2c78ed6..3d98780 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -842,6 +842,7 @@ virSecretUsageTypeToString; # conf/secret_event.h virSecretEventLifecycleNew; virSecretEventStateRegisterID; +virSecretEventValueChangedNew;
# conf/snapshot_conf.h diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index e689316..59d74a2 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -391,6 +391,11 @@ remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, void *evdata, void *opaque);
static void +remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque); + +static void remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque); @@ -593,6 +598,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteSecretBuildEventLifecycle, sizeof(remote_secret_event_lifecycle_msg), (xdrproc_t)xdr_remote_secret_event_lifecycle_msg }, + { REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED, + remoteSecretBuildEventValueChanged, + sizeof(remote_secret_event_value_changed_msg), + (xdrproc_t)xdr_remote_secret_event_value_changed_msg }, };
static void @@ -5517,6 +5526,27 @@ remoteSecretBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, }
static void +remoteSecretBuildEventValueChanged(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + remote_secret_event_value_changed_msg *msg = evdata; + virSecretPtr secret; + virObjectEventPtr event = NULL; + + secret = get_nonnull_secret(conn, msg->secret); + if (!secret) + return; + + event = virSecretEventValueChangedNew(secret->uuid, secret->usageType, secret->usageID); + virObjectUnref(secret); + + remoteEventQueue(priv, event, msg->callbackID); +} + +static void remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, void *opaque) diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index ce4c4ef..6445685 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3381,6 +3381,11 @@ struct remote_secret_event_lifecycle_msg { int detail; };
+struct remote_secret_event_value_changed_msg { + int callbackID; + remote_nonnull_secret secret; +}; + /*----- Protocol. -----*/
/* Define the program number, protocol version and procedure numbers here. */ @@ -6007,6 +6012,12 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382 + REMOTE_PROC_SECRET_EVENT_LIFECYCLE = 382, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_SECRET_EVENT_VALUE_CHANGED = 383
}; diff --git a/src/secret/secret_driver.c b/src/secret/secret_driver.c index 0e3d97e..2a371b6 100644 --- a/src/secret/secret_driver.c +++ b/src/secret/secret_driver.c @@ -327,6 +327,7 @@ secretSetValue(virSecretPtr obj, int ret = -1; virSecretObjPtr secret; virSecretDefPtr def; + virObjectEventPtr event = NULL;
virCheckFlags(0, -1);
@@ -343,10 +344,15 @@ secretSetValue(virSecretPtr obj, if (virSecretObjSetValue(secret, value, value_size) < 0) goto cleanup;
+ event = virSecretEventValueChangedNew(def->uuid, + def->usage_type, + def->usage_id); ret = 0;
cleanup: virSecretObjEndAPI(&secret); + if (event) + virObjectEventStateQueue(driver->secretEventState, event);
return ret; } diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c index f7197f4..cd788b6 100644 --- a/tools/virsh-secret.c +++ b/tools/virsh-secret.c @@ -614,9 +614,44 @@ vshEventLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, vshEventDone(data->ctl); }
+static void +vshEventGenericPrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + void *opaque) +{ + virshSecretEventData *data = opaque; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (!data->loop && data->count) + return; + + virSecretGetUUIDString(secret, uuid); + + if (data->timestamp) { + char timestamp[VIR_TIME_STRING_BUFLEN]; + + if (virTimeStringNowRaw(timestamp) < 0) + timestamp[0] = '\0'; + + vshPrint(data->ctl, _("%s: event '%s' for secret %s\n"), + timestamp, + data->cb->name, + uuid); + } else { + vshPrint(data->ctl, _("event '%s' for secret %s\n"), + data->cb->name, + uuid); + } + + data->count++; + if (!data->loop) + vshEventDone(data->ctl); +} + static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_SECRET_EVENT_CALLBACK(vshEventLifecyclePrint), }, + { "value-changed", vshEventGenericPrint, },
We advise users to wrap their callbacks with VIR_SECRET_EVENT_CALLBACK() macro. We should follow our own advices.
};
static const vshCmdInfo info_secret_event[] = {
Also, looks like there's something missing: object-events/event-test.c:1094:1: note: in expansion of macro 'verify' verify(ARRAY_CARDINALITY(secretEvents) == VIR_SECRET_EVENT_ID_LAST); ^ Perhaps this? diff --git i/examples/object-events/event-test.c w/examples/object-events/event-test.c index cf8c1bca2..55c004f93 100644 --- i/examples/object-events/event-test.c +++ w/examples/object-events/event-test.c @@ -763,6 +763,18 @@ mySecretEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int +mySecretEventValueChanged(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + void *opaque ATTRIBUTE_UNUSED) +{ + char uuid[VIR_UUID_STRING_BUFLEN]; + virSecretGetUUIDString(secret, uuid); + printf("%s EVENT: Secret %s\n", __func__, uuid); + return 0; +} + + static void eventTypedParamsPrint(virTypedParameterPtr params, int nparams) @@ -1085,6 +1097,7 @@ struct secretEventData { struct secretEventData secretEvents[] = { SECRET_EVENT(VIR_SECRET_EVENT_ID_LIFECYCLE, mySecretEventCallback), + SECRET_EVENT(VIR_SECRET_EVENT_ID_VALUE_CHANGED, mySecretEventValueChanged), }; /* make sure that the events are kept in sync */ Michal

On Fri, Jan 06, 2017 at 04:02:45PM +0100, Michal Privoznik wrote:
On 01/05/2017 02:59 PM, Daniel P. Berrange wrote:
Emit an event whenever a secret value changes
Signed-off-by: Daniel P. Berrange <berrange@redhat.com> --- daemon/remote.c | 29 ++++++++++++++++++++ include/libvirt/libvirt-secret.h | 1 + src/conf/secret_event.c | 59 ++++++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 4 +++ src/libvirt_private.syms | 1 + src/remote/remote_driver.c | 30 ++++++++++++++++++++ src/remote/remote_protocol.x | 13 ++++++++- src/secret/secret_driver.c | 6 ++++ tools/virsh-secret.c | 35 ++++++++++++++++++++++++ 9 files changed, 177 insertions(+), 1 deletion(-)
+ static vshEventCallback vshEventCallbacks[] = { { "lifecycle", VIR_SECRET_EVENT_CALLBACK(vshEventLifecyclePrint), }, + { "value-changed", vshEventGenericPrint, },
We advise users to wrap their callbacks with VIR_SECRET_EVENT_CALLBACK() macro. We should follow our own advices.
The macro casts to virConnectSecretGenericCallback, and vshEventGenericPrint already has that type, so no cast is required here.
};
static const vshCmdInfo info_secret_event[] = {
Also, looks like there's something missing:
object-events/event-test.c:1094:1: note: in expansion of macro 'verify' verify(ARRAY_CARDINALITY(secretEvents) == VIR_SECRET_EVENT_ID_LAST); ^
Opps, yes, I guess I didn't test the examples
Perhaps this?
diff --git i/examples/object-events/event-test.c w/examples/object-events/event-test.c index cf8c1bca2..55c004f93 100644 --- i/examples/object-events/event-test.c +++ w/examples/object-events/event-test.c @@ -763,6 +763,18 @@ mySecretEventCallback(virConnectPtr conn ATTRIBUTE_UNUSED, }
+static int +mySecretEventValueChanged(virConnectPtr conn ATTRIBUTE_UNUSED, + virSecretPtr secret, + void *opaque ATTRIBUTE_UNUSED) +{ + char uuid[VIR_UUID_STRING_BUFLEN]; + virSecretGetUUIDString(secret, uuid); + printf("%s EVENT: Secret %s\n", __func__, uuid); + return 0; +} + + static void eventTypedParamsPrint(virTypedParameterPtr params, int nparams) @@ -1085,6 +1097,7 @@ struct secretEventData {
struct secretEventData secretEvents[] = { SECRET_EVENT(VIR_SECRET_EVENT_ID_LIFECYCLE, mySecretEventCallback), + SECRET_EVENT(VIR_SECRET_EVENT_ID_VALUE_CHANGED, mySecretEventValueChanged), };
/* make sure that the events are kept in sync */
Yep, that looks sane. Regards, Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://entangle-photo.org -o- http://search.cpan.org/~danberr/ :|

On 01/05/2017 02:59 PM, Daniel P. Berrange wrote:
This series adds suppoort for more events
- Event when <metadata> in a domain changes - Event when virSecret are created/deleted - Event when virSecret value is changed
The latter two of course required wiring up the secret object for event support, since we've not had secret events before now.
Daniel P. Berrange (9): Add domain event for metadata changes Introduce secret lifecycle event APIs conf: add secret event handling conf: simplify internal virSecretDef handling of usage remote: implement secret lifecycle event APIs secret: add support for lifecycle events examples: add secret events to event demo program virsh: add secret lifecycle event handling secret: add support for value change events
daemon/libvirtd.h | 2 + daemon/remote.c | 276 ++++++++++++++++++++++++++++++++ examples/object-events/event-test.c | 107 +++++++++++++ include/libvirt/libvirt-domain.h | 20 +++ include/libvirt/libvirt-secret.h | 92 +++++++++++ src/Makefile.am | 5 + src/access/viraccessdriverpolkit.c | 8 +- src/bhyve/bhyve_driver.c | 7 + src/conf/domain_event.c | 86 ++++++++++ src/conf/domain_event.h | 10 ++ src/conf/secret_conf.c | 74 ++------- src/conf/secret_conf.h | 9 +- src/conf/secret_event.c | 311 ++++++++++++++++++++++++++++++++++++ src/conf/secret_event.h | 65 ++++++++ src/conf/virsecretobj.c | 42 +---- src/datatypes.c | 3 +- src/datatypes.h | 14 ++ src/driver-secret.h | 14 ++ src/libvirt-secret.c | 129 +++++++++++++++ src/libvirt_private.syms | 9 +- src/libvirt_public.syms | 2 + src/lxc/lxc_driver.c | 6 + src/qemu/qemu_driver.c | 6 + src/remote/remote_driver.c | 196 ++++++++++++++++++++++- src/remote/remote_protocol.x | 69 +++++++- src/remote_protocol-structs | 26 +++ src/secret/secret_driver.c | 82 +++++++++- src/storage/storage_backend.c | 2 +- src/test/test_driver.c | 6 + tools/virsh-domain.c | 25 +++ tools/virsh-secret.c | 225 ++++++++++++++++++++++++++ tools/virsh.pod | 18 +++ 32 files changed, 1829 insertions(+), 117 deletions(-) create mode 100644 src/conf/secret_event.c create mode 100644 src/conf/secret_event.h
Looking good, except for a small problem in the last patch. Fix it please before pushing. ACK series. Michal
participants (3)
-
Daniel P. Berrange
-
Michal Privoznik
-
Peter Krempa