Presently domain events are emitted for the "agent lifecycle" which is a specialization on virtio-serial events specific to the channel named "org.qemu.guest_agent.0". This patch adds a generic event for "channel lifecycles", which emit state change events for all attached channels. --- daemon/remote.c | 42 +++++++++++++++++ examples/object-events/event-test.c | 57 ++++++++++++++++++++++++ include/libvirt/libvirt-domain.h | 44 ++++++++++++++++++ src/conf/domain_event.c | 89 +++++++++++++++++++++++++++++++++++++ src/conf/domain_event.h | 12 +++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 5 +++ src/qemu/qemu_process.c | 6 +++ src/remote/remote_driver.c | 32 +++++++++++++ src/remote/remote_protocol.x | 17 ++++++- src/remote_protocol-structs | 7 +++ tools/virsh-domain.c | 35 +++++++++++++++ 12 files changed, 347 insertions(+), 1 deletion(-) diff --git a/daemon/remote.c b/daemon/remote.c index e414f92..25b32f2 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -1086,6 +1086,47 @@ remoteRelayDomainEventAgentLifecycle(virConnectPtr conn, static int +remoteRelayDomainEventChannelLifecycle(virConnectPtr conn, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + daemonClientEventCallbackPtr callback = opaque; + remote_domain_event_callback_channel_lifecycle_msg data; + + if (callback->callbackID < 0 || + !remoteRelayDomainEventCheckACL(callback->client, conn, dom)) + return -1; + + VIR_DEBUG("Relaying domain channel lifecycle event %s %d, callback %d, " + " name: %s, state %d, reason %d", + dom->name, dom->id, callback->callbackID, channelName, state, reason); + + /* build return data */ + memset(&data, 0, sizeof(data)); + data.callbackID = callback->callbackID; + make_nonnull_domain(&data.dom, dom); + + if (VIR_STRDUP(data.channelName, channelName) < 0) + goto error; + data.state = state; + data.reason = reason; + + remoteDispatchObjectEventSend(callback->client, remoteProgram, + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_callback_channel_lifecycle_msg, + &data); + + return 0; + error: + VIR_FREE(data.channelName); + return -1; +} + + +static int remoteRelayDomainEventDeviceAdded(virConnectPtr conn, virDomainPtr dom, const char *devAlias, @@ -1248,6 +1289,7 @@ static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventMigrationIteration), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventJobCompleted), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventDeviceRemovalFailed), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventChannelLifecycle) }; 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..da023eb 100644 --- a/examples/object-events/event-test.c +++ b/examples/object-events/event-test.c @@ -337,6 +337,46 @@ guestAgentLifecycleEventReasonToString(int event) return "unknown"; } + +static const char * +guestChannelLifecycleEventStateToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleState) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED: + return "Disconnected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED: + return "Connected"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST: + break; + } + + return "unknown"; +} + + +static const char * +guestChannelLifecycleEventReasonToString(int event) +{ + switch ((virConnectDomainEventChannelLifecycleReason) event) { + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN: + return "Unknown"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED: + return "Domain started"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL: + return "Channel event"; + + case VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST: + break; + } + + return "unknown"; +} + + static const char * storagePoolEventToString(int event) { @@ -797,6 +837,22 @@ myDomainEventAgentLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int +myDomainEventChannelLifecycleCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) guest channel(%s) state changed: %s reason: %s\n", + __func__, virDomainGetName(dom), virDomainGetID(dom), channelName, + guestChannelLifecycleEventStateToString(state), + guestChannelLifecycleEventReasonToString(reason)); + + return 0; +} + static int myDomainEventDeviceAddedCallback(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -971,6 +1027,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_CHANNEL_LIFECYCLE, myDomainEventChannelLifecycleCallback) }; struct storagePoolEventData { diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 5f50660..9a5c664 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -3965,6 +3965,49 @@ typedef void (*virConnectDomainEventAgentLifecycleCallback)(virConnectPtr conn, int reason, void *opaque); +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_CONNECTED = 1, /* channel connected */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_DISCONNECTED = 2, /* channel disconnected */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST +# endif +} virConnectDomainEventChannelLifecycleState; + +typedef enum { + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_UNKNOWN = 0, /* unknown state change reason */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_DOMAIN_STARTED = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_DOMAIN_STARTED, /* state changed due to domain start */ + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL = + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL, /* channel state changed */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST +# endif +} virConnectDomainEventChannelLifecycleReason; + +/** + * virConnectDomainEventChannelLifecycleCallback: + * @conn: connection object + * @dom: domain on which the event occurred + * @channelName: the name of the channel on which the event occurred + * @state: new state of the guest channel, one of virConnectDomainEventChannelLifecycleState + * @reason: reason for state change; one of virConnectDomainEventChannelLifecycleReason + * @opaque: application specified data + * + * This callback occurs when libvirt detects a change in the state of a guest + * serial channel. The hypervisor must support serial notification, and this is + * currently limited to modern versions of qemu. + * + * The callback signature to use when registering for an event of type + * VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE with virConnectDomainEventRegisterAny() + */ +typedef void (*virConnectDomainEventChannelLifecycleCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque); /** * VIR_DOMAIN_EVENT_CALLBACK: @@ -4006,6 +4049,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_CHANNEL_LIFECYCLE = 23, /* virConnectDomainEventChannelLifecycleCallback */ # ifdef VIR_ENUM_SENTINELS VIR_DOMAIN_EVENT_ID_LAST diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 63ae9e1..f971a0d 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -55,6 +55,7 @@ static virClassPtr virDomainEventPMClass; static virClassPtr virDomainQemuMonitorEventClass; static virClassPtr virDomainEventTunableClass; static virClassPtr virDomainEventAgentLifecycleClass; +static virClassPtr virDomainEventChannelLifecycleClass; static virClassPtr virDomainEventDeviceAddedClass; static virClassPtr virDomainEventMigrationIterationClass; static virClassPtr virDomainEventJobCompletedClass; @@ -75,6 +76,7 @@ static void virDomainEventPMDispose(void *obj); static void virDomainQemuMonitorEventDispose(void *obj); static void virDomainEventTunableDispose(void *obj); static void virDomainEventAgentLifecycleDispose(void *obj); +static void virDomainEventChannelLifecycleDispose(void *obj); static void virDomainEventDeviceAddedDispose(void *obj); static void virDomainEventMigrationIterationDispose(void *obj); static void virDomainEventJobCompletedDispose(void *obj); @@ -241,6 +243,16 @@ struct _virDomainEventAgentLifecycle { typedef struct _virDomainEventAgentLifecycle virDomainEventAgentLifecycle; typedef virDomainEventAgentLifecycle *virDomainEventAgentLifecyclePtr; +struct _virDomainEventChannelLifecycle { + virDomainEvent parent; + + char *channelName; + int state; + int reason; +}; +typedef struct _virDomainEventChannelLifecycle virDomainEventChannelLifecycle; +typedef virDomainEventChannelLifecycle *virDomainEventChannelLifecyclePtr; + struct _virDomainEventMigrationIteration { virDomainEvent parent; @@ -367,6 +379,12 @@ virDomainEventsOnceInit(void) sizeof(virDomainEventAgentLifecycle), virDomainEventAgentLifecycleDispose))) return -1; + if (!(virDomainEventChannelLifecycleClass = + virClassNew(virDomainEventClass, + "virDomainEventChannelLifecycle", + sizeof(virDomainEventChannelLifecycle), + virDomainEventChannelLifecycleDispose))) + return -1; if (!(virDomainEventMigrationIterationClass = virClassNew(virDomainEventClass, "virDomainEventMigrationIteration", @@ -557,6 +575,15 @@ virDomainEventAgentLifecycleDispose(void *obj) }; static void +virDomainEventChannelLifecycleDispose(void *obj) +{ + virDomainEventChannelLifecyclePtr event = obj; + VIR_DEBUG("obj=%p", event); + + VIR_FREE(event->channelName); +}; + +static void virDomainEventMigrationIterationDispose(void *obj) { virDomainEventMigrationIterationPtr event = obj; @@ -1460,6 +1487,56 @@ virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, } static virObjectEventPtr +virDomainEventChannelLifecycleNew(int id, + const char *name, + const unsigned char *uuid, + const char *channelName, + int state, + int reason) +{ + virDomainEventChannelLifecyclePtr ev; + + if (virDomainEventsInitialize() < 0) + return NULL; + + if (!(ev = virDomainEventNew(virDomainEventChannelLifecycleClass, + VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE, + id, name, uuid))) + return NULL; + + if (VIR_STRDUP(ev->channelName, channelName) < 0) { + virObjectUnref(ev); + return NULL; + } + + ev->state = state; + ev->reason = reason; + + return (virObjectEventPtr)ev; +} + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromObj(virDomainObjPtr obj, + const char *channelName, + int state, + int reason) +{ + return virDomainEventChannelLifecycleNew(obj->def->id, obj->def->name, + obj->def->uuid, channelName, state, reason); +} + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromDom(virDomainPtr dom, + const char *channelName, + int state, + int reason) +{ + return virDomainEventChannelLifecycleNew(dom->id, dom->name, dom->uuid, + channelName, state, reason); +} + + +static virObjectEventPtr virDomainEventMigrationIterationNew(int id, const char *name, const unsigned char *uuid, @@ -1812,6 +1889,18 @@ virDomainEventDispatchDefaultFunc(virConnectPtr conn, goto cleanup; } + case VIR_DOMAIN_EVENT_ID_CHANNEL_LIFECYCLE: + { + virDomainEventChannelLifecyclePtr channelLifecycleEvent; + channelLifecycleEvent = (virDomainEventChannelLifecyclePtr)event; + ((virConnectDomainEventChannelLifecycleCallback)cb)(conn, dom, + channelLifecycleEvent->channelName, + channelLifecycleEvent->state, + channelLifecycleEvent->reason, + cbopaque); + goto cleanup; + } + case VIR_DOMAIN_EVENT_ID_DEVICE_ADDED: { virDomainEventDeviceAddedPtr deviceAddedEvent; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 54fa879..3a689b3 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -217,6 +217,18 @@ virDomainEventAgentLifecycleNewFromDom(virDomainPtr dom, int reason); virObjectEventPtr +virDomainEventChannelLifecycleNewFromObj(virDomainObjPtr obj, + const char *channelName, + int state, + int reason); + +virObjectEventPtr +virDomainEventChannelLifecycleNewFromDom(virDomainPtr dom, + const char *channelName, + int state, + int reason); + +virObjectEventPtr virDomainEventMigrationIterationNewFromObj(virDomainObjPtr obj, int iteration); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 74dd527..e259687 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -535,6 +535,8 @@ virDomainEventBlockJob2NewFromDom; virDomainEventBlockJob2NewFromObj; virDomainEventBlockJobNewFromDom; virDomainEventBlockJobNewFromObj; +virDomainEventChannelLifecycleNewFromDom; +virDomainEventChannelLifecycleNewFromObj; virDomainEventControlErrorNewFromDom; virDomainEventControlErrorNewFromObj; virDomainEventDeviceAddedNewFromDom; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 38c8414..b464412 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -4407,6 +4407,7 @@ processSerialChangedEvent(virQEMUDriverPtr driver, virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); virDomainChrDeviceState newstate; virObjectEventPtr event = NULL; + virObjectEventPtr channelEvent = NULL; virDomainDeviceDef dev; qemuDomainObjPrivatePtr priv = vm->privateData; int rc; @@ -4482,6 +4483,10 @@ processSerialChangedEvent(virQEMUDriverPtr driver, qemuDomainEventQueue(driver, event); } + channelEvent = virDomainEventChannelLifecycleNewFromObj(vm, dev.data.chr->target.name, newstate, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_CHANNEL); + qemuDomainEventQueue(driver, channelEvent); + endjob: qemuDomainObjEndJob(driver, vm); diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1b67aee..31b5028 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1931,6 +1931,7 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, int agentReason = VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL; qemuMonitorChardevInfoPtr entry; virObjectEventPtr event = NULL; + virObjectEventPtr channelEvent = NULL; char id[32]; if (booted) @@ -1958,6 +1959,11 @@ qemuProcessRefreshChannelVirtioState(virQEMUDriverPtr driver, agentReason))) qemuDomainEventQueue(driver, event); + + channelEvent = + virDomainEventChannelLifecycleNewFromObj(vm, chr->target.name, entry->state, agentReason); + qemuDomainEventQueue(driver, channelEvent); + chr->state = entry->state; } } diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index a3cd7cd..eb783cb 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -345,6 +345,11 @@ remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog, void *evdata, void *opaque); static void +remoteDomainBuildEventCallbackChannelLifecycle(virNetClientProgramPtr prog, + virNetClientPtr client, + void *evdata, void *opaque); + +static void remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog, virNetClientPtr client, void *evdata, void *opaque); @@ -574,6 +579,10 @@ static virNetClientProgramEvent remoteEvents[] = { remoteNodeDeviceBuildEventUpdate, sizeof(remote_node_device_event_update_msg), (xdrproc_t)xdr_remote_node_device_event_update_msg }, + { REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE, + remoteDomainBuildEventCallbackChannelLifecycle, + sizeof(remote_domain_event_callback_channel_lifecycle_msg), + (xdrproc_t)xdr_remote_domain_event_callback_channel_lifecycle_msg }, }; static void @@ -5152,6 +5161,29 @@ remoteDomainBuildEventCallbackAgentLifecycle(virNetClientProgramPtr prog ATTRIBU static void +remoteDomainBuildEventCallbackChannelLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, + virNetClientPtr client ATTRIBUTE_UNUSED, + void *evdata, void *opaque) +{ + virConnectPtr conn = opaque; + remote_domain_event_callback_channel_lifecycle_msg *msg = evdata; + struct private_data *priv = conn->privateData; + virDomainPtr dom; + virObjectEventPtr event = NULL; + + if (!(dom = get_nonnull_domain(conn, msg->dom))) + return; + + event = virDomainEventChannelLifecycleNewFromDom(dom, msg->channelName, msg->state, + msg->reason); + + virObjectUnref(dom); + + remoteEventQueue(priv, event, msg->callbackID); +} + + +static void remoteDomainBuildEventCallbackMigrationIteration(virNetClientProgramPtr prog ATTRIBUTE_UNUSED, virNetClientPtr client ATTRIBUTE_UNUSED, void *evdata, diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index e8382dc..e5227c4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -3242,6 +3242,15 @@ struct remote_domain_event_callback_agent_lifecycle_msg { int reason; }; +struct remote_domain_event_callback_channel_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + + char *channelName; + int state; + int reason; +}; + struct remote_connect_get_all_domain_stats_ret { remote_domain_stats_record retStats<REMOTE_DOMAIN_LIST_MAX>; }; @@ -5934,5 +5943,11 @@ enum remote_procedure { * @generate: both * @acl: none */ - REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377 + REMOTE_PROC_NODE_DEVICE_EVENT_UPDATE = 377, + + /** + * @generate: both + * @acl: none + */ + REMOTE_PROC_DOMAIN_EVENT_CALLBACK_CHANNEL_LIFECYCLE = 378 }; diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index b71accc..7921016 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -2689,6 +2689,13 @@ struct remote_domain_event_callback_agent_lifecycle_msg { int state; int reason; }; +struct remote_domain_event_callback_channel_lifecycle_msg { + int callbackID; + remote_nonnull_domain dom; + remote_string channelName; + int state; + int reason; +}; struct remote_connect_get_all_domain_stats_ret { struct { u_int retStats_len; diff --git a/tools/virsh-domain.c b/tools/virsh-domain.c index 184f64d..e085358 100644 --- a/tools/virsh-domain.c +++ b/tools/virsh-domain.c @@ -12637,6 +12637,20 @@ VIR_ENUM_IMPL(virshEventAgentLifecycleReason, N_("domain started"), N_("channel event")) +VIR_ENUM_DECL(virshEventChannelLifecycleState) +VIR_ENUM_IMPL(virshEventChannelLifecycleState, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_STATE_LAST, + N_("unknown"), + N_("connected"), + N_("disconnected")) + +VIR_ENUM_DECL(virshEventChannelLifecycleReason) +VIR_ENUM_IMPL(virshEventChannelLifecycleReason, + VIR_CONNECT_DOMAIN_EVENT_CHANNEL_LIFECYCLE_REASON_LAST, + N_("unknown"), + N_("domain started"), + N_("channel event")) + #define UNKNOWNSTR(str) (str ? str : N_("unsupported value")) static void virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, @@ -12656,6 +12670,25 @@ virshEventAgentLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, } static void +virshEventChannelLifecyclePrint(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *channelName, + int state, + int reason, + void *opaque) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + + virBufferAsprintf(&buf, _("event 'channel-lifecycle' for domain %s: name: '%s', " + "state: '%s' reason: '%s'\n"), + virDomainGetName(dom), + channelName, + UNKNOWNSTR(virshEventChannelLifecycleStateTypeToString(state)), + UNKNOWNSTR(virshEventChannelLifecycleReasonTypeToString(reason))); + virshEventPrint(opaque, &buf); +} + +static void virshEventMigrationIterationPrint(virConnectPtr conn ATTRIBUTE_UNUSED, virDomainPtr dom, int iteration, @@ -12755,6 +12788,8 @@ static vshEventCallback vshEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(virshEventJobCompletedPrint), }, { "device-removal-failed", VIR_DOMAIN_EVENT_CALLBACK(virshEventDeviceRemovalFailedPrint), }, + { "channel-lifecycle", + VIR_DOMAIN_EVENT_CALLBACK(virshEventChannelLifecyclePrint), }, }; verify(VIR_DOMAIN_EVENT_ID_LAST == ARRAY_CARDINALITY(vshEventCallbacks)); -- 2.7.4 (Apple Git-66) -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list