[libvirt] [PATCH 00/13] Add support for all QEMU event notifications

This series adds support for (nearly) all QEMU event notifications that are currently supported by the new JSON mode monitor. - Guest reboot - RTC adjustment - Watchdog firing - Block IO errors - Graphics client connect/disconnect It required two new public APIs, and a bunch of extra function callback typedefs which will be shown in each following commit message. This scheme is extensible to support any further events against domains. It does not support events on other types of object (storage, node devices, etc), but those can follow exactly the same API design scheme should we need them in the future. We don't want to unify them at the public API level, since internally they need to dispatch to separate drivers anyway. I'm generally happy with the public API for the events, with the exception of the graphics event which is horribly complex due to the large amount of data. I'm not sure if there's a way to make that event nicer.... The full diffstat for all patches daemon/dispatch.h | 10 daemon/libvirtd.c | 20 daemon/libvirtd.h | 2 daemon/remote.c | 348 ++++++++++++++-- daemon/remote_dispatch_args.h | 2 daemon/remote_dispatch_prototypes.h | 16 daemon/remote_dispatch_table.h | 37 + examples/domain-events/events-c/event-test.c | 168 ++++++- include/libvirt/libvirt.h.in | 106 ++++ src/conf/domain_event.c | 580 ++++++++++++++++++++++++--- src/conf/domain_event.h | 82 +++ src/driver.c | 4 src/driver.h | 18 src/esx/esx_driver.c | 2 src/libvirt.c | 133 +++++- src/libvirt_private.syms | 16 src/libvirt_public.syms | 6 src/lxc/lxc_driver.c | 60 ++ src/opennebula/one_driver.c | 2 src/openvz/openvz_driver.c | 2 src/phyp/phyp_driver.c | 2 src/qemu/qemu_driver.c | 290 +++++++++++++ src/qemu/qemu_monitor.c | 77 +++ src/qemu/qemu_monitor.h | 39 + src/qemu/qemu_monitor_json.c | 144 ++++++ src/remote/remote_driver.c | 459 +++++++++++++++++---- src/remote/remote_protocol.c | 111 +++++ src/remote/remote_protocol.h | 185 ++++++-- src/remote/remote_protocol.x | 65 ++- src/test/test_driver.c | 58 ++ src/uml/uml_driver.c | 2 src/vbox/vbox_tmpl.c | 155 +++++-- src/xen/xen_driver.c | 71 ++- src/xenapi/xenapi_driver.c | 2 34 files changed, 2947 insertions(+), 327 deletions(-) Daniel

The current API for domain events has a number of problems - Only allows for domain lifecycle change events - Does not allow the same callback to be registered multiple times - Does not allow filtering of events to a specific domain This introduces a new more general purpose domain events API typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ ...more events later.. } int virConnectDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, /* Optional, to filter */ int eventID, virConnectDomainEventGenericCallback cb, void *opaque, virFreeCallback freecb); int virConnectDomainEventDeregisterAny(virConnectPtr conn, int callbackID); Since different event types can received different data in the callback, the API is defined with a generic callback. Specific events will each have a custom signature for their callback. Thus when registering an event it is neccessary to cast the callback to the generic signature eg int myDomainEventCallback(virConnectPtr conn, virDomainPtr dom, int event, int detail, void *opaque) { ... } virConnectDomainEventRegisterAny(conn, NULL, VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventCallback) NULL, NULL); The VIR_DOMAIN_EVENT_CALLBACK() macro simply does a "bad" cast to the generic signature * include/libvirt/libvirt.h.in: Define new APIs for registering domain events * src/driver.h: Internal driver entry points for new events APIs * src/libvirt.c: Wire up public API to driver API for events APIs * src/libvirt_public.syms: Export new APIs * src/esx/esx_driver.c, src/lxc/lxc_driver.c, src/opennebula/one_driver.c, src/openvz/openvz_driver.c, src/phyp/phyp_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c, src/uml/uml_driver.c, src/vbox/vbox_tmpl.c, src/xen/xen_driver.c, src/xenapi/xenapi_driver.c: Stub out new API entries --- include/libvirt/libvirt.h.in | 34 +++++++++++ src/driver.h | 14 +++++ src/esx/esx_driver.c | 2 + src/libvirt.c | 133 ++++++++++++++++++++++++++++++++++++++++-- src/libvirt_public.syms | 6 ++ src/lxc/lxc_driver.c | 2 + src/opennebula/one_driver.c | 2 + src/openvz/openvz_driver.c | 2 + src/phyp/phyp_driver.c | 2 + src/qemu/qemu_driver.c | 2 + src/remote/remote_driver.c | 2 + src/test/test_driver.c | 2 + src/uml/uml_driver.c | 2 + src/vbox/vbox_tmpl.c | 2 + src/xen/xen_driver.c | 2 + src/xenapi/xenapi_driver.c | 2 + 16 files changed, 206 insertions(+), 5 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 0d1b5b5..62a51ea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1837,6 +1837,40 @@ int virDomainGetJobInfo(virDomainPtr dom, int virDomainAbortJob(virDomainPtr dom); +/* A generic callback definition. Specific events usually have a customization + * with extra parameters */ +typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, + virDomainPtr dom, + void *opaque); + +/* Use this to cast the event specific callback into the generic one + * for use for virDomainEventRegister */ +#define VIR_DOMAIN_EVENT_CALLBACK(cb) ((virConnectDomainEventGenericCallback)(cb)) + + +typedef enum { + VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ + + /* + * 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. + */ + VIR_DOMAIN_EVENT_ID_LAST +} virDomainEventID; + + +/* Use VIR_DOMAIN_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, /* Optional, to filter */ + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectDomainEventDeregisterAny(virConnectPtr conn, + int callbackID); + #ifdef __cplusplus } #endif diff --git a/src/driver.h b/src/driver.h index a64bba0..805b2b1 100644 --- a/src/driver.h +++ b/src/driver.h @@ -381,6 +381,18 @@ typedef int typedef int (*virDrvDomainAbortJob)(virDomainPtr domain); +typedef int + (*virDrvDomainEventRegisterAny)(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +typedef int + (*virDrvDomainEventDeregisterAny)(virConnectPtr conn, + int callbackID); + /** * _virDriver: * @@ -474,6 +486,8 @@ struct _virDriver { virDrvCPUBaseline cpuBaseline; virDrvDomainGetJobInfo domainGetJobInfo; virDrvDomainAbortJob domainAbortJob; + virDrvDomainEventRegisterAny domainEventRegisterAny; + virDrvDomainEventDeregisterAny domainEventDeregisterAny; }; typedef int diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index ed2cbf7..f8b57b1 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -3411,6 +3411,8 @@ static virDriver esxDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; diff --git a/src/libvirt.c b/src/libvirt.c index 1d9b878..6aabb57 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -9291,8 +9291,12 @@ error: * @opaque: opaque data to pass on to the callback * @freecb: optional function to deallocate opaque when not used anymore * - * Adds a Domain Event Callback. - * Registering for a domain callback will enable delivery of the events + * Adds a callback to receive notifications of domain lifecycle events + * occurring on a connection + * + * Use of this method is no longer recommended. Instead applications + * should try virConnectDomainEventRegisterAny which has a more flexible + * API contract * * The virDomainPtr object handle passed into the callback upon delivery * of an event is only valid for the duration of execution of the callback. @@ -9341,9 +9345,12 @@ error: * @conn: pointer to the connection * @cb: callback to the function handling domain events * - * Removes a Domain Event Callback. - * De-registering for a domain callback will disable - * delivery of this event type + * Removes a callback previously registered with the virConnectDomainEventRegister + * funtion. + * + * Use of this method is no longer recommended. Instead applications + * should try virConnectDomainEventUnregisterAny which has a more flexible + * API contract * * Returns 0 on success, -1 on failure */ @@ -11265,3 +11272,119 @@ error: virDispatchError(conn); return -1; } + + +/** + * virConnectDomainEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventID: the event type to receive + * @cb: callback to the function handling domain 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 domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use + * the VIR_DOMAIN_EVENT_CALLBACK() macro to cast the supplied function pointer + * to match the signature of this method. + * + * The virDomainPtr 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 domain object after the callback + * returns, it shall take a reference to it, by calling virDomainRef. + * The reference can be released once the object is no longer required + * by calling virDomainFree. + * + * 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 virDomainEventUnregisterAny method + * + * Returns a callback identifier on success, -1 on failure + */ +int +virConnectDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) +{ + DEBUG("conn=%p dom=%p, eventID=%d, cb=%p, opaque=%p, freecb=%p", conn, dom, eventID, cb, opaque, freecb); + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (dom != NULL && + !(VIR_IS_CONNECTED_DOMAIN(dom) && dom->conn == conn)) { + virLibConnError(conn, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(conn); + return (-1); + } + if (eventID < 0 || eventID >= VIR_DOMAIN_EVENT_ID_LAST || cb == NULL) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + + if ((conn->driver) && (conn->driver->domainEventRegisterAny)) { + int ret; + ret = conn->driver->domainEventRegisterAny(conn, dom, eventID, cb, opaque, freecb); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} + +/** + * virDomainEventUnregister: + * @conn: pointer to the connection + * @callbackID: the callback identifier + * + * Removes an event callback. The callbackID parameter should be the + * vaule obtained from a previous virDomainEventRegisterAny method. + * + * Returns 0 on success, -1 on failure + */ +int +virConnectDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + DEBUG("conn=%p, callbackID=%d", conn, callbackID); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(NULL, VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return (-1); + } + if (callbackID < 0) { + virLibConnError(conn, VIR_ERR_INVALID_ARG, __FUNCTION__); + goto error; + } + if ((conn->driver) && (conn->driver->domainEventDeregisterAny)) { + int ret; + ret = conn->driver->domainEventDeregisterAny(conn, callbackID); + if (ret < 0) + goto error; + return ret; + } + + virLibConnError(conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); +error: + virDispatchError(conn); + return -1; +} diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 64e7505..b1689ee 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -358,4 +358,10 @@ LIBVIRT_0.7.7 { virDomainAbortJob; } LIBVIRT_0.7.5; +LIBVIRT_0.7.8 { + global: + virConnectDomainEventRegisterAny; + virConnectDomainEventDeregisterAny; +} LIBVIRT_0.7.7; + # .... define new API here using predicted next version number .... diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 35c8659..41d66bf 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -2453,6 +2453,8 @@ static virDriver lxcDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; static virStateDriver lxcStateDriver = { diff --git a/src/opennebula/one_driver.c b/src/opennebula/one_driver.c index 9fc0ada..a3f7c6c 100644 --- a/src/opennebula/one_driver.c +++ b/src/opennebula/one_driver.c @@ -788,6 +788,8 @@ static virDriver oneDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; static virStateDriver oneStateDriver = { diff --git a/src/openvz/openvz_driver.c b/src/openvz/openvz_driver.c index b7bc43b..49f3347 100644 --- a/src/openvz/openvz_driver.c +++ b/src/openvz/openvz_driver.c @@ -1540,6 +1540,8 @@ static virDriver openvzDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; int openvzRegister(void) { diff --git a/src/phyp/phyp_driver.c b/src/phyp/phyp_driver.c index ae210c3..7783182 100644 --- a/src/phyp/phyp_driver.c +++ b/src/phyp/phyp_driver.c @@ -1647,6 +1647,8 @@ virDriver phypDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; int diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 67d9ade..81b4f36 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -9597,6 +9597,8 @@ static virDriver qemuDriver = { qemuCPUBaseline, /* cpuBaseline */ qemuDomainGetJobInfo, /* domainGetJobInfo */ qemuDomainAbortJob, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 11513bd..7013a05 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -9126,6 +9126,8 @@ static virDriver remote_driver = { remoteCPUBaseline, /* cpuBaseline */ remoteDomainGetJobInfo, /* domainGetJobInfo */ remoteDomainAbortJob, /* domainFinishJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; static virNetworkDriver network_driver = { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 7960812..d58086e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5245,6 +5245,8 @@ static virDriver testDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/uml/uml_driver.c b/src/uml/uml_driver.c index eec239f..63c2e98 100644 --- a/src/uml/uml_driver.c +++ b/src/uml/uml_driver.c @@ -1932,6 +1932,8 @@ static virDriver umlDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index b808910..b540f61 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -7066,6 +7066,8 @@ virDriver NAME(Driver) = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 5b9649c..9839858 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1906,6 +1906,8 @@ static virDriver xenUnifiedDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; /** diff --git a/src/xenapi/xenapi_driver.c b/src/xenapi/xenapi_driver.c index a33d791..41908ee 100644 --- a/src/xenapi/xenapi_driver.c +++ b/src/xenapi/xenapi_driver.c @@ -1747,6 +1747,8 @@ static virDriver xenapiDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ + NULL, /* domainEventRegisterAny */ + NULL, /* domainEventDeregisterAny */ }; /** -- 1.6.6.1

On 03/19/2010 09:38 AM, Daniel P. Berrange wrote:
Since different event types can received different data in the callback, the API is defined with a generic callback. Specific events will each have a custom signature for their callback. Thus when registering an event it is neccessary to cast the callback to the generic signature
+/* A generic callback definition. Specific events usually have a customization + * with extra parameters */ +typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, + virDomainPtr dom, + void *opaque);
Should the callback be allowed to return anything, as void*? For example, I've seen some programming paradigms where you can write a generic iterator that marches through callbacks for each item in a set, allowing an early abort of the iteration if any of the callbacks return a non-NULL value (useful for both finding the first element that matches a given criteria, and for gracefully aborting operations on first failure).
+ +/** + * virConnectDomainEventRegister: + * @conn: pointer to the connection + * @dom: pointer to the domain + * @eventID: the event type to receive + * @cb: callback to the function handling domain 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 domain events + * occurring on a domain. + * + * If dom is NULL, then events will be monitored for any domain. If dom + * is non-NULL, then only the specific domain will be monitored + * + * Most types of event have a callback providing a custom set of parameters + * for the event. When registering an event, it is thus neccessary to use
s/neccessary/necessary/ -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Mar 19, 2010 at 11:06:40AM -0600, Eric Blake wrote:
On 03/19/2010 09:38 AM, Daniel P. Berrange wrote:
Since different event types can received different data in the callback, the API is defined with a generic callback. Specific events will each have a custom signature for their callback. Thus when registering an event it is neccessary to cast the callback to the generic signature
+/* A generic callback definition. Specific events usually have a customization + * with extra parameters */ +typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, + virDomainPtr dom, + void *opaque);
Should the callback be allowed to return anything, as void*? For example, I've seen some programming paradigms where you can write a generic iterator that marches through callbacks for each item in a set, allowing an early abort of the iteration if any of the callbacks return a non-NULL value (useful for both finding the first element that matches a given criteria, and for gracefully aborting operations on first failure).
If these callbacks where synchronous with the operation causing them, then I'd agree we need a return value. All the domain events though are reporting something that has already happened, asychronously, so there's nothing that a callback could stop/abort for this. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Fri, Mar 19, 2010 at 03:38:49PM +0000, Daniel P. Berrange wrote:
The current API for domain events has a number of problems
- Only allows for domain lifecycle change events - Does not allow the same callback to be registered multiple times - Does not allow filtering of events to a specific domain
This introduces a new more general purpose domain events API
typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ ...more events later.. }
int virConnectDomainEventRegisterAny(virConnectPtr conn, virDomainPtr dom, /* Optional, to filter */ int eventID, virConnectDomainEventGenericCallback cb, void *opaque, virFreeCallback freecb);
int virConnectDomainEventDeregisterAny(virConnectPtr conn, int callbackID);
Since different event types can received different data in the callback, the API is defined with a generic callback. Specific events will each have a custom signature for their callback. Thus when registering an event it is neccessary to cast the callback to the generic signature
okay, following our previous discussion about events API, the only drawback is that it limits the filtering to domain only kind of objects, but we can add similar entry points if needed on other main types later. [...]
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 0d1b5b5..62a51ea 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1837,6 +1837,40 @@ int virDomainGetJobInfo(virDomainPtr dom, int virDomainAbortJob(virDomainPtr dom);
+/* A generic callback definition. Specific events usually have a customization + * with extra parameters */ +typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, + virDomainPtr dom, + void *opaque); + +/* Use this to cast the event specific callback into the generic one + * for use for virDomainEventRegister */ +#define VIR_DOMAIN_EVENT_CALLBACK(cb) ((virConnectDomainEventGenericCallback)(cb))
cosmetic, would be nicer if that fit in 80 columns
+ +typedef enum { + VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ + + /* + * 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. + */ + VIR_DOMAIN_EVENT_ID_LAST +} virDomainEventID; + + +/* Use VIR_DOMAIN_EVENT_CALLBACK() to cast the 'cb' parameter */ +int virConnectDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, /* Optional, to filter */ + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb); + +int virConnectDomainEventDeregisterAny(virConnectPtr conn, + int callbackID); + #ifdef __cplusplus } #endif [...]
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The virtual box driver was directly accesing the domain events structs instead of using the APIs provided. To prevent this kind of abuse, make the struct definitions private, forcing use of the internal APIs. This requires adding one extra internal API. * src/conf/domain_event.h, src/conf/domain_event.c: Move virDomainEventCallback and virDomainEvent structs into the source file instead of header * src/vbox/vbox_tmpl.c: Use official APIs for dispatching domain events instead of accessing structs directly. --- src/conf/domain_event.c | 22 ++++++++++++++++++++ src/conf/domain_event.h | 16 +------------ src/vbox/vbox_tmpl.c | 51 ++++++++++++++++++---------------------------- 3 files changed, 44 insertions(+), 45 deletions(-) diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index e791cab..b520232 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -35,6 +35,21 @@ virReportErrorHelper(conn, VIR_FROM_THIS, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) +struct _virDomainEventCallback { + virConnectPtr conn; + virConnectDomainEventCallback cb; + void *opaque; + virFreeCallback freecb; + int deleted; +}; + +struct _virDomainEvent { + int id; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; + int type; + int detail; +}; /** * virDomainEventCallbackListFree: @@ -243,6 +258,13 @@ virDomainEventCallbackListAdd(virConnectPtr conn, return 0; } + +int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) +{ + return cbList->count; +} + + void virDomainEventFree(virDomainEventPtr event) { if (!event) diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 8d1b330..5d8faee 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -27,13 +27,6 @@ # include "domain_conf.h" -struct _virDomainEventCallback { - virConnectPtr conn; - virConnectDomainEventCallback cb; - void *opaque; - virFreeCallback freecb; - int deleted; -}; typedef struct _virDomainEventCallback virDomainEventCallback; typedef virDomainEventCallback *virDomainEventCallbackPtr; @@ -69,17 +62,12 @@ int virDomainEventCallbackListMarkDelete(virConnectPtr conn, int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList); +int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); + /** * Dispatching domain events that come in while * in a call / response rpc */ -struct _virDomainEvent { - int id; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; - int type; - int detail; -}; typedef struct _virDomainEvent virDomainEvent; typedef virDomainEvent *virDomainEventPtr; diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index b540f61..e4a7748 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5015,7 +5015,7 @@ static nsresult vboxCallbackOnMachineStateChange (IVirtualBoxCallback *pThis, dom = vboxDomainLookupByUUID(g_pVBoxGlobalData->conn, uuid); if (dom) { - int i = 0; + virDomainEventPtr ev; if (state == MachineState_Starting) { event = VIR_DOMAIN_EVENT_STARTED; @@ -5046,14 +5046,14 @@ static nsresult vboxCallbackOnMachineStateChange (IVirtualBoxCallback *pThis, detail = VIR_DOMAIN_EVENT_STOPPED_SHUTDOWN; } - for (i = 0; i < g_pVBoxGlobalData->domainEventCallbacks->count; i++) { - if (g_pVBoxGlobalData->domainEventCallbacks->callbacks[i]->cb) { - g_pVBoxGlobalData->domainEventCallbacks->callbacks[i]->cb(g_pVBoxGlobalData->conn, - dom, - event, - detail, - NULL); - } + ev = virDomainEventNewFromDom(dom, event, detail); + + if (ev) { + virDomainEventDispatch(ev, + g_pVBoxGlobalData->domainEventCallbacks, + virDomainEventDispatchDefaultFunc, + NULL); + virDomainEventFree(ev); } } } @@ -5136,7 +5136,7 @@ static nsresult vboxCallbackOnMachineRegistered (IVirtualBoxCallback *pThis, dom = vboxDomainLookupByUUID(g_pVBoxGlobalData->conn, uuid); if (dom) { - int i = 0; + virDomainEventPtr ev; /* CURRENT LIMITATION: we never get the VIR_DOMAIN_EVENT_UNDEFINED * event becuase the when the machine is de-registered the call @@ -5152,14 +5152,14 @@ static nsresult vboxCallbackOnMachineRegistered (IVirtualBoxCallback *pThis, detail = VIR_DOMAIN_EVENT_UNDEFINED_REMOVED; } - for (i = 0; i < g_pVBoxGlobalData->domainEventCallbacks->count; i++) { - if (g_pVBoxGlobalData->domainEventCallbacks->callbacks[i]->cb) { - g_pVBoxGlobalData->domainEventCallbacks->callbacks[i]->cb(g_pVBoxGlobalData->conn, - dom, - event, - detail, - NULL); - } + ev = virDomainEventNewFromDom(dom, event, detail); + + if (ev) { + virDomainEventDispatch(ev, + g_pVBoxGlobalData->domainEventCallbacks, + virDomainEventDispatchDefaultFunc, + NULL); + virDomainEventFree(ev); } } } @@ -5406,28 +5406,17 @@ static int vboxDomainEventDeregister (virConnectPtr conn, ret = virDomainEventCallbackListRemove(conn, data->domainEventCallbacks, callback); - if(data->vboxCallback) { + if (data->vboxCallback) { /* check count here of how many times register was called * and only on the last de-register do the un-register call */ - if (data->domainEventCallbacks && (data->domainEventCallbacks->count <= 0)) { - int i = 0; - + if (data->domainEventCallbacks && virDomainEventCallbackListCount(data->domainEventCallbacks) == 0) { data->vboxObj->vtbl->UnregisterCallback(data->vboxObj, data->vboxCallback); VBOX_RELEASE(data->vboxCallback); /* Remove the Event file handle on which we are listening as well */ virEventRemoveHandle(data->fdWatch); data->fdWatch = -1; - - /* iterate and free all the opaque objects using the - * freecb callback provided in vboxDomainEventRegister() - */ - for (i = 0; i < data->domainEventCallbacks->count; i++) { - if (data->domainEventCallbacks->callbacks[i]->freecb) { - data->domainEventCallbacks->callbacks[i]->freecb(data->domainEventCallbacks->callbacks[i]->opaque); - } - } } } -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:50PM +0000, Daniel P. Berrange wrote:
The virtual box driver was directly accesing the domain events structs instead of using the APIs provided. To prevent this kind of abuse, make the struct definitions private, forcing use of the internal APIs. This requires adding one extra internal API.
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The internal domain events APIs are designed to handle the lifecycle events. This needs to be refactored to allow arbitrary new event types to be handled. * The signature of virDomainEventDispatchFunc changes to use virConnectDomainEventGenericCallback instead of the lifecycle event specific virConnectDomainEventCallback * Every registered callback gains a unique ID to allow its removal based on ID, instead of function pointer * Every registered callback gains an 'eventID' to allow callbacks for different types of events to be distinguished * virDomainEventDispatch is adapted to filter out callbacks whose eventID does not match the eventID of the event being dispatched * virDomainEventDispatch is adapted to filter based on the domain name and uuid, if this filter is set for a callback. * virDomainEvent type/detail fields are moved into a union to allow different data fields for other types of events to be added later * src/conf/domain_event.h, src/conf/domain_event.c: Refactor to allow handling of different types of events * src/lxc/lxc_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c, src/xen/xen_driver.c: Change dispatch function signature to use virConnectDomainEventGenericCallback --- src/conf/domain_event.c | 178 +++++++++++++++++++++++++++++++++----------- src/conf/domain_event.h | 5 +- src/lxc/lxc_driver.c | 2 +- src/qemu/qemu_driver.c | 2 +- src/remote/remote_driver.c | 2 +- src/test/test_driver.c | 2 +- src/xen/xen_driver.c | 2 +- 7 files changed, 142 insertions(+), 51 deletions(-) diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index b520232..9d47eeb 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -35,20 +35,36 @@ virReportErrorHelper(conn, VIR_FROM_THIS, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) +struct _virDomainMeta { + int id; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; +}; +typedef struct _virDomainMeta virDomainMeta; +typedef virDomainMeta *virDomainMetaPtr; + struct _virDomainEventCallback { + int callbackID; + int eventID; virConnectPtr conn; - virConnectDomainEventCallback cb; + virDomainMetaPtr dom; + virConnectDomainEventGenericCallback cb; void *opaque; virFreeCallback freecb; int deleted; }; struct _virDomainEvent { - int id; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; - int type; - int detail; + int eventID; + + virDomainMeta dom; + + union { + struct { + int type; + int detail; + } lifecycle; + } data; }; /** @@ -72,6 +88,8 @@ virDomainEventCallbackListFree(virDomainEventCallbackListPtr list) } VIR_FREE(list); } + + /** * virDomainEventCallbackListRemove: * @conn: pointer to the connection @@ -87,8 +105,9 @@ virDomainEventCallbackListRemove(virConnectPtr conn, { int i; for (i = 0 ; i < cbList->count ; i++) { - if(cbList->callbacks[i]->cb == callback && - cbList->callbacks[i]->conn == conn) { + if (cbList->callbacks[i]->cb == VIR_DOMAIN_EVENT_CALLBACK(callback) && + cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE && + cbList->callbacks[i]->conn == conn) { virFreeCallback freecb = cbList->callbacks[i]->freecb; if (freecb) (*freecb)(cbList->callbacks[i]->opaque); @@ -116,6 +135,7 @@ virDomainEventCallbackListRemove(virConnectPtr conn, return -1; } + /** * virDomainEventCallbackListRemoveConn: * @conn: pointer to the connection @@ -131,7 +151,7 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, int old_count = cbList->count; int i; for (i = 0 ; i < cbList->count ; i++) { - if(cbList->callbacks[i]->conn == conn) { + if (cbList->callbacks[i]->conn == conn) { virFreeCallback freecb = cbList->callbacks[i]->freecb; if (freecb) (*freecb)(cbList->callbacks[i]->opaque); @@ -154,14 +174,16 @@ virDomainEventCallbackListRemoveConn(virConnectPtr conn, return 0; } + int virDomainEventCallbackListMarkDelete(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) { int i; for (i = 0 ; i < cbList->count ; i++) { - if (cbList->callbacks[i]->conn == conn && - cbList->callbacks[i]->cb == callback) { + if (cbList->callbacks[i]->cb == VIR_DOMAIN_EVENT_CALLBACK(callback) && + cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE && + cbList->callbacks[i]->conn == conn) { cbList->callbacks[i]->deleted = 1; return 0; } @@ -172,6 +194,7 @@ int virDomainEventCallbackListMarkDelete(virConnectPtr conn, return -1; } + int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) { int old_count = cbList->count; @@ -200,6 +223,7 @@ int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) return 0; } + /** * virDomainEventCallbackListAdd: * @conn: pointer to the connection @@ -217,7 +241,7 @@ virDomainEventCallbackListAdd(virConnectPtr conn, virFreeCallback freecb) { virDomainEventCallbackPtr event; - int n; + int i; /* Check incoming */ if ( !cbList ) { @@ -225,9 +249,10 @@ virDomainEventCallbackListAdd(virConnectPtr conn, } /* check if we already have this callback on our list */ - for (n=0; n < cbList->count; n++) { - if(cbList->callbacks[n]->cb == callback && - conn == cbList->callbacks[n]->conn) { + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->cb == VIR_DOMAIN_EVENT_CALLBACK(callback) && + cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE && + cbList->callbacks[i]->conn == conn) { eventReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", _("event callback already tracked")); return -1; @@ -239,13 +264,13 @@ virDomainEventCallbackListAdd(virConnectPtr conn, return -1; } event->conn = conn; - event->cb = callback; + event->cb = VIR_DOMAIN_EVENT_CALLBACK(callback); + event->eventID = VIR_DOMAIN_EVENT_ID_LIFECYCLE; event->opaque = opaque; event->freecb = freecb; /* Make space on list */ - n = cbList->count; - if (VIR_REALLOC_N(cbList->callbacks, n + 1) < 0) { + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) { virReportOOMError(); VIR_FREE(event); return -1; @@ -253,15 +278,27 @@ virDomainEventCallbackListAdd(virConnectPtr conn, event->conn->refs++; - cbList->callbacks[n] = event; + cbList->callbacks[cbList->count] = event; cbList->count++; - return 0; + + event->callbackID = cbList->nextID++; + + return event->callbackID; } int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) { - return cbList->count; + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + count++; + } + + return count; } @@ -270,7 +307,7 @@ void virDomainEventFree(virDomainEventPtr event) if (!event) return; - VIR_FREE(event->name); + VIR_FREE(event->dom.name); VIR_FREE(event); } @@ -287,9 +324,10 @@ virDomainEventQueuePtr virDomainEventQueueNew(void) return ret; } -virDomainEventPtr virDomainEventNew(int id, const char *name, - const unsigned char *uuid, - int type, int detail) +static virDomainEventPtr virDomainEventNewInternal(int eventID, + int id, + const char *name, + const unsigned char *uuid) { virDomainEventPtr event; @@ -298,15 +336,29 @@ virDomainEventPtr virDomainEventNew(int id, const char *name, return NULL; } - event->type = type; - event->detail = detail; - if (!(event->name = strdup(name))) { + event->eventID = eventID; + if (!(event->dom.name = strdup(name))) { virReportOOMError(); VIR_FREE(event); return NULL; } - event->id = id; - memcpy(event->uuid, uuid, VIR_UUID_BUFLEN); + event->dom.id = id; + memcpy(event->dom.uuid, uuid, VIR_UUID_BUFLEN); + + return event; +} + +virDomainEventPtr virDomainEventNew(int id, const char *name, + const unsigned char *uuid, + int type, int detail) +{ + virDomainEventPtr event = virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_LIFECYCLE, + id, name, uuid); + + if (event) { + event->data.lifecycle.type = type; + event->data.lifecycle.detail = detail; + } return event; } @@ -414,19 +466,57 @@ virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque ATTRIBUTE_UNUSED) { - virDomainPtr dom = virGetDomain(conn, event->name, event->uuid); - if (dom) { - dom->id = event->id; - (*cb)(conn, dom, event->type, event->detail, cbopaque); - virDomainFree(dom); + virDomainPtr dom = virGetDomain(conn, event->dom.name, event->dom.uuid); + if (!dom) + return; + dom->id = event->dom.id; + + switch (event->eventID) { + case VIR_DOMAIN_EVENT_ID_LIFECYCLE: + ((virConnectDomainEventCallback)cb)(conn, dom, + event->data.lifecycle.type, + event->data.lifecycle.detail, + cbopaque); + break; + + default: + VIR_WARN("Unexpected event ID %d", event->eventID); + break; } + + virDomainFree(dom); } +static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) +{ + if (!cb) + return 0; + if (cb->deleted) + return 0; + if (cb->eventID != event->eventID) + return 0; + + if (cb->dom) { + /* Delibrately ignoring 'id' for matching, since that + * will cause problems when a domain switches between + * running & shutoff states */ + + if (STREQ(event->dom.name, cb->dom->name) && + (memcmp(event->dom.uuid, cb->dom->uuid, VIR_UUID_BUFLEN) == 0)) + return 1; + + return 0; + } else { + return 1; + } +} + void virDomainEventDispatch(virDomainEventPtr event, virDomainEventCallbackListPtr callbacks, virDomainEventDispatchFunc dispatch, @@ -439,14 +529,14 @@ void virDomainEventDispatch(virDomainEventPtr event, int cbCount = callbacks->count; for (i = 0 ; i < cbCount ; i++) { - if (callbacks->callbacks[i] && - !callbacks->callbacks[i]->deleted) { - (*dispatch)(callbacks->callbacks[i]->conn, - event, - callbacks->callbacks[i]->cb, - callbacks->callbacks[i]->opaque, - opaque); - } + if (!virDomainEventDispatchMatchCallback(event, callbacks->callbacks[i])) + continue; + + (*dispatch)(callbacks->callbacks[i]->conn, + event, + callbacks->callbacks[i]->cb, + callbacks->callbacks[i]->opaque, + opaque); } } diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 5d8faee..8658631 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -31,6 +31,7 @@ typedef struct _virDomainEventCallback virDomainEventCallback; typedef virDomainEventCallback *virDomainEventCallbackPtr; struct _virDomainEventCallbackList { + unsigned int nextID; unsigned int count; virDomainEventCallbackPtr *callbacks; }; @@ -96,12 +97,12 @@ void virDomainEventQueueFree(virDomainEventQueuePtr queue); typedef void (*virDomainEventDispatchFunc)(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque); void virDomainEventDispatchDefaultFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque); diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 41d66bf..592663d 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1470,7 +1470,7 @@ lxcDomainEventDeregister (virConnectPtr conn, static void lxcDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque) { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 81b4f36..d522c43 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7979,7 +7979,7 @@ qemudDomainEventDeregister (virConnectPtr conn, static void qemuDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque) { diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 7013a05..2c3a70e 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -8891,7 +8891,7 @@ done: static void remoteDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque) { diff --git a/src/test/test_driver.c b/src/test/test_driver.c index d58086e..9c26a03 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5091,7 +5091,7 @@ testDomainEventDeregister (virConnectPtr conn, static void testDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque) { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index 9839858..bf28d43 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -2041,7 +2041,7 @@ xenUnifiedRemoveDomainInfo(xenUnifiedDomainInfoListPtr list, static void xenUnifiedDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, - virConnectDomainEventCallback cb, + virConnectDomainEventGenericCallback cb, void *cbopaque, void *opaque) { -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:51PM +0000, Daniel P. Berrange wrote:
The internal domain events APIs are designed to handle the lifecycle events. This needs to be refactored to allow arbitrary new event types to be handled.
* The signature of virDomainEventDispatchFunc changes to use virConnectDomainEventGenericCallback instead of the lifecycle event specific virConnectDomainEventCallback * Every registered callback gains a unique ID to allow its removal based on ID, instead of function pointer * Every registered callback gains an 'eventID' to allow callbacks for different types of events to be distinguished * virDomainEventDispatch is adapted to filter out callbacks whose eventID does not match the eventID of the event being dispatched * virDomainEventDispatch is adapted to filter based on the domain name and uuid, if this filter is set for a callback. * virDomainEvent type/detail fields are moved into a union to allow different data fields for other types of events to be added later
* src/conf/domain_event.h, src/conf/domain_event.c: Refactor to allow handling of different types of events * src/lxc/lxc_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c, src/xen/xen_driver.c: Change dispatch function signature to use virConnectDomainEventGenericCallback [...] diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index b520232..9d47eeb 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -35,20 +35,36 @@ virReportErrorHelper(conn, VIR_FROM_THIS, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__)
+struct _virDomainMeta { + int id; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; +}; +typedef struct _virDomainMeta virDomainMeta; +typedef virDomainMeta *virDomainMetaPtr; + struct _virDomainEventCallback { + int callbackID; + int eventID; virConnectPtr conn; - virConnectDomainEventCallback cb; + virDomainMetaPtr dom; + virConnectDomainEventGenericCallback cb; void *opaque; virFreeCallback freecb; int deleted; };
struct _virDomainEvent { - int id; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; - int type; - int detail; + int eventID; + + virDomainMeta dom; + + union { + struct { + int type; + int detail; + } lifecycle; + } data; };
/**
Okay, that's logical, and most of the patch derives from that change, [...]
+static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) +{ + if (!cb) + return 0; + if (cb->deleted) + return 0; + if (cb->eventID != event->eventID) + return 0; + + if (cb->dom) { + /* Delibrately ignoring 'id' for matching, since that + * will cause problems when a domain switches between + * running & shutoff states */ + + if (STREQ(event->dom.name, cb->dom->name) && + (memcmp(event->dom.uuid, cb->dom->uuid, VIR_UUID_BUFLEN) == 0)) + return 1;
Do we really need to match for both ?
+ return 0; + } else { + return 1; + } +} + [...]
ACK Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Mon, Mar 22, 2010 at 03:36:34PM +0100, Daniel Veillard wrote:
On Fri, Mar 19, 2010 at 03:38:51PM +0000, Daniel P. Berrange wrote:
The internal domain events APIs are designed to handle the lifecycle events. This needs to be refactored to allow arbitrary new event types to be handled.
* The signature of virDomainEventDispatchFunc changes to use virConnectDomainEventGenericCallback instead of the lifecycle event specific virConnectDomainEventCallback * Every registered callback gains a unique ID to allow its removal based on ID, instead of function pointer * Every registered callback gains an 'eventID' to allow callbacks for different types of events to be distinguished * virDomainEventDispatch is adapted to filter out callbacks whose eventID does not match the eventID of the event being dispatched * virDomainEventDispatch is adapted to filter based on the domain name and uuid, if this filter is set for a callback. * virDomainEvent type/detail fields are moved into a union to allow different data fields for other types of events to be added later
* src/conf/domain_event.h, src/conf/domain_event.c: Refactor to allow handling of different types of events * src/lxc/lxc_driver.c, src/qemu/qemu_driver.c, src/remote/remote_driver.c, src/test/test_driver.c, src/xen/xen_driver.c: Change dispatch function signature to use virConnectDomainEventGenericCallback [...] diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index b520232..9d47eeb 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -35,20 +35,36 @@ virReportErrorHelper(conn, VIR_FROM_THIS, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__)
+struct _virDomainMeta { + int id; + char *name; + unsigned char uuid[VIR_UUID_BUFLEN]; +}; +typedef struct _virDomainMeta virDomainMeta; +typedef virDomainMeta *virDomainMetaPtr; + struct _virDomainEventCallback { + int callbackID; + int eventID; virConnectPtr conn; - virConnectDomainEventCallback cb; + virDomainMetaPtr dom; + virConnectDomainEventGenericCallback cb; void *opaque; virFreeCallback freecb; int deleted; };
struct _virDomainEvent { - int id; - char *name; - unsigned char uuid[VIR_UUID_BUFLEN]; - int type; - int detail; + int eventID; + + virDomainMeta dom; + + union { + struct { + int type; + int detail; + } lifecycle; + } data; };
/**
Okay, that's logical, and most of the patch derives from that change,
[...]
+static int virDomainEventDispatchMatchCallback(virDomainEventPtr event, + virDomainEventCallbackPtr cb) +{ + if (!cb) + return 0; + if (cb->deleted) + return 0; + if (cb->eventID != event->eventID) + return 0; + + if (cb->dom) { + /* Delibrately ignoring 'id' for matching, since that + * will cause problems when a domain switches between + * running & shutoff states */ + + if (STREQ(event->dom.name, cb->dom->name) && + (memcmp(event->dom.uuid, cb->dom->uuid, VIR_UUID_BUFLEN) == 0)) + return 1;
Do we really need to match for both ?
It might be better to check for just UUID actually, since you can get the crazy situation where Xen renames a guest to 'migrating-XXXX' during migration. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The current internal domain events API tracks callbacks based on the function pointer, and only supports lifecycle events. This adds new internal APIs for registering callbacks for other event types. These new APIs are postfixed with the word 'ID' to indicate that they operated based on event ID, instead of hardcoded to lifecycle events * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Add new APIs for handling callbacks for non-lifecycle events --- src/conf/domain_event.c | 169 ++++++++++++++++++++++++++++++++++++++++++--- src/conf/domain_event.h | 28 +++++++- src/libvirt_private.syms | 6 ++ 3 files changed, 190 insertions(+), 13 deletions(-) diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 9d47eeb..3bd570e 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -137,6 +137,51 @@ virDomainEventCallbackListRemove(virConnectPtr conn, /** + * virDomainEventCallbackListRemoveID: + * @conn: pointer to the connection + * @cbList: the list + * @callback: the callback to remove + * + * Internal function to remove a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + virFreeCallback freecb = cbList->callbacks[i]->freecb; + if (freecb) + (*freecb)(cbList->callbacks[i]->opaque); + virUnrefConnect(cbList->callbacks[i]->conn); + VIR_FREE(cbList->callbacks[i]); + + if (i < (cbList->count - 1)) + memmove(cbList->callbacks + i, + cbList->callbacks + i + 1, + sizeof(*(cbList->callbacks)) * + (cbList->count - (i + 1))); + + if (VIR_REALLOC_N(cbList->callbacks, + cbList->count - 1) < 0) { + ; /* Failure to reduce memory allocation isn't fatal */ + } + cbList->count--; + + return 0; + } + } + + eventReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for removal")); + return -1; +} + + +/** * virDomainEventCallbackListRemoveConn: * @conn: pointer to the connection * @cbList: the list @@ -195,6 +240,25 @@ int virDomainEventCallbackListMarkDelete(virConnectPtr conn, } +int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int i; + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) { + cbList->callbacks[i]->deleted = 1; + return 0; + } + } + + eventReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s", + _("could not find event callback for deletion")); + return -1; +} + + int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList) { int old_count = cbList->count; @@ -240,6 +304,32 @@ virDomainEventCallbackListAdd(virConnectPtr conn, void *opaque, virFreeCallback freecb) { + return virDomainEventCallbackListAddID(conn, cbList, NULL, + VIR_DOMAIN_EVENT_ID_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(callback), + opaque, freecb); +} + + +/** + * virDomainEventCallbackListAddID: + * @conn: pointer to the connection + * @cbList: the list + * @eventID: the event ID + * @callback: the callback to add + * @opaque: opaque data tio pass to callback + * + * Internal function to add a callback from a virDomainEventCallbackListPtr + */ +int +virDomainEventCallbackListAddID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ virDomainEventCallbackPtr event; int i; @@ -259,23 +349,27 @@ virDomainEventCallbackListAdd(virConnectPtr conn, } } /* Allocate new event */ - if (VIR_ALLOC(event) < 0) { - virReportOOMError(); - return -1; - } + if (VIR_ALLOC(event) < 0) + goto no_memory; event->conn = conn; - event->cb = VIR_DOMAIN_EVENT_CALLBACK(callback); - event->eventID = VIR_DOMAIN_EVENT_ID_LIFECYCLE; + event->cb = callback; + event->eventID = eventID; event->opaque = opaque; event->freecb = freecb; - /* Make space on list */ - if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) { - virReportOOMError(); - VIR_FREE(event); - return -1; + if (dom) { + if (VIR_ALLOC(event->dom) < 0) + goto no_memory; + if (!(event->dom->name = strdup(dom->name))) + goto no_memory; + memcpy(event->dom->uuid, dom->uuid, VIR_UUID_BUFLEN); + event->dom->id = dom->id; } + /* Make space on list */ + if (VIR_REALLOC_N(cbList->callbacks, cbList->count + 1) < 0) + goto no_memory; + event->conn->refs++; cbList->callbacks[cbList->count] = event; @@ -284,6 +378,56 @@ virDomainEventCallbackListAdd(virConnectPtr conn, event->callbackID = cbList->nextID++; return event->callbackID; + +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event); + return -1; +} + + +int virDomainEventCallbackListCountID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int eventID) +{ + int i; + int count = 0; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (cbList->callbacks[i]->eventID == eventID && + cbList->callbacks[i]->conn == conn) + count++; + } + + return count; +} + + +int virDomainEventCallbackListEventID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) +{ + int i; + + for (i = 0 ; i < cbList->count ; i++) { + if (cbList->callbacks[i]->deleted) + continue; + + if (cbList->callbacks[i]->callbackID == callbackID && + cbList->callbacks[i]->conn == conn) + return cbList->callbacks[i]->eventID; + } + + return -1; } @@ -295,7 +439,8 @@ int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList) for (i = 0 ; i < cbList->count ; i++) { if (cbList->callbacks[i]->deleted) continue; - count++; + if (cbList->callbacks[i]->eventID == VIR_DOMAIN_EVENT_ID_LIFECYCLE) + count++; } return count; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 8658631..5578cd9 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -46,24 +46,50 @@ int virDomainEventCallbackListAdd(virConnectPtr conn, void *opaque, virFreeCallback freecb) ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListAddID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListRemove(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); - +int virDomainEventCallbackListRemoveID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); int virDomainEventCallbackListRemoveConn(virConnectPtr conn, virDomainEventCallbackListPtr cbList) ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListMarkDelete(virConnectPtr conn, virDomainEventCallbackListPtr cbList, virConnectDomainEventCallback callback) ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListMarkDeleteID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); + int virDomainEventCallbackListPurgeMarked(virDomainEventCallbackListPtr cbList); int virDomainEventCallbackListCount(virDomainEventCallbackListPtr cbList); +int virDomainEventCallbackListCountID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int eventID) + ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListEventID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + int callbackID) + ATTRIBUTE_NONNULL(1); /** * Dispatching domain events that come in while diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c5ee23d..2d54fb1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -195,11 +195,17 @@ virDomainClockOffsetTypeFromString; # domain_event.h virDomainEventCallbackListAdd; +virDomainEventCallbackListAddID; virDomainEventCallbackListFree; virDomainEventCallbackListRemove; +virDomainEventCallbackListRemoveID; virDomainEventCallbackListRemoveConn; virDomainEventCallbackListMarkDelete; +virDomainEventCallbackListMarkDeleteID; virDomainEventCallbackListPurgeMarked; +virDomainEventCallbackListCount; +virDomainEventCallbackListCountID; +virDomainEventCallbackListEventID; virDomainEventQueueNew; virDomainEventQueueFree; virDomainEventQueuePop; -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:52PM +0000, Daniel P. Berrange wrote:
The current internal domain events API tracks callbacks based on the function pointer, and only supports lifecycle events. This adds new internal APIs for registering callbacks for other event types. These new APIs are postfixed with the word 'ID' to indicate that they operated based on event ID, instead of hardcoded to lifecycle events
* src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Add new APIs for handling callbacks for non-lifecycle events --- src/conf/domain_event.c | 169 ++++++++++++++++++++++++++++++++++++++++++--- src/conf/domain_event.h | 28 +++++++- src/libvirt_private.syms | 6 ++ 3 files changed, 190 insertions(+), 13 deletions(-)
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c [...] +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event);
Probably worth having an event freeing routine of its own [...]
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 8658631..5578cd9 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -46,24 +46,50 @@ int virDomainEventCallbackListAdd(virConnectPtr conn, void *opaque, virFreeCallback freecb) ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListAddID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1);
Hum, parameters 0 (conn), 4 (cb) could also be passed in NONNULL list I think ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Mon, Mar 22, 2010 at 03:42:52PM +0100, Daniel Veillard wrote:
On Fri, Mar 19, 2010 at 03:38:52PM +0000, Daniel P. Berrange wrote:
The current internal domain events API tracks callbacks based on the function pointer, and only supports lifecycle events. This adds new internal APIs for registering callbacks for other event types. These new APIs are postfixed with the word 'ID' to indicate that they operated based on event ID, instead of hardcoded to lifecycle events
* src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Add new APIs for handling callbacks for non-lifecycle events --- src/conf/domain_event.c | 169 ++++++++++++++++++++++++++++++++++++++++++--- src/conf/domain_event.h | 28 +++++++- src/libvirt_private.syms | 6 ++ 3 files changed, 190 insertions(+), 13 deletions(-)
diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c [...] +no_memory: + virReportOOMError(); + + if (event) { + if (event->dom) + VIR_FREE(event->dom->name); + VIR_FREE(event->dom); + } + VIR_FREE(event);
Probably worth having an event freeing routine of its own
[...]
diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 8658631..5578cd9 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -46,24 +46,50 @@ int virDomainEventCallbackListAdd(virConnectPtr conn, void *opaque, virFreeCallback freecb) ATTRIBUTE_NONNULL(1); +int virDomainEventCallbackListAddID(virConnectPtr conn, + virDomainEventCallbackListPtr cbList, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback cb, + void *opaque, + virFreeCallback freecb) + ATTRIBUTE_NONNULL(1);
Hum, parameters 0 (conn), 4 (cb) could also be passed in NONNULL list I think
Parameter indexes start from '1', so this is checking 'conn' already. I could add the callback arg too. Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

The libvirtd daemon impl will need to switch over to using the new event APIs. To make this simpler, ensure all drivers currently providing events support both the new APIs and old APIs. * src/lxc/lxc_driver.c, src/qemu/qemu_driver.c, src/test/test_driver.c, src/vbox/vbox_tmpl.c, src/xen/xen_driver.c: Implement the new virConnectDomainEvent(Dereg|Reg)isterAny driver entry points --- src/lxc/lxc_driver.c | 60 +++++++++++++++++++++++---- src/qemu/qemu_driver.c | 64 ++++++++++++++++++++++++----- src/test/test_driver.c | 58 ++++++++++++++++++++++---- src/vbox/vbox_tmpl.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ src/xen/xen_driver.c | 71 ++++++++++++++++++++++++++++---- 5 files changed, 325 insertions(+), 34 deletions(-) diff --git a/src/lxc/lxc_driver.c b/src/lxc/lxc_driver.c index 592663d..bf10f66 100644 --- a/src/lxc/lxc_driver.c +++ b/src/lxc/lxc_driver.c @@ -1433,10 +1433,10 @@ cleanup: static int -lxcDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - virFreeCallback freecb) +lxcDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) { lxc_driver_t *driver = conn->privateData; int ret; @@ -1449,9 +1449,10 @@ lxcDomainEventRegister (virConnectPtr conn, return ret; } + static int -lxcDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +lxcDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { lxc_driver_t *driver = conn->privateData; int ret; @@ -1468,6 +1469,49 @@ lxcDomainEventDeregister (virConnectPtr conn, return ret; } + +static int +lxcDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + lxc_driver_t *driver = conn->privateData; + int ret; + + lxcDriverLock(driver); + ret = virDomainEventCallbackListAddID(conn, + driver->domainEventCallbacks, + dom, eventID, + callback, opaque, freecb); + lxcDriverUnlock(driver); + + return ret; +} + + +static int +lxcDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + lxc_driver_t *driver = conn->privateData; + int ret; + + lxcDriverLock(driver); + if (driver->domainEventDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, driver->domainEventCallbacks, + callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, driver->domainEventCallbacks, + callbackID); + lxcDriverUnlock(driver); + + return ret; +} + + static void lxcDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, @@ -2453,8 +2497,8 @@ static virDriver lxcDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ + lxcDomainEventRegisterAny, /* domainEventRegisterAny */ + lxcDomainEventDeregisterAny, /* domainEventDeregisterAny */ }; static virStateDriver lxcStateDriver = { diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index d522c43..a8af7b4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -7942,10 +7942,10 @@ cleanup: static int -qemudDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - virFreeCallback freecb) +qemuDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) { struct qemud_driver *driver = conn->privateData; int ret; @@ -7958,9 +7958,10 @@ qemudDomainEventRegister (virConnectPtr conn, return ret; } + static int -qemudDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +qemuDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { struct qemud_driver *driver = conn->privateData; int ret; @@ -7977,6 +7978,49 @@ qemudDomainEventDeregister (virConnectPtr conn, return ret; } + +static int +qemuDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + struct qemud_driver *driver = conn->privateData; + int ret; + + qemuDriverLock(driver); + ret = virDomainEventCallbackListAddID(conn, + driver->domainEventCallbacks, + dom, eventID, + callback, opaque, freecb); + qemuDriverUnlock(driver); + + return ret; +} + + +static int +qemuDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct qemud_driver *driver = conn->privateData; + int ret; + + qemuDriverLock(driver); + if (driver->domainEventDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, driver->domainEventCallbacks, + callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, driver->domainEventCallbacks, + callbackID); + qemuDriverUnlock(driver); + + return ret; +} + + static void qemuDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, @@ -9581,8 +9625,8 @@ static virDriver qemuDriver = { qemudDomainMemoryPeek, /* domainMemoryPeek */ nodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ nodeGetFreeMemory, /* getFreeMemory */ - qemudDomainEventRegister, /* domainEventRegister */ - qemudDomainEventDeregister, /* domainEventDeregister */ + qemuDomainEventRegister, /* domainEventRegister */ + qemuDomainEventDeregister, /* domainEventDeregister */ qemudDomainMigratePrepare2, /* domainMigratePrepare2 */ qemudDomainMigrateFinish2, /* domainMigrateFinish2 */ qemudNodeDeviceDettach, /* nodeDeviceDettach */ @@ -9597,8 +9641,8 @@ static virDriver qemuDriver = { qemuCPUBaseline, /* cpuBaseline */ qemuDomainGetJobInfo, /* domainGetJobInfo */ qemuDomainAbortJob, /* domainAbortJob */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ + qemuDomainEventRegisterAny, /* domainEventRegisterAny */ + qemuDomainEventDeregisterAny, /* domainEventDeregisterAny */ }; diff --git a/src/test/test_driver.c b/src/test/test_driver.c index 9c26a03..8977d4e 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -5054,10 +5054,10 @@ out: /* Domain event implementations */ static int -testDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - virFreeCallback freecb) +testDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) { testConnPtr driver = conn->privateData; int ret; @@ -5070,9 +5070,10 @@ testDomainEventRegister (virConnectPtr conn, return ret; } + static int -testDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +testDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { testConnPtr driver = conn->privateData; int ret; @@ -5089,6 +5090,47 @@ testDomainEventDeregister (virConnectPtr conn, return ret; } + +static int +testDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + ret = virDomainEventCallbackListAddID(conn, driver->domainEventCallbacks, + dom, eventID, + callback, opaque, freecb); + testDriverUnlock(driver); + + return ret; +} + +static int +testDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + testConnPtr driver = conn->privateData; + int ret; + + testDriverLock(driver); + if (driver->domainEventDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, driver->domainEventCallbacks, + callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, driver->domainEventCallbacks, + callbackID); + testDriverUnlock(driver); + + return ret; +} + + static void testDomainEventDispatchFunc(virConnectPtr conn, virDomainEventPtr event, virConnectDomainEventGenericCallback cb, @@ -5245,8 +5287,8 @@ static virDriver testDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ + testDomainEventRegisterAny, /* domainEventRegisterAny */ + testDomainEventDeregisterAny, /* domainEventDeregisterAny */ }; static virNetworkDriver testNetworkDriver = { diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c index e4a7748..22f9071 100644 --- a/src/vbox/vbox_tmpl.c +++ b/src/vbox/vbox_tmpl.c @@ -5425,6 +5425,107 @@ static int vboxDomainEventDeregister (virConnectPtr conn, return ret; } +static int vboxDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) { + VBOX_OBJECT_CHECK(conn, int, -1); + int vboxRet = -1; + nsresult rc; + + /* Locking has to be there as callbacks are not + * really fully thread safe + */ + vboxDriverLock(data); + + if (data->vboxCallback == NULL) { + data->vboxCallback = vboxAllocCallbackObj(); + if (data->vboxCallback != NULL) { + rc = data->vboxObj->vtbl->RegisterCallback(data->vboxObj, data->vboxCallback); + if (NS_SUCCEEDED(rc)) { + vboxRet = 0; + } + } + } else { + vboxRet = 0; + } + + /* Get the vbox file handle and add a event handle to it + * so that the events can be passed down to the user + */ + if (vboxRet == 0) { + if (data->fdWatch < 0) { + PRInt32 vboxFileHandle; + vboxFileHandle = data->vboxQueue->vtbl->GetEventQueueSelectFD(data->vboxQueue); + + data->fdWatch = virEventAddHandle(vboxFileHandle, VIR_EVENT_HANDLE_READABLE, vboxReadCallback, NULL, NULL); + } + + if (data->fdWatch >= 0) { + /* Once a callback is registered with virtualbox, use a list + * to store the callbacks registered with libvirt so that + * later you can iterate over them + */ + + ret = virDomainEventCallbackListAddID(conn, data->domainEventCallbacks, + dom, eventID, + callback, opaque, freecb); + DEBUG("virDomainEventCallbackListAddID (ret = %d) ( conn: %p, " + "data->domainEventCallbacks: %p, callback: %p, opaque: %p, " + "freecb: %p )", ret, conn, data->domainEventCallbacks, callback, + opaque, freecb); + } + } + + vboxDriverUnlock(data); + + if (ret >= 0) { + return ret; + } else { + if (data->vboxObj && data->vboxCallback) { + data->vboxObj->vtbl->UnregisterCallback(data->vboxObj, data->vboxCallback); + } + return -1; + } +} + +static int vboxDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) { + VBOX_OBJECT_CHECK(conn, int, -1); + + /* Locking has to be there as callbacks are not + * really fully thread safe + */ + vboxDriverLock(data); + + if (data->domainEventDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, data->domainEventCallbacks, + callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, data->domainEventCallbacks, + callbackID); + + if (data->vboxCallback) { + /* check count here of how many times register was called + * and only on the last de-register do the un-register call + */ + if (data->domainEventCallbacks && virDomainEventCallbackListCount(data->domainEventCallbacks) == 0) { + data->vboxObj->vtbl->UnregisterCallback(data->vboxObj, data->vboxCallback); + VBOX_RELEASE(data->vboxCallback); + + /* Remove the Event file handle on which we are listening as well */ + virEventRemoveHandle(data->fdWatch); + data->fdWatch = -1; + } + } + + vboxDriverUnlock(data); + + return ret; +} + #endif /* !(VBOX_API_VERSION == 2002) */ /** @@ -7055,8 +7156,13 @@ virDriver NAME(Driver) = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ +#if VBOX_API_VERSION == 2002 NULL, /* domainEventRegisterAny */ NULL, /* domainEventDeregisterAny */ +#else + vboxDomainEventRegisterAny, /* domainEventRegisterAny */ + vboxDomainEventDeregisterAny, /* domainEventDeregisterAny */ +#endif }; virNetworkDriver NAME(NetworkDriver) = { diff --git a/src/xen/xen_driver.c b/src/xen/xen_driver.c index bf28d43..984d72b 100644 --- a/src/xen/xen_driver.c +++ b/src/xen/xen_driver.c @@ -1656,11 +1656,12 @@ xenUnifiedNodeGetFreeMemory (virConnectPtr conn) return(0); } + static int -xenUnifiedDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - void (*freefunc)(void *)) +xenUnifiedDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freefunc) { GET_PRIVATE (conn); @@ -1680,9 +1681,10 @@ xenUnifiedDomainEventRegister (virConnectPtr conn, return (ret); } + static int -xenUnifiedDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +xenUnifiedDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { int ret; GET_PRIVATE (conn); @@ -1707,6 +1709,59 @@ xenUnifiedDomainEventDeregister (virConnectPtr conn, static int +xenUnifiedDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freefunc) +{ + GET_PRIVATE (conn); + + int ret; + xenUnifiedLock(priv); + + if (priv->xsWatch == -1) { + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); + return -1; + } + + ret = virDomainEventCallbackListAddID(conn, priv->domainEventCallbacks, + dom, eventID, + callback, opaque, freefunc); + + xenUnifiedUnlock(priv); + return (ret); +} + +static int +xenUnifiedDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + int ret; + GET_PRIVATE (conn); + xenUnifiedLock(priv); + + if (priv->xsWatch == -1) { + xenUnifiedError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); + xenUnifiedUnlock(priv); + return -1; + } + + if (priv->domainEventDispatching) + ret = virDomainEventCallbackListMarkDeleteID(conn, priv->domainEventCallbacks, + callbackID); + else + ret = virDomainEventCallbackListRemoveID(conn, priv->domainEventCallbacks, + callbackID); + + xenUnifiedUnlock(priv); + return ret; +} + + +static int xenUnifiedNodeDeviceGetPciInfo (virNodeDevicePtr dev, unsigned *domain, unsigned *bus, @@ -1906,8 +1961,8 @@ static virDriver xenUnifiedDriver = { NULL, /* cpuBaseline */ NULL, /* domainGetJobInfo */ NULL, /* domainAbortJob */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ + xenUnifiedDomainEventRegisterAny, /* domainEventRegisterAny */ + xenUnifiedDomainEventDeregisterAny, /* domainEventDeregisterAny */ }; /** -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:53PM +0000, Daniel P. Berrange wrote:
The libvirtd daemon impl will need to switch over to using the new event APIs. To make this simpler, ensure all drivers currently providing events support both the new APIs and old APIs.
ACK, quite a bit of reformatting too, it's also surprizing that the vbox implementation need to be more complex, but this looks fine, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this - Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach. * daemon/dispatch.h: Don't export remoteRelayDomainEvent since it is no longer needed for unregistering callbacks, instead the unique callback ID is used * daemon/libvirtd.c, daemon/libvirtd.h: Track and unregister callbacks based on callback ID, instead of function pointer * daemon/remote.c: Switch over to using virConnectDomainEventRegisterAny instead of legacy virConnectDomainEventRegister function. Refactor remoteDispatchDomainEventSend() to cope with arbitrary event types * src/driver.h, src/driver.c: Move verify() call into source file instead of header, to avoid polluting the global namespace with the verify function name * src/remote/remote_driver.c: Implement new APIs for event registration. Refactor processCallDispatchMessage() to cope with arbitrary incoming event types. Merge remoteDomainQueueEvent() into processCallDispatchMessage() to avoid duplication of code. Rename remoteDomainReadEvent() to remoteDomainReadEventLifecycle() * src/remote/remote_protocol.x: Define wire format for the new virConnectDomainEventRegisterAny and virConnectDomainEventDeregisterAny functions --- daemon/dispatch.h | 10 -- daemon/libvirtd.c | 20 +++- daemon/libvirtd.h | 2 +- daemon/remote.c | 160 +++++++++++++++++++----- daemon/remote_dispatch_args.h | 2 + daemon/remote_dispatch_prototypes.h | 16 +++ daemon/remote_dispatch_table.h | 10 ++ src/driver.c | 4 + src/driver.h | 4 - src/remote/remote_driver.c | 240 +++++++++++++++++++++++----------- src/remote/remote_protocol.c | 18 +++ src/remote/remote_protocol.h | 104 +++++++++------- src/remote/remote_protocol.x | 13 ++- 13 files changed, 429 insertions(+), 174 deletions(-) diff --git a/daemon/dispatch.h b/daemon/dispatch.h index 1371b05..85f8fc3 100644 --- a/daemon/dispatch.h +++ b/daemon/dispatch.h @@ -60,16 +60,6 @@ remoteSerializeStreamError(struct qemud_client *client, int proc, int serial); -/* Having this here is dubious. It should be in remote.h - * but qemud.c shouldn't depend on that header directly. - * Refactor this later to deal with this properly. - */ -int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainPtr dom, - int event, - int detail, - void *opaque); - int remoteSendStreamData(struct qemud_client *client, diff --git a/daemon/libvirtd.c b/daemon/libvirtd.c index d59a2ab..0d89c53 100644 --- a/daemon/libvirtd.c +++ b/daemon/libvirtd.c @@ -1275,6 +1275,7 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket socklen_t addrlen = (socklen_t) (sizeof addr); struct qemud_client *client; int no_slow_start = 1; + int i; if ((fd = accept(sock->fd, (struct sockaddr *)&addr, &addrlen)) < 0) { char ebuf[1024]; @@ -1346,6 +1347,10 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket memcpy (&client->addr, &addr, sizeof addr); client->addrlen = addrlen; + for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { + client->domainEventCallbackID[i] = -1; + } + /* Prepare one for packet receive */ if (VIR_ALLOC(client->rx) < 0) goto cleanup; @@ -1415,7 +1420,6 @@ static int qemudDispatchServer(struct qemud_server *server, struct qemud_socket if (server->nclients > server->nactiveworkers && server->nactiveworkers < server->nworkers) { - int i; for (i = 0 ; i < server->nworkers ; i++) { if (!server->workers[i].hasThread) { if (qemudStartWorker(server, &server->workers[i]) < 0) @@ -1454,9 +1458,17 @@ void qemudDispatchClientFailure(struct qemud_client *client) { } /* Deregister event delivery callback */ - if (client->conn && client->domain_events_registered) { - DEBUG0("Deregistering to relay remote events"); - virConnectDomainEventDeregister(client->conn, remoteRelayDomainEvent); + if (client->conn) { + int i; + + for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++) { + if (client->domainEventCallbackID[i] != -1) { + DEBUG("Deregistering to relay remote events %d", i); + virConnectDomainEventDeregisterAny(client->conn, + client->domainEventCallbackID[i]); + } + client->domainEventCallbackID[i] = -1; + } } #if HAVE_SASL diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index d30fcd7..d292681 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -177,7 +177,7 @@ struct qemud_client { int watch; unsigned int readonly :1; unsigned int closing :1; - unsigned int domain_events_registered :1; + int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST]; struct sockaddr_storage addr; socklen_t addrlen; diff --git a/daemon/remote.c b/daemon/remote.c index 7c4339f..fd3aa4e 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -94,36 +94,48 @@ const dispatch_data const *remoteGetDispatchData(int proc) /* Prototypes */ static void remoteDispatchDomainEventSend (struct qemud_client *client, - remote_domain_event_msg *data); + int procnr, + xdrproc_t proc, + void *data); -int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainPtr dom, - int event, - int detail, - void *opaque) +static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int event, + int detail, + void *opaque) { struct qemud_client *client = opaque; - REMOTE_DEBUG("Relaying domain event %d %d", event, detail); + remote_domain_event_msg data; - if (client) { - remote_domain_event_msg data; + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain lifecycle event %d %d", event, detail); - virMutexLock(&client->lock); + virMutexLock(&client->lock); - /* build return data */ - memset(&data, 0, sizeof data); - make_nonnull_domain (&data.dom, dom); - data.event = event; - data.detail = detail; + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + data.event = event; + data.detail = detail; - remoteDispatchDomainEventSend (client, &data); + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT, + (xdrproc_t)xdr_remote_domain_event_msg, &data); + + virMutexUnlock(&client->lock); - virMutexUnlock(&client->lock); - } return 0; } +static virConnectDomainEventGenericCallback domainEventCallbacks[VIR_DOMAIN_EVENT_ID_LAST] = { + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), +}; + +verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); + /*----- Functions. -----*/ static int @@ -4818,18 +4830,24 @@ remoteDispatchDomainEventsRegister (struct qemud_server *server ATTRIBUTE_UNUSED remote_domain_events_register_ret *ret ATTRIBUTE_UNUSED) { CHECK_CONN(client); + int callbackID; + + if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] != -1) { + remoteDispatchFormatError(rerr, _("domain event %d already registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE); + return -1; + } - if (virConnectDomainEventRegister(conn, - remoteRelayDomainEvent, - client, NULL) < 0) { + if ((callbackID = virConnectDomainEventRegisterAny(conn, + NULL, + VIR_DOMAIN_EVENT_ID_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), + client, NULL)) < 0) { remoteDispatchConnError(rerr, conn); return -1; } - if (ret) - ret->cb_registered = 1; + client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = callbackID; - client->domain_events_registered = 1; return 0; } @@ -4844,21 +4862,26 @@ remoteDispatchDomainEventsDeregister (struct qemud_server *server ATTRIBUTE_UNUS { CHECK_CONN(client); - if (virConnectDomainEventDeregister(conn, remoteRelayDomainEvent) < 0) { - remoteDispatchConnError(rerr, conn); + if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] == -1) { + remoteDispatchFormatError(rerr, _("domain event %d not registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE); return -1; } - if (ret) - ret->cb_registered = 0; + if (virConnectDomainEventDeregisterAny(conn, + client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0) { + remoteDispatchConnError(rerr, conn); + return -1; + } - client->domain_events_registered = 0; + client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1; return 0; } static void remoteDispatchDomainEventSend (struct qemud_client *client, - remote_domain_event_msg *data) + int procnr, + xdrproc_t proc, + void *data) { struct qemud_client_message *msg = NULL; XDR xdr; @@ -4869,7 +4892,7 @@ remoteDispatchDomainEventSend (struct qemud_client *client, msg->hdr.prog = REMOTE_PROGRAM; msg->hdr.vers = REMOTE_PROTOCOL_VERSION; - msg->hdr.proc = REMOTE_PROC_DOMAIN_EVENT; + msg->hdr.proc = procnr; msg->hdr.type = REMOTE_MESSAGE; msg->hdr.serial = 1; msg->hdr.status = REMOTE_OK; @@ -4887,8 +4910,10 @@ remoteDispatchDomainEventSend (struct qemud_client *client, if (xdr_setpos (&xdr, msg->bufferOffset) == 0) goto xdr_error; - if (!xdr_remote_domain_event_msg(&xdr, data)) + if (!(proc)(&xdr, data)) { + VIR_WARN("Failed to serialize domain event %d", procnr); goto xdr_error; + } /* Update length word to include payload*/ len = msg->bufferOffset = xdr_getpos (&xdr); @@ -5463,6 +5488,77 @@ remoteDispatchDomainAbortJob (struct qemud_server *server ATTRIBUTE_UNUSED, } +static int +remoteDispatchDomainEventsRegisterAny (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr ATTRIBUTE_UNUSED, + remote_domain_events_register_any_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + CHECK_CONN(client); + int callbackID; + + if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || + args->eventID < 0) { + remoteDispatchFormatError(rerr, _("unsupported event ID %d"), args->eventID); + return -1; + } + + if (client->domainEventCallbackID[args->eventID] != -1) { + remoteDispatchFormatError(rerr, _("domain event %d already registered"), args->eventID); + return -1; + } + + if ((callbackID = virConnectDomainEventRegisterAny(conn, + NULL, + args->eventID, + domainEventCallbacks[args->eventID], + client, NULL)) < 0) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + client->domainEventCallbackID[args->eventID] = callbackID; + + return 0; +} + +static int +remoteDispatchDomainEventsDeregisterAny (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + virConnectPtr conn, + remote_message_header *hdr ATTRIBUTE_UNUSED, + remote_error *rerr ATTRIBUTE_UNUSED, + remote_domain_events_deregister_any_args *args, + void *ret ATTRIBUTE_UNUSED) +{ + CHECK_CONN(client); + int callbackID = -1; + + if (args->eventID >= VIR_DOMAIN_EVENT_ID_LAST || + args->eventID < 0) { + remoteDispatchFormatError(rerr, _("unsupported event ID %d"), args->eventID); + return -1; + } + + callbackID = client->domainEventCallbackID[args->eventID]; + if (callbackID < 0) { + remoteDispatchFormatError(rerr, _("domain event %d not registered"), args->eventID); + return -1; + } + + if (virConnectDomainEventDeregisterAny(conn, callbackID) < 0) { + remoteDispatchConnError(rerr, conn); + return -1; + } + + client->domainEventCallbackID[args->eventID] = -1; + return 0; +} + + /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/daemon/remote_dispatch_args.h b/daemon/remote_dispatch_args.h index f97155b..699c584 100644 --- a/daemon/remote_dispatch_args.h +++ b/daemon/remote_dispatch_args.h @@ -140,3 +140,5 @@ remote_cpu_baseline_args val_remote_cpu_baseline_args; remote_domain_get_job_info_args val_remote_domain_get_job_info_args; remote_domain_abort_job_args val_remote_domain_abort_job_args; + remote_domain_events_register_any_args val_remote_domain_events_register_any_args; + remote_domain_events_deregister_any_args val_remote_domain_events_deregister_any_args; diff --git a/daemon/remote_dispatch_prototypes.h b/daemon/remote_dispatch_prototypes.h index b81c8c3..18558d2 100644 --- a/daemon/remote_dispatch_prototypes.h +++ b/daemon/remote_dispatch_prototypes.h @@ -178,6 +178,14 @@ static int remoteDispatchDomainEventsDeregister( remote_error *err, void *args, remote_domain_events_deregister_ret *ret); +static int remoteDispatchDomainEventsDeregisterAny( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_events_deregister_any_args *args, + void *ret); static int remoteDispatchDomainEventsRegister( struct qemud_server *server, struct qemud_client *client, @@ -186,6 +194,14 @@ static int remoteDispatchDomainEventsRegister( remote_error *err, void *args, remote_domain_events_register_ret *ret); +static int remoteDispatchDomainEventsRegisterAny( + struct qemud_server *server, + struct qemud_client *client, + virConnectPtr conn, + remote_message_header *hdr, + remote_error *err, + remote_domain_events_register_any_args *args, + void *ret); static int remoteDispatchDomainGetAutostart( struct qemud_server *server, struct qemud_client *client, diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 5ad6bff..6abf0ff 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -827,3 +827,13 @@ .args_filter = (xdrproc_t) xdr_remote_domain_abort_job_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* DomainEventsRegisterAny => 165 */ + .fn = (dispatch_fn) remoteDispatchDomainEventsRegisterAny, + .args_filter = (xdrproc_t) xdr_remote_domain_events_register_any_args, + .ret_filter = (xdrproc_t) xdr_void, +}, +{ /* DomainEventsDeregisterAny => 166 */ + .fn = (dispatch_fn) remoteDispatchDomainEventsDeregisterAny, + .args_filter = (xdrproc_t) xdr_remote_domain_events_deregister_any_args, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/src/driver.c b/src/driver.c index c247f14..e6f3aaa 100644 --- a/src/driver.c +++ b/src/driver.c @@ -31,6 +31,10 @@ #define DEFAULT_DRIVER_DIR LIBDIR "/libvirt/drivers" +/* Make sure ... INTERNAL_CALL can not be set by the caller */ +verify((VIR_SECRET_GET_VALUE_INTERNAL_CALL & + VIR_SECRET_GET_VALUE_FLAGS_MASK) == 0); + #ifdef WITH_DRIVER_MODULES /* XXX re-implment this for other OS, or use libtools helper lib ? */ diff --git a/src/driver.h b/src/driver.h index 805b2b1..c461a70 100644 --- a/src/driver.h +++ b/src/driver.h @@ -897,10 +897,6 @@ enum { VIR_SECRET_GET_VALUE_INTERNAL_CALL = 1 << 16 }; -/* Make sure ... INTERNAL_CALL can not be set by the caller */ -verify((VIR_SECRET_GET_VALUE_INTERNAL_CALL & - VIR_SECRET_GET_VALUE_FLAGS_MASK) == 0); - typedef virSecretPtr (*virDrvSecretLookupByUUID) (virConnectPtr conn, const unsigned char *uuid); diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 2c3a70e..eba29f2 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -260,7 +260,6 @@ static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, vi static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); static void make_nonnull_secret (remote_nonnull_secret *secret_dst, virSecretPtr secret_src); void remoteDomainEventFired(int watch, int fd, int event, void *data); -static void remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr); void remoteDomainEventQueueFlush(int timer, void *opaque); /*----------------------------------------------------------------------*/ @@ -6784,10 +6783,10 @@ remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, #endif /* HAVE_POLKIT */ /*----------------------------------------------------------------------*/ -static int remoteDomainEventRegister (virConnectPtr conn, - virConnectDomainEventCallback callback, - void *opaque, - virFreeCallback freecb) +static int remoteDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback callback, + void *opaque, + virFreeCallback freecb) { int rv = -1; struct private_data *priv = conn->privateData; @@ -6804,7 +6803,7 @@ static int remoteDomainEventRegister (virConnectPtr conn, goto done; } - if ( priv->callbackList->count == 1 ) { + if (virDomainEventCallbackListCountID(conn, priv->callbackList, VIR_DOMAIN_EVENT_ID_LIFECYCLE) == 1) { /* Tell the server when we are the first callback deregistering */ if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_REGISTER, (xdrproc_t) xdr_void, (char *) NULL, @@ -6819,8 +6818,8 @@ done: return rv; } -static int remoteDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +static int remoteDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { struct private_data *priv = conn->privateData; int rv = -1; @@ -6839,14 +6838,14 @@ static int remoteDomainEventDeregister (virConnectPtr conn, error (conn, VIR_ERR_RPC, _("removing cb from list")); goto done; } + } - if ( priv->callbackList->count == 0 ) { - /* Tell the server when we are the last callback deregistering */ - if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_void, (char *) NULL) == -1) - goto done; - } + if (virDomainEventCallbackListCountID(conn, priv->callbackList, VIR_DOMAIN_EVENT_ID_LIFECYCLE) == 0) { + /* Tell the server when we are the last callback deregistering */ + if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; } rv = 0; @@ -6856,6 +6855,38 @@ done: return rv; } +/** + * remoteDomainReadEventLifecycle + * + * Read the domain lifecycle event data off the wire + */ +static virDomainEventPtr +remoteDomainReadEventLifecycle(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall lifecycle event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + event = virDomainEventNewFromDom(dom, msg.event, msg.detail); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_msg, (char *) &msg); + + virDomainFree(dom); + return event; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -7707,6 +7738,101 @@ done: } +static int remoteDomainEventRegisterAny(virConnectPtr conn, + virDomainPtr dom, + int eventID, + virConnectDomainEventGenericCallback callback, + void *opaque, + virFreeCallback freecb) +{ + int rv = -1; + struct private_data *priv = conn->privateData; + remote_domain_events_register_any_args args; + int callbackID; + + remoteDriverLock(priv); + + if (priv->eventFlushTimer < 0) { + error (conn, VIR_ERR_NO_SUPPORT, _("no event support")); + goto done; + } + + if ((callbackID = virDomainEventCallbackListAddID(conn, priv->callbackList, + dom, eventID, + callback, opaque, freecb)) < 0) { + error (conn, VIR_ERR_RPC, _("adding cb to list")); + goto done; + } + + /* If this is the first callback for this eventID, we need to enable + * events on the server */ + if (virDomainEventCallbackListCountID(conn, priv->callbackList, eventID) == 1) { + args.eventID = eventID; + + if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY, + (xdrproc_t) xdr_remote_domain_events_register_any_args, (char *) &args, + (xdrproc_t) xdr_void, (char *)NULL) == -1) { + virDomainEventCallbackListRemoveID(conn, priv->callbackList, callbackID); + goto done; + } + } + + rv = callbackID; + +done: + remoteDriverUnlock(priv); + return rv; +} + + +static int remoteDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + remote_domain_events_deregister_any_args args; + int eventID; + + remoteDriverLock(priv); + + if ((eventID = virDomainEventCallbackListEventID(conn, priv->callbackList, callbackID)) < 0) { + errorf (conn, VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID); + goto done; + } + + if (priv->domainEventDispatching) { + if (virDomainEventCallbackListMarkDeleteID(conn, priv->callbackList, + callbackID) < 0) { + error (conn, VIR_ERR_RPC, _("marking cb for deletion")); + goto done; + } + } else { + if (virDomainEventCallbackListRemoveID(conn, priv->callbackList, + callbackID) < 0) { + error (conn, VIR_ERR_RPC, _("removing cb from list")); + goto done; + } + } + + /* If that was the last callback for this eventID, we need to disable + * events on the server */ + if (virDomainEventCallbackListCountID(conn, priv->callbackList, eventID) == 0) { + args.eventID = eventID; + + if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY, + (xdrproc_t) xdr_remote_domain_events_deregister_any_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + } + + rv = 0; + +done: + remoteDriverUnlock(priv); + return rv; +} + + /*----------------------------------------------------------------------*/ @@ -8258,6 +8384,7 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, int in_open, remote_message_header *hdr, XDR *xdr) { + virDomainEventPtr event = NULL; /* An async message has come in while we were waiting for the * response. Process it to pull it off the wire, and try again */ @@ -8268,13 +8395,26 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, return -1; } - if (hdr->proc == REMOTE_PROC_DOMAIN_EVENT) { - remoteDomainQueueEvent(conn, xdr); - virEventUpdateTimeout(priv->eventFlushTimer, 0); - } else { - return -1; + switch (hdr->proc) { + case REMOTE_PROC_DOMAIN_EVENT: + event = remoteDomainReadEventLifecycle(conn, xdr); + break; + + default: DEBUG("Unexpected event proc %d", hdr->proc); + break; } + + if (!event) + return -1; + + if (virDomainEventQueuePush(priv->domainEvents, + event) < 0) { + DEBUG0("Error adding event to queue"); + virDomainEventFree(event); + } + virEventUpdateTimeout(priv->eventFlushTimer, 0); + return 0; } @@ -8798,54 +8938,6 @@ call (virConnectPtr conn, struct private_data *priv, } - -/** - * remoteDomainReadEvent - * - * Read the event data off the wire - */ -static virDomainEventPtr -remoteDomainReadEvent(virConnectPtr conn, XDR *xdr) -{ - remote_domain_event_msg msg; - virDomainPtr dom; - virDomainEventPtr event = NULL; - memset (&msg, 0, sizeof msg); - - /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_msg(xdr, &msg) ) { - error (conn, VIR_ERR_RPC, - _("remoteDomainProcessEvent: unmarshalling msg")); - return NULL; - } - - dom = get_nonnull_domain(conn,msg.dom); - if (!dom) - return NULL; - - event = virDomainEventNewFromDom(dom, msg.event, msg.detail); - - virDomainFree(dom); - return event; -} - -static void -remoteDomainQueueEvent(virConnectPtr conn, XDR *xdr) -{ - struct private_data *priv = conn->privateData; - virDomainEventPtr event; - - event = remoteDomainReadEvent(conn, xdr); - if (!event) - return; - - if (virDomainEventQueuePush(priv->domainEvents, - event) < 0) { - DEBUG0("Error adding event to queue"); - virDomainEventFree(event); - } -} - /** remoteDomainEventFired: * * The callback for monitoring the remote socket @@ -8927,14 +9019,6 @@ remoteDomainEventQueueFlush(int timer ATTRIBUTE_UNUSED, void *opaque) /* Purge any deleted callbacks */ virDomainEventCallbackListPurgeMarked(priv->callbackList); - if ( priv->callbackList->count == 0 ) { - /* Tell the server when we are the last callback deregistering */ - if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_void, (char *) NULL) == -1) - VIR_WARN0("Failed to de-register events"); - } - priv->domainEventDispatching = 0; remoteDriverUnlock(priv); @@ -9126,8 +9210,8 @@ static virDriver remote_driver = { remoteCPUBaseline, /* cpuBaseline */ remoteDomainGetJobInfo, /* domainGetJobInfo */ remoteDomainAbortJob, /* domainFinishJob */ - NULL, /* domainEventRegisterAny */ - NULL, /* domainEventDeregisterAny */ + remoteDomainEventRegisterAny, /* domainEventRegisterAny */ + remoteDomainEventDeregisterAny, /* domainEventDeregisterAny */ }; static virNetworkDriver network_driver = { diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 701acab..3f4af38 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3009,6 +3009,24 @@ xdr_remote_domain_abort_job_args (XDR *xdrs, remote_domain_abort_job_args *objp) } bool_t +xdr_remote_domain_events_register_any_args (XDR *xdrs, remote_domain_events_register_any_args *objp) +{ + + if (!xdr_int (xdrs, &objp->eventID)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_events_deregister_any_args (XDR *xdrs, remote_domain_events_deregister_any_args *objp) +{ + + if (!xdr_int (xdrs, &objp->eventID)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index f76e6e5..40e1094 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -4,51 +4,51 @@ */ #ifndef _RP_H_RPCGEN -# define _RP_H_RPCGEN +#define _RP_H_RPCGEN -# include <rpc/rpc.h> +#include <rpc/rpc.h> -# ifdef __cplusplus +#ifdef __cplusplus extern "C" { -# endif +#endif -# include "internal.h" -# include <arpa/inet.h> -# define REMOTE_MESSAGE_MAX 262144 -# define REMOTE_MESSAGE_HEADER_MAX 24 -# define REMOTE_MESSAGE_PAYLOAD_MAX 262120 -# define REMOTE_STRING_MAX 65536 +#include "internal.h" +#include <arpa/inet.h> +#define REMOTE_MESSAGE_MAX 262144 +#define REMOTE_MESSAGE_HEADER_MAX 24 +#define REMOTE_MESSAGE_PAYLOAD_MAX 262120 +#define REMOTE_STRING_MAX 65536 typedef char *remote_nonnull_string; typedef remote_nonnull_string *remote_string; -# define REMOTE_DOMAIN_ID_LIST_MAX 16384 -# define REMOTE_DOMAIN_NAME_LIST_MAX 1024 -# define REMOTE_CPUMAP_MAX 256 -# define REMOTE_VCPUINFO_MAX 2048 -# define REMOTE_CPUMAPS_MAX 16384 -# define REMOTE_MIGRATE_COOKIE_MAX 256 -# define REMOTE_NETWORK_NAME_LIST_MAX 256 -# define REMOTE_INTERFACE_NAME_LIST_MAX 256 -# define REMOTE_DEFINED_INTERFACE_NAME_LIST_MAX 256 -# define REMOTE_STORAGE_POOL_NAME_LIST_MAX 256 -# define REMOTE_STORAGE_VOL_NAME_LIST_MAX 1024 -# define REMOTE_NODE_DEVICE_NAME_LIST_MAX 16384 -# define REMOTE_NODE_DEVICE_CAPS_LIST_MAX 16384 -# define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 -# define REMOTE_NODE_MAX_CELLS 1024 -# define REMOTE_AUTH_SASL_DATA_MAX 65536 -# define REMOTE_AUTH_TYPE_LIST_MAX 20 -# define REMOTE_DOMAIN_MEMORY_STATS_MAX 1024 -# define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 -# define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 -# define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN -# define REMOTE_SECURITY_LABEL_MAX VIR_SECURITY_LABEL_BUFLEN -# define REMOTE_SECURITY_DOI_MAX VIR_SECURITY_DOI_BUFLEN -# define REMOTE_SECRET_VALUE_MAX 65536 -# define REMOTE_SECRET_UUID_LIST_MAX 16384 -# define REMOTE_CPU_BASELINE_MAX 256 +#define REMOTE_DOMAIN_ID_LIST_MAX 16384 +#define REMOTE_DOMAIN_NAME_LIST_MAX 1024 +#define REMOTE_CPUMAP_MAX 256 +#define REMOTE_VCPUINFO_MAX 2048 +#define REMOTE_CPUMAPS_MAX 16384 +#define REMOTE_MIGRATE_COOKIE_MAX 256 +#define REMOTE_NETWORK_NAME_LIST_MAX 256 +#define REMOTE_INTERFACE_NAME_LIST_MAX 256 +#define REMOTE_DEFINED_INTERFACE_NAME_LIST_MAX 256 +#define REMOTE_STORAGE_POOL_NAME_LIST_MAX 256 +#define REMOTE_STORAGE_VOL_NAME_LIST_MAX 1024 +#define REMOTE_NODE_DEVICE_NAME_LIST_MAX 16384 +#define REMOTE_NODE_DEVICE_CAPS_LIST_MAX 16384 +#define REMOTE_DOMAIN_SCHEDULER_PARAMETERS_MAX 16 +#define REMOTE_NODE_MAX_CELLS 1024 +#define REMOTE_AUTH_SASL_DATA_MAX 65536 +#define REMOTE_AUTH_TYPE_LIST_MAX 20 +#define REMOTE_DOMAIN_MEMORY_STATS_MAX 1024 +#define REMOTE_DOMAIN_BLOCK_PEEK_BUFFER_MAX 65536 +#define REMOTE_DOMAIN_MEMORY_PEEK_BUFFER_MAX 65536 +#define REMOTE_SECURITY_MODEL_MAX VIR_SECURITY_MODEL_BUFLEN +#define REMOTE_SECURITY_LABEL_MAX VIR_SECURITY_LABEL_BUFLEN +#define REMOTE_SECURITY_DOI_MAX VIR_SECURITY_DOI_BUFLEN +#define REMOTE_SECRET_VALUE_MAX 65536 +#define REMOTE_SECRET_UUID_LIST_MAX 16384 +#define REMOTE_CPU_BASELINE_MAX 256 typedef char remote_uuid[VIR_UUID_BUFLEN]; @@ -1704,8 +1704,18 @@ struct remote_domain_abort_job_args { remote_nonnull_domain dom; }; typedef struct remote_domain_abort_job_args remote_domain_abort_job_args; -# define REMOTE_PROGRAM 0x20008086 -# define REMOTE_PROTOCOL_VERSION 1 + +struct remote_domain_events_register_any_args { + int eventID; +}; +typedef struct remote_domain_events_register_any_args remote_domain_events_register_any_args; + +struct remote_domain_events_deregister_any_args { + int eventID; +}; +typedef struct remote_domain_events_deregister_any_args remote_domain_events_deregister_any_args; +#define REMOTE_PROGRAM 0x20008086 +#define REMOTE_PROTOCOL_VERSION 1 enum remote_procedure { REMOTE_PROC_OPEN = 1, @@ -1872,6 +1882,8 @@ enum remote_procedure { REMOTE_PROC_CPU_BASELINE = 162, REMOTE_PROC_DOMAIN_GET_JOB_INFO = 163, REMOTE_PROC_DOMAIN_ABORT_JOB = 164, + REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, + REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, }; typedef enum remote_procedure remote_procedure; @@ -1889,7 +1901,7 @@ enum remote_message_status { REMOTE_CONTINUE = 2, }; typedef enum remote_message_status remote_message_status; -# define REMOTE_MESSAGE_HEADER_XDR_LEN 4 +#define REMOTE_MESSAGE_HEADER_XDR_LEN 4 struct remote_message_header { u_int prog; @@ -1903,7 +1915,7 @@ typedef struct remote_message_header remote_message_header; /* the xdr functions */ -# if defined(__STDC__) || defined(__cplusplus) +#if defined(__STDC__) || defined(__cplusplus) extern bool_t xdr_remote_nonnull_string (XDR *, remote_nonnull_string*); extern bool_t xdr_remote_string (XDR *, remote_string*); extern bool_t xdr_remote_uuid (XDR *, remote_uuid); @@ -2181,12 +2193,14 @@ extern bool_t xdr_remote_cpu_baseline_ret (XDR *, remote_cpu_baseline_ret*); extern bool_t xdr_remote_domain_get_job_info_args (XDR *, remote_domain_get_job_info_args*); extern bool_t xdr_remote_domain_get_job_info_ret (XDR *, remote_domain_get_job_info_ret*); extern bool_t xdr_remote_domain_abort_job_args (XDR *, remote_domain_abort_job_args*); +extern bool_t xdr_remote_domain_events_register_any_args (XDR *, remote_domain_events_register_any_args*); +extern bool_t xdr_remote_domain_events_deregister_any_args (XDR *, remote_domain_events_deregister_any_args*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); extern bool_t xdr_remote_message_header (XDR *, remote_message_header*); -# else /* K&R C */ +#else /* K&R C */ extern bool_t xdr_remote_nonnull_string (); extern bool_t xdr_remote_string (); extern bool_t xdr_remote_uuid (); @@ -2464,15 +2478,17 @@ extern bool_t xdr_remote_cpu_baseline_ret (); extern bool_t xdr_remote_domain_get_job_info_args (); extern bool_t xdr_remote_domain_get_job_info_ret (); extern bool_t xdr_remote_domain_abort_job_args (); +extern bool_t xdr_remote_domain_events_register_any_args (); +extern bool_t xdr_remote_domain_events_deregister_any_args (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); extern bool_t xdr_remote_message_header (); -# endif /* K&R C */ +#endif /* K&R C */ -# ifdef __cplusplus +#ifdef __cplusplus } -# endif +#endif #endif /* !_RP_H_RPCGEN */ diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 5e33da5..60435f4 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1517,6 +1517,15 @@ struct remote_domain_abort_job_args { }; +struct remote_domain_events_register_any_args { + int eventID; +}; + +struct remote_domain_events_deregister_any_args { + int eventID; +}; + + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1703,7 +1712,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_DETACH_DEVICE_FLAGS = 161, REMOTE_PROC_CPU_BASELINE = 162, REMOTE_PROC_DOMAIN_GET_JOB_INFO = 163, - REMOTE_PROC_DOMAIN_ABORT_JOB = 164 + REMOTE_PROC_DOMAIN_ABORT_JOB = 164, + REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, + REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:54PM +0000, Daniel P. Berrange wrote:
This wires up the remote driver to handle the new events APIs. The public API allows an application to request a callback filters events to a specific domain object, and register multiple callbacks for the same event type. On the wire there are two strategies for this
- Register multiple callbacks with the remote daemon, each with filtering as needed - Register only one callback per event type, with no filtering
Both approaches have potential inefficiency. In the first scheme, the same event gets sent over the wire many times if multiple callbacks are registered. With the second scheme, unneccessary events get sent over the wire if a per-domain filter is set on the client. The second scheme is far easier to implement though, so this patch takes that approach.
I think we should try to optimize for the new API, and for monitoring tools I would assume they are gonna monitor all running domains, so we are likely to end up with as many registration with each a domain filter. In such a situation the second scheme sounds better since events are sent only once over the wire, and they would be no waste since all doamins are likely to be monitored. So assuming I understood correctly the second approach is also the one mich makes the most sense to me. [...]
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h index d30fcd7..d292681 100644 --- a/daemon/libvirtd.h +++ b/daemon/libvirtd.h @@ -177,7 +177,7 @@ struct qemud_client { int watch; unsigned int readonly :1; unsigned int closing :1; - unsigned int domain_events_registered :1; + int domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LAST];
I can't remember if we explicitely initialized the first value in the enum.
@@ -4844,21 +4862,26 @@ remoteDispatchDomainEventsDeregister (struct qemud_server *server ATTRIBUTE_UNUS { CHECK_CONN(client);
- if (virConnectDomainEventDeregister(conn, remoteRelayDomainEvent) < 0) { - remoteDispatchConnError(rerr, conn); + if (client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] == -1) { + remoteDispatchFormatError(rerr, _("domain event %d not registered"), VIR_DOMAIN_EVENT_ID_LIFECYCLE); return -1; }
- if (ret) - ret->cb_registered = 0; + if (virConnectDomainEventDeregisterAny(conn, + client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE]) < 0) { + remoteDispatchConnError(rerr, conn); + return -1; + }
- client->domain_events_registered = 0; + client->domainEventCallbackID[VIR_DOMAIN_EVENT_ID_LIFECYCLE] = -1; return 0; }
[...]
-static int remoteDomainEventDeregister (virConnectPtr conn, - virConnectDomainEventCallback callback) +static int remoteDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback callback) { struct private_data *priv = conn->privateData; int rv = -1; @@ -6839,14 +6838,14 @@ static int remoteDomainEventDeregister (virConnectPtr conn, error (conn, VIR_ERR_RPC, _("removing cb from list")); goto done; } + }
- if ( priv->callbackList->count == 0 ) { - /* Tell the server when we are the last callback deregistering */ - if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, - (xdrproc_t) xdr_void, (char *) NULL, - (xdrproc_t) xdr_void, (char *) NULL) == -1) - goto done; - } + if (virDomainEventCallbackListCountID(conn, priv->callbackList, VIR_DOMAIN_EVENT_ID_LIFECYCLE) == 0) { + /* Tell the server when we are the last callback deregistering */ + if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER, + (xdrproc_t) xdr_void, (char *) NULL, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; }
okay, that's where we could the number of local client registered before forwarding a deregisting request remotely. [...]
+static int remoteDomainEventDeregisterAny(virConnectPtr conn, + int callbackID) +{ + struct private_data *priv = conn->privateData; + int rv = -1; + remote_domain_events_deregister_any_args args; + int eventID; + + remoteDriverLock(priv); + + if ((eventID = virDomainEventCallbackListEventID(conn, priv->callbackList, callbackID)) < 0) { + errorf (conn, VIR_ERR_RPC, _("unable to find callback ID %d"), callbackID); + goto done; + } + + if (priv->domainEventDispatching) { + if (virDomainEventCallbackListMarkDeleteID(conn, priv->callbackList, + callbackID) < 0) { + error (conn, VIR_ERR_RPC, _("marking cb for deletion")); + goto done; + } + } else { + if (virDomainEventCallbackListRemoveID(conn, priv->callbackList, + callbackID) < 0) { + error (conn, VIR_ERR_RPC, _("removing cb from list")); + goto done; + } + } + + /* If that was the last callback for this eventID, we need to disable + * events on the server */ + if (virDomainEventCallbackListCountID(conn, priv->callbackList, eventID) == 0) { + args.eventID = eventID; + + if (call (conn, priv, 0, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY, + (xdrproc_t) xdr_remote_domain_events_deregister_any_args, (char *) &args, + (xdrproc_t) xdr_void, (char *) NULL) == -1) + goto done; + }
and here too. [...]
typedef remote_nonnull_string *remote_string; -# define REMOTE_DOMAIN_ID_LIST_MAX 16384 -# define REMOTE_DOMAIN_NAME_LIST_MAX 1024 -# define REMOTE_CPUMAP_MAX 256
hitting the indentation of generated headers here too, will probably need a rebase ACK, looks fine! Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

Convert the domain events example program to use the new events APIs for one of its callback registrations to demo the new API and interoperability with the old API. * examples/domain-events/events-c/event-test.c: Convert to new events API --- examples/domain-events/events-c/event-test.c | 41 +++++++++++++------------ 1 files changed, 21 insertions(+), 20 deletions(-) diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 7464f93..ed00fb8 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -38,10 +38,6 @@ void *t_opaque = NULL; /* Prototypes */ const char *eventToString(int event); -int myDomainEventCallback1 (virConnectPtr conn, virDomainPtr dom, - int event, int detail, void *opaque); -int myDomainEventCallback2 (virConnectPtr conn, virDomainPtr dom, - int event, int detail, void *opaque); int myEventAddHandleFunc (int fd, int event, virEventHandleCallback cb, void *opaque, @@ -152,11 +148,11 @@ static const char *eventDetailToString(int event, int detail) { return ret; } -int myDomainEventCallback1 (virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainPtr dom, - int event, - int detail, - void *opaque ATTRIBUTE_UNUSED) +static int myDomainEventCallback1(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int event, + int detail, + void *opaque ATTRIBUTE_UNUSED) { printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom), virDomainGetID(dom), eventToString(event), @@ -164,11 +160,11 @@ int myDomainEventCallback1 (virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } -int myDomainEventCallback2 (virConnectPtr conn ATTRIBUTE_UNUSED, - virDomainPtr dom, - int event, - int detail, - void *opaque ATTRIBUTE_UNUSED) +static int myDomainEventCallback2(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int event, + int detail, + void *opaque ATTRIBUTE_UNUSED) { printf("%s EVENT: Domain %s(%d) %s %s\n", __func__, virDomainGetName(dom), virDomainGetID(dom), eventToString(event), @@ -293,6 +289,7 @@ int main(int argc, char **argv) int sts; int callback1ret = -1; int callback2ret = -1; + struct sigaction action_stop = { .sa_handler = stop }; @@ -310,7 +307,7 @@ int main(int argc, char **argv) myEventRemoveTimeoutFunc); virConnectPtr dconn = NULL; - dconn = virConnectOpen (argv[1] ? argv[1] : NULL); + dconn = virConnectOpenReadOnly (argv[1] ? argv[1] : NULL); if (!dconn) { printf("error opening\n"); return -1; @@ -324,10 +321,14 @@ int main(int argc, char **argv) /* Add 2 callbacks to prove this works with more than just one */ callback1ret = virConnectDomainEventRegister(dconn, myDomainEventCallback1, strdup("callback 1"), myFreeFunc); - callback2ret = virConnectDomainEventRegister(dconn, myDomainEventCallback2, - strdup("callback 2"), myFreeFunc); - - if ((callback1ret == 0) && (callback2ret == 0) ) { + callback2ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_LIFECYCLE, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventCallback2), + strdup("callback 2"), myFreeFunc); + + if ((callback1ret != -1) && + (callback2ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -364,7 +365,7 @@ int main(int argc, char **argv) DEBUG0("Deregistering event handlers"); virConnectDomainEventDeregister(dconn, myDomainEventCallback1); - virConnectDomainEventDeregister(dconn, myDomainEventCallback2); + virConnectDomainEventDeregisterAny(dconn, callback2ret); } -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:55PM +0000, Daniel P. Berrange wrote:
Convert the domain events example program to use the new events APIs for one of its callback registrations to demo the new API and interoperability with the old API.
* examples/domain-events/events-c/event-test.c: Convert to new events API --- examples/domain-events/events-c/event-test.c | 41 +++++++++++++------------ 1 files changed, 21 insertions(+), 20 deletions(-)
diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 7464f93..ed00fb8 100644
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

To avoid confusion, rename the current REMOTE_PROC_DOMAIN_EVENT message to REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE. This does not cause ABI problems, since the names are only relevant at the source code level. On the wire they encoding is a plain integer whose value does not change * src/remote/remote_protocol.x: Rename REMOTE_PROC_DOMAIN_EVENT to REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE. * daemon/remote.c, src/remote/remote_driver.c: Update code for renamed event --- daemon/remote.c | 6 +++--- daemon/remote_dispatch_table.h | 2 +- src/remote/remote_driver.c | 8 ++++---- src/remote/remote_protocol.c | 2 +- src/remote/remote_protocol.h | 10 +++++----- src/remote/remote_protocol.x | 4 ++-- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index fd3aa4e..c55331c 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -105,7 +105,7 @@ static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, void *opaque) { struct qemud_client *client = opaque; - remote_domain_event_msg data; + remote_domain_event_lifecycle_msg data; if (!client) return -1; @@ -121,8 +121,8 @@ static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, data.detail = detail; remoteDispatchDomainEventSend (client, - REMOTE_PROC_DOMAIN_EVENT, - (xdrproc_t)xdr_remote_domain_event_msg, &data); + REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE, + (xdrproc_t)xdr_remote_domain_event_lifecycle_msg, &data); virMutexUnlock(&client->lock); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 6abf0ff..adfc58b 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -537,7 +537,7 @@ .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_remote_domain_events_deregister_ret, }, -{ /* Async event DomainEvent => 107 */ +{ /* Async event DomainEventLifecycle => 107 */ .fn = NULL, .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_void, diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index eba29f2..8b6db9c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6863,13 +6863,13 @@ done: static virDomainEventPtr remoteDomainReadEventLifecycle(virConnectPtr conn, XDR *xdr) { - remote_domain_event_msg msg; + remote_domain_event_lifecycle_msg msg; virDomainPtr dom; virDomainEventPtr event = NULL; memset (&msg, 0, sizeof msg); /* unmarshall parameters, and process it*/ - if (! xdr_remote_domain_event_msg(xdr, &msg) ) { + if (! xdr_remote_domain_event_lifecycle_msg(xdr, &msg) ) { error (conn, VIR_ERR_RPC, _("unable to demarshall lifecycle event")); return NULL; @@ -6880,7 +6880,7 @@ remoteDomainReadEventLifecycle(virConnectPtr conn, XDR *xdr) return NULL; event = virDomainEventNewFromDom(dom, msg.event, msg.detail); - xdr_free ((xdrproc_t) &xdr_remote_domain_event_msg, (char *) &msg); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_lifecycle_msg, (char *) &msg); virDomainFree(dom); return event; @@ -8396,7 +8396,7 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, } switch (hdr->proc) { - case REMOTE_PROC_DOMAIN_EVENT: + case REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE: event = remoteDomainReadEventLifecycle(conn, xdr); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 3f4af38..3ad8958 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -2560,7 +2560,7 @@ xdr_remote_domain_events_deregister_ret (XDR *xdrs, remote_domain_events_deregis } bool_t -xdr_remote_domain_event_msg (XDR *xdrs, remote_domain_event_msg *objp) +xdr_remote_domain_event_lifecycle_msg (XDR *xdrs, remote_domain_event_lifecycle_msg *objp) { if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 40e1094..9005d6f 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1450,12 +1450,12 @@ struct remote_domain_events_deregister_ret { }; typedef struct remote_domain_events_deregister_ret remote_domain_events_deregister_ret; -struct remote_domain_event_msg { +struct remote_domain_event_lifecycle_msg { remote_nonnull_domain dom; int event; int detail; }; -typedef struct remote_domain_event_msg remote_domain_event_msg; +typedef struct remote_domain_event_lifecycle_msg remote_domain_event_lifecycle_msg; struct remote_domain_xml_from_native_args { remote_nonnull_string nativeFormat; @@ -1824,7 +1824,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104, REMOTE_PROC_DOMAIN_EVENTS_REGISTER = 105, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER = 106, - REMOTE_PROC_DOMAIN_EVENT = 107, + REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE = 107, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 108, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 109, REMOTE_PROC_GET_URI = 110, @@ -2150,7 +2150,7 @@ extern bool_t xdr_remote_node_device_create_xml_ret (XDR *, remote_node_device_ extern bool_t xdr_remote_node_device_destroy_args (XDR *, remote_node_device_destroy_args*); extern bool_t xdr_remote_domain_events_register_ret (XDR *, remote_domain_events_register_ret*); extern bool_t xdr_remote_domain_events_deregister_ret (XDR *, remote_domain_events_deregister_ret*); -extern bool_t xdr_remote_domain_event_msg (XDR *, remote_domain_event_msg*); +extern bool_t xdr_remote_domain_event_lifecycle_msg (XDR *, remote_domain_event_lifecycle_msg*); extern bool_t xdr_remote_domain_xml_from_native_args (XDR *, remote_domain_xml_from_native_args*); extern bool_t xdr_remote_domain_xml_from_native_ret (XDR *, remote_domain_xml_from_native_ret*); extern bool_t xdr_remote_domain_xml_to_native_args (XDR *, remote_domain_xml_to_native_args*); @@ -2435,7 +2435,7 @@ extern bool_t xdr_remote_node_device_create_xml_ret (); extern bool_t xdr_remote_node_device_destroy_args (); extern bool_t xdr_remote_domain_events_register_ret (); extern bool_t xdr_remote_domain_events_deregister_ret (); -extern bool_t xdr_remote_domain_event_msg (); +extern bool_t xdr_remote_domain_event_lifecycle_msg (); extern bool_t xdr_remote_domain_xml_from_native_args (); extern bool_t xdr_remote_domain_xml_from_native_ret (); extern bool_t xdr_remote_domain_xml_to_native_args (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 60435f4..2807afa 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1299,7 +1299,7 @@ struct remote_domain_events_deregister_ret { int cb_registered; }; -struct remote_domain_event_msg { +struct remote_domain_event_lifecycle_msg { remote_nonnull_domain dom; int event; int detail; @@ -1649,7 +1649,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104, REMOTE_PROC_DOMAIN_EVENTS_REGISTER = 105, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER = 106, - REMOTE_PROC_DOMAIN_EVENT = 107, + REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE = 107, REMOTE_PROC_DOMAIN_MIGRATE_PREPARE2 = 108, REMOTE_PROC_DOMAIN_MIGRATE_FINISH2 = 109, REMOTE_PROC_GET_URI = 110, -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:56PM +0000, Daniel P. Berrange wrote:
To avoid confusion, rename the current REMOTE_PROC_DOMAIN_EVENT message to REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE. This does not cause ABI problems, since the names are only relevant at the source code level. On the wire they encoding is a plain integer whose value does not change
* src/remote/remote_protocol.x: Rename REMOTE_PROC_DOMAIN_EVENT to REMOTE_PROC_DOMAIN_EVENT_LIFECYCLE. * daemon/remote.c, src/remote/remote_driver.c: Update code for renamed event
ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

The reboot event is not a normal lifecycle event, since the virtual machine on the host does not change state. Rather the guest OS is resetting the virtual CPUs. ie, the QEMU process does not restart. Thus, this does not belong in the current lifecycle events callback. This introduces a new event type VIR_DOMAIN_EVENT_ID_REBOOT It takes no parameters, besides the virDomainPtr, so it can use the generic callback signature. * daemon/remote.c: Dispatch reboot events to client * examples/domain-events/events-c/event-test.c: Watch for reboot events * include/libvirt/libvirt.h.in: Define new reboot event ID * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle reboot events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for reboots and emit a libvirt reboot event * src/remote/remote_driver.c: Receive and dispatch reboot events to application * src/remote/remote_protocol.x: Wire protocol definition for reboot events --- daemon/remote.c | 30 ++++++++++++++++++++++++- daemon/remote_dispatch_table.h | 5 ++++ examples/domain-events/events-c/event-test.c | 20 ++++++++++++++++- include/libvirt/libvirt.h.in | 1 + src/conf/domain_event.c | 16 +++++++++++++ src/conf/domain_event.h | 3 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 23 +++++++++++++++++++ src/remote/remote_driver.c | 31 ++++++++++++++++++++++++++ src/remote/remote_protocol.c | 9 +++++++ src/remote/remote_protocol.h | 8 ++++++ src/remote/remote_protocol.x | 6 ++++- 12 files changed, 151 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index c55331c..f86c102 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -129,9 +129,37 @@ static int remoteRelayDomainEventLifecycle(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + void *opaque) +{ + struct qemud_client *client = opaque; + remote_domain_event_reboot_msg data; + + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain reboot event %s %d", dom->name, dom->id); + + virMutexLock(&client->lock); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT_REBOOT, + (xdrproc_t)xdr_remote_domain_event_reboot_msg, &data); + + virMutexUnlock(&client->lock); + + return 0; +} + -static virConnectDomainEventGenericCallback domainEventCallbacks[VIR_DOMAIN_EVENT_ID_LAST] = { +static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index adfc58b..990e95c 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -837,3 +837,8 @@ .args_filter = (xdrproc_t) xdr_remote_domain_events_deregister_any_args, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* Async event DomainEventReboot => 167 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index ed00fb8..c7da0c0 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -172,6 +172,16 @@ static int myDomainEventCallback2(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventRebootCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) rebooted\n", __func__, virDomainGetName(dom), + virDomainGetID(dom)); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -289,6 +299,7 @@ int main(int argc, char **argv) int sts; int callback1ret = -1; int callback2ret = -1; + int callback3ret = -1; struct sigaction action_stop = { .sa_handler = stop @@ -326,9 +337,15 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_LIFECYCLE, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventCallback2), strdup("callback 2"), myFreeFunc); + callback3ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_REBOOT, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRebootCallback), + strdup("callback reboot"), myFreeFunc); if ((callback1ret != -1) && - (callback2ret != -1)) { + (callback2ret != -1) && + (callback3ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -366,6 +383,7 @@ int main(int argc, char **argv) DEBUG0("Deregistering event handlers"); virConnectDomainEventDeregister(dconn, myDomainEventCallback1); virConnectDomainEventDeregisterAny(dconn, callback2ret); + virConnectDomainEventDeregisterAny(dconn, callback3ret); } diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 62a51ea..4b55ded 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1850,6 +1850,7 @@ typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ + VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 3bd570e..bc16d97 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -523,6 +523,17 @@ virDomainEventPtr virDomainEventNewFromDef(virDomainDefPtr def, int type, int de return virDomainEventNew(def->id, def->name, def->uuid, type, detail); } +virDomainEventPtr virDomainEventRebootNewFromDom(virDomainPtr dom) +{ + return virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_REBOOT, + dom->id, dom->name, dom->uuid); +} +virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj) +{ + return virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_REBOOT, + obj->def->id, obj->def->name, obj->def->uuid); +} + /** * virDomainEventQueueFree: * @queue: pointer to the queue @@ -628,6 +639,11 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_REBOOT: + (cb)(conn, dom, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 5578cd9..d9c950d 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -112,6 +112,9 @@ virDomainEventPtr virDomainEventNewFromDom(virDomainPtr dom, int type, int detai virDomainEventPtr virDomainEventNewFromObj(virDomainObjPtr obj, int type, int detail); virDomainEventPtr virDomainEventNewFromDef(virDomainDefPtr def, int type, int detail); +virDomainEventPtr virDomainEventRebootNewFromDom(virDomainPtr dom); +virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2d54fb1..38dc0a4 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -214,6 +214,8 @@ virDomainEventNew; virDomainEventNewFromDom; virDomainEventNewFromObj; virDomainEventNewFromDef; +virDomainEventRebootNewFromDom; +virDomainEventRebootNewFromObj; virDomainEventFree; virDomainEventDispatchDefaultFunc; virDomainEventDispatch; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a8af7b4..9dfc77e 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -855,9 +855,32 @@ cleanup: return ret; } + +static int +qemuHandleDomainReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + + virDomainObjLock(vm); + event = virDomainEventRebootNewFromObj(vm); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, + .domainReset = qemuHandleDomainReset, }; static int diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8b6db9c..1a64749 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6887,6 +6887,33 @@ remoteDomainReadEventLifecycle(virConnectPtr conn, XDR *xdr) } +static virDomainEventPtr +remoteDomainReadEventReboot(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_reboot_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_reboot_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall reboot event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + event = virDomainEventRebootNewFromDom(dom); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_reboot_msg, (char *) &msg); + + virDomainFree(dom); + return event; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -8400,6 +8427,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, event = remoteDomainReadEventLifecycle(conn, xdr); break; + case REMOTE_PROC_DOMAIN_EVENT_REBOOT: + event = remoteDomainReadEventReboot(conn, xdr); + break; + default: DEBUG("Unexpected event proc %d", hdr->proc); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 3ad8958..f2a59c7 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3027,6 +3027,15 @@ xdr_remote_domain_events_deregister_any_args (XDR *xdrs, remote_domain_events_de } bool_t +xdr_remote_domain_event_reboot_msg (XDR *xdrs, remote_domain_event_reboot_msg *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 9005d6f..900a8b8 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1714,6 +1714,11 @@ struct remote_domain_events_deregister_any_args { int eventID; }; typedef struct remote_domain_events_deregister_any_args remote_domain_events_deregister_any_args; + +struct remote_domain_event_reboot_msg { + remote_nonnull_domain dom; +}; +typedef struct remote_domain_event_reboot_msg remote_domain_event_reboot_msg; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1884,6 +1889,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_ABORT_JOB = 164, REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, + REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, }; typedef enum remote_procedure remote_procedure; @@ -2195,6 +2201,7 @@ extern bool_t xdr_remote_domain_get_job_info_ret (XDR *, remote_domain_get_job_ extern bool_t xdr_remote_domain_abort_job_args (XDR *, remote_domain_abort_job_args*); extern bool_t xdr_remote_domain_events_register_any_args (XDR *, remote_domain_events_register_any_args*); extern bool_t xdr_remote_domain_events_deregister_any_args (XDR *, remote_domain_events_deregister_any_args*); +extern bool_t xdr_remote_domain_event_reboot_msg (XDR *, remote_domain_event_reboot_msg*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2480,6 +2487,7 @@ extern bool_t xdr_remote_domain_get_job_info_ret (); extern bool_t xdr_remote_domain_abort_job_args (); extern bool_t xdr_remote_domain_events_register_any_args (); extern bool_t xdr_remote_domain_events_deregister_any_args (); +extern bool_t xdr_remote_domain_event_reboot_msg (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 2807afa..72fe5b3 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1525,6 +1525,9 @@ struct remote_domain_events_deregister_any_args { int eventID; }; +struct remote_domain_event_reboot_msg { + remote_nonnull_domain dom; +}; /*----- Protocol. -----*/ @@ -1714,7 +1717,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_JOB_INFO = 163, REMOTE_PROC_DOMAIN_ABORT_JOB = 164, REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, - REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166 + REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, + REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:57PM +0000, Daniel P. Berrange wrote:
The reboot event is not a normal lifecycle event, since the virtual machine on the host does not change state. Rather the guest OS is resetting the virtual CPUs. ie, the QEMU process does not restart. Thus, this does not belong in the current lifecycle events callback.
This introduces a new event type
VIR_DOMAIN_EVENT_ID_REBOOT
It takes no parameters, besides the virDomainPtr, so it can use the generic callback signature.
[...]
--- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1850,6 +1850,7 @@ typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn,
typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ + VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */
+ +static int +qemuHandleDomainReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + + virDomainObjLock(vm); + event = virDomainEventRebootNewFromObj(vm); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, + .domainReset = qemuHandleDomainReset, }; Okay, we already had qemuMonitorEmitReset() emit the event if the hook was in place, which explain why there is no detection code in that
that answer my question on a previous review, yes we initialized to 0 [...] patch, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This introduces a new event type VIR_DOMAIN_EVENT_ID_RTC_CHANGE This event includes the new UTC offset measured in seconds. Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque); If the guest XML configuration for the <clock> is set to offset='variable', then the XML will automatically be updated with the new UTC offset value. This ensures that during migration/save/restore the new offset is preserved. * daemon/remote.c: Dispatch RTC change events to client * examples/domain-events/events-c/event-test.c: Watch for RTC change events * include/libvirt/libvirt.h.in: Define new RTC change event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle RTC change events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for RTC changes and emit a libvirt RTC change event * src/remote/remote_driver.c: Receive and dispatch RTC change events to application * src/remote/remote_protocol.x: Wire protocol definition for RTC change events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for RTC_CHANGE event from QEMU monitor --- daemon/remote.c | 31 +++++++++++++++++++++++ daemon/remote_dispatch_table.h | 5 ++++ examples/domain-events/events-c/event-test.c | 21 +++++++++++++++- include/libvirt/libvirt.h.in | 5 ++++ src/conf/domain_event.c | 34 ++++++++++++++++++++++++++ src/conf/domain_event.h | 3 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 30 ++++++++++++++++++++++ src/qemu/qemu_monitor.c | 15 +++++++++++ src/qemu/qemu_monitor.h | 4 +++ src/qemu/qemu_monitor_json.c | 12 +++++++++ src/remote/remote_driver.c | 31 +++++++++++++++++++++++ src/remote/remote_protocol.c | 11 ++++++++ src/remote/remote_protocol.h | 9 +++++++ src/remote/remote_protocol.x | 8 +++++- 15 files changed, 219 insertions(+), 2 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index f86c102..ac62f43 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -157,9 +157,40 @@ static int remoteRelayDomainEventReboot(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + long long offset, + void *opaque) +{ + struct qemud_client *client = opaque; + remote_domain_event_rtc_change_msg data; + + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain rtc change event %s %d %lld", dom->name, dom->id, offset); + + virMutexLock(&client->lock); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + data.offset = offset; + + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE, + (xdrproc_t)xdr_remote_domain_event_rtc_change_msg, &data); + + virMutexUnlock(&client->lock); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 990e95c..e5cc724 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -842,3 +842,8 @@ .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* Async event DomainEventRtcChange => 168 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index c7da0c0..97b37de 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -182,6 +182,17 @@ static int myDomainEventRebootCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + long long offset, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) rtc change %lld\n", __func__, virDomainGetName(dom), + virDomainGetID(dom), offset); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -300,6 +311,7 @@ int main(int argc, char **argv) int callback1ret = -1; int callback2ret = -1; int callback3ret = -1; + int callback4ret = -1; struct sigaction action_stop = { .sa_handler = stop @@ -342,10 +354,16 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_REBOOT, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRebootCallback), strdup("callback reboot"), myFreeFunc); + callback4ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_RTC_CHANGE, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRTCChangeCallback), + strdup("callback rtcchange"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && - (callback3ret != -1)) { + (callback3ret != -1) && + (callback4ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -384,6 +402,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregister(dconn, myDomainEventCallback1); virConnectDomainEventDeregisterAny(dconn, callback2ret); virConnectDomainEventDeregisterAny(dconn, callback3ret); + virConnectDomainEventDeregisterAny(dconn, callback4ret); } diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 4b55ded..edb0a6e 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1842,6 +1842,10 @@ int virDomainAbortJob(virDomainPtr dom); typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, virDomainPtr dom, void *opaque); +typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + long long utcoffset, + void *opaque); /* Use this to cast the event specific callback into the generic one * for use for virDomainEventRegister */ @@ -1851,6 +1855,7 @@ typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */ + VIR_DOMAIN_EVENT_ID_RTC_CHANGE = 2, /* virConnectDomainEventRTCChangeCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index bc16d97..79dfbac 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -64,6 +64,9 @@ struct _virDomainEvent { int type; int detail; } lifecycle; + struct { + long long offset; + } rtcChange; } data; }; @@ -534,6 +537,31 @@ virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj) obj->def->id, obj->def->name, obj->def->uuid); } +virDomainEventPtr virDomainEventRTCChangeNewFromDom(virDomainPtr dom, + long long offset) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_RTC_CHANGE, + dom->id, dom->name, dom->uuid); + + if (ev) + ev->data.rtcChange.offset = offset; + + return ev; +} +virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, + long long offset) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_RTC_CHANGE, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) + ev->data.rtcChange.offset = offset; + + return ev; +} + /** * virDomainEventQueueFree: * @queue: pointer to the queue @@ -644,6 +672,12 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_RTC_CHANGE: + ((virConnectDomainEventRTCChangeCallback)cb)(conn, dom, + event->data.rtcChange.offset, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index d9c950d..8517ffe 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -115,6 +115,9 @@ virDomainEventPtr virDomainEventNewFromDef(virDomainDefPtr def, int type, int de virDomainEventPtr virDomainEventRebootNewFromDom(virDomainPtr dom); virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj); +virDomainEventPtr virDomainEventRTCChangeNewFromDom(virDomainPtr dom, long long offset); +virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, long long offset); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 38dc0a4..582a98e 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -216,6 +216,8 @@ virDomainEventNewFromObj; virDomainEventNewFromDef; virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; +virDomainEventRTCChangeNewFromDom; +virDomainEventRTCChangeNewFromObj; virDomainEventFree; virDomainEventDispatchDefaultFunc; virDomainEventDispatch; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 9dfc77e..a5cf246 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -877,10 +877,40 @@ qemuHandleDomainReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuHandleDomainRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + long long offset) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + + virDomainObjLock(vm); + event = virDomainEventRTCChangeNewFromObj(vm, offset); + + if (vm->def->clock.offset == VIR_DOMAIN_CLOCK_OFFSET_VARIABLE) + vm->def->clock.data.adjustment = offset; + + if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0) + VIR_WARN0("unable to save domain status with RTC change"); + + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, .domainReset = qemuHandleDomainReset, + .domainRTCChange = qemuHandleDomainRTCChange, }; static int diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index acc841b..c0e2384 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -791,6 +791,21 @@ int qemuMonitorEmitStop(qemuMonitorPtr mon) } +int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainRTCChange) + ret = mon->cb->domainRTCChange(mon, mon->vm, offset); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index cfb76b6..86d6b87 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -86,6 +86,9 @@ struct _qemuMonitorCallbacks { virDomainObjPtr vm); int (*domainStop)(qemuMonitorPtr mon, virDomainObjPtr vm); + int (*domainRTCChange)(qemuMonitorPtr mon, + virDomainObjPtr vm, + long long offset); }; @@ -122,6 +125,7 @@ int qemuMonitorEmitShutdown(qemuMonitorPtr mon); int qemuMonitorEmitReset(qemuMonitorPtr mon); int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); int qemuMonitorEmitStop(qemuMonitorPtr mon); +int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 7a263cb..ce7796a 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -49,6 +49,7 @@ static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr da static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data); struct { const char *type; @@ -58,6 +59,7 @@ struct { { "RESET", qemuMonitorJSONHandleReset, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "STOP", qemuMonitorJSONHandleStop, }, + { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, }; @@ -495,6 +497,16 @@ static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data A qemuMonitorEmitStop(mon); } +static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data) +{ + long long offset = 0; + if (virJSONValueObjectGetNumberLong(data, "offset", &offset) < 0) { + VIR_WARN0("missing offset in RTC change event"); + offset = 0; + } + qemuMonitorEmitRTCChange(mon, offset); +} + int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 1a64749..afaa26c 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6914,6 +6914,33 @@ remoteDomainReadEventReboot(virConnectPtr conn, XDR *xdr) } +static virDomainEventPtr +remoteDomainReadEventRTCChange(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_rtc_change_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_rtc_change_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall reboot event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + event = virDomainEventRTCChangeNewFromDom(dom, msg.offset); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_rtc_change_msg, (char *) &msg); + + virDomainFree(dom); + return event; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -8431,6 +8458,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, event = remoteDomainReadEventReboot(conn, xdr); break; + case REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE: + event = remoteDomainReadEventRTCChange(conn, xdr); + break; + default: DEBUG("Unexpected event proc %d", hdr->proc); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index f2a59c7..f78bea0 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3036,6 +3036,17 @@ xdr_remote_domain_event_reboot_msg (XDR *xdrs, remote_domain_event_reboot_msg *o } bool_t +xdr_remote_domain_event_rtc_change_msg (XDR *xdrs, remote_domain_event_rtc_change_msg *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_uint64_t (xdrs, &objp->offset)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 900a8b8..4afd5f3 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1719,6 +1719,12 @@ struct remote_domain_event_reboot_msg { remote_nonnull_domain dom; }; typedef struct remote_domain_event_reboot_msg remote_domain_event_reboot_msg; + +struct remote_domain_event_rtc_change_msg { + remote_nonnull_domain dom; + uint64_t offset; +}; +typedef struct remote_domain_event_rtc_change_msg remote_domain_event_rtc_change_msg; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1890,6 +1896,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, + REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, }; typedef enum remote_procedure remote_procedure; @@ -2202,6 +2209,7 @@ extern bool_t xdr_remote_domain_abort_job_args (XDR *, remote_domain_abort_job_ extern bool_t xdr_remote_domain_events_register_any_args (XDR *, remote_domain_events_register_any_args*); extern bool_t xdr_remote_domain_events_deregister_any_args (XDR *, remote_domain_events_deregister_any_args*); extern bool_t xdr_remote_domain_event_reboot_msg (XDR *, remote_domain_event_reboot_msg*); +extern bool_t xdr_remote_domain_event_rtc_change_msg (XDR *, remote_domain_event_rtc_change_msg*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2488,6 +2496,7 @@ extern bool_t xdr_remote_domain_abort_job_args (); extern bool_t xdr_remote_domain_events_register_any_args (); extern bool_t xdr_remote_domain_events_deregister_any_args (); extern bool_t xdr_remote_domain_event_reboot_msg (); +extern bool_t xdr_remote_domain_event_rtc_change_msg (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 72fe5b3..0e0d59a 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1529,6 +1529,11 @@ struct remote_domain_event_reboot_msg { remote_nonnull_domain dom; }; +struct remote_domain_event_rtc_change_msg { + remote_nonnull_domain dom; + hyper offset; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1718,7 +1723,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_ABORT_JOB = 164, REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, - REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167 + REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, + REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:58PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_RTC_CHANGE
This event includes the new UTC offset measured in seconds. Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque);
If the guest XML configuration for the <clock> is set to offset='variable', then the XML will automatically be updated with the new UTC offset value. This ensures that during migration/save/restore the new offset is preserved.
* daemon/remote.c: Dispatch RTC change events to client * examples/domain-events/events-c/event-test.c: Watch for RTC change events * include/libvirt/libvirt.h.in: Define new RTC change event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle RTC change events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for RTC changes and emit a libvirt RTC change event * src/remote/remote_driver.c: Receive and dispatch RTC change events to application * src/remote/remote_protocol.x: Wire protocol definition for RTC change events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for RTC_CHANGE event from QEMU monitor
Okay, in taht case we need to add a new event handler, [...]
--- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1842,6 +1842,10 @@ int virDomainAbortJob(virDomainPtr dom); typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, virDomainPtr dom, void *opaque);
+typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + long long utcoffset, + void *opaque);
I think the time unit of the offset should be documented there, and maybe a bit of explanation people may get confused and expect the offset to be relative to the previous offset instead of absolute [...]
--- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -49,6 +49,7 @@ static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr da static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data);
struct { const char *type; @@ -58,6 +59,7 @@ struct { { "RESET", qemuMonitorJSONHandleReset, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "STOP", qemuMonitorJSONHandleStop, }, + { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, };
The new event lookup registration, okay ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Mon, Mar 22, 2010 at 05:17:42PM +0100, Daniel Veillard wrote:
On Fri, Mar 19, 2010 at 03:38:58PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_RTC_CHANGE
This event includes the new UTC offset measured in seconds. Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque);
If the guest XML configuration for the <clock> is set to offset='variable', then the XML will automatically be updated with the new UTC offset value. This ensures that during migration/save/restore the new offset is preserved.
* daemon/remote.c: Dispatch RTC change events to client * examples/domain-events/events-c/event-test.c: Watch for RTC change events * include/libvirt/libvirt.h.in: Define new RTC change event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle RTC change events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for RTC changes and emit a libvirt RTC change event * src/remote/remote_driver.c: Receive and dispatch RTC change events to application * src/remote/remote_protocol.x: Wire protocol definition for RTC change events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for RTC_CHANGE event from QEMU monitor
Okay, in taht case we need to add a new event handler,
[...]
--- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1842,6 +1842,10 @@ int virDomainAbortJob(virDomainPtr dom); typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, virDomainPtr dom, void *opaque);
+typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + long long utcoffset, + void *opaque);
I think the time unit of the offset should be documented there, and maybe a bit of explanation people may get confused and expect the offset to be relative to the previous offset instead of absolute
Where should we put API docs for typedef's like this ? If I add the magic /** style comments in the header, will the docs generator pick them up, or should I put them in the libvirt.c file instead ? Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Mar 22, 2010 at 04:20:59PM +0000, Daniel P. Berrange wrote:
On Mon, Mar 22, 2010 at 05:17:42PM +0100, Daniel Veillard wrote:
[...]
--- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1842,6 +1842,10 @@ int virDomainAbortJob(virDomainPtr dom); typedef void (*virConnectDomainEventGenericCallback)(virConnectPtr conn, virDomainPtr dom, void *opaque);
+typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, + virDomainPtr dom, + long long utcoffset, + void *opaque);
I think the time unit of the offset should be documented there, and maybe a bit of explanation people may get confused and expect the offset to be relative to the previous offset instead of absolute
Where should we put API docs for typedef's like this ? If I add the magic /** style comments in the header, will the docs generator pick them up, or should I put them in the libvirt.c file instead ?
the generator recognize functions type like this so /** style should work here, yes :-) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This introduces a new event type VIR_DOMAIN_EVENT_ID_WATCHDOG This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, VIR_DOMAIN_EVENT_WATCHDOG_RESET, VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, } virDomainEventWatchdogAction; Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, virDomainPtr dom, int action, void *opaque); * daemon/remote.c: Dispatch watchdog events to client * examples/domain-events/events-c/event-test.c: Watch for watchdog events * include/libvirt/libvirt.h.in: Define new watchdg event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle watchdog events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for watchdogs and emit a libvirt watchdog event * src/remote/remote_driver.c: Receive and dispatch watchdog events to application * src/remote/remote_protocol.x: Wire protocol definition for watchdog events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for WATCHDOG event from QEMU monitor --- daemon/remote.c | 31 +++++++++++++++++++++++ daemon/remote_dispatch_table.h | 5 ++++ examples/domain-events/events-c/event-test.c | 22 +++++++++++++++- include/libvirt/libvirt.h.in | 13 ++++++++++ src/conf/domain_event.c | 34 ++++++++++++++++++++++++++ src/conf/domain_event.h | 3 ++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 23 +++++++++++++++++ src/qemu/qemu_monitor.c | 15 +++++++++++ src/qemu/qemu_monitor.h | 4 +++ src/qemu/qemu_monitor_json.c | 24 ++++++++++++++++++ src/remote/remote_driver.c | 31 +++++++++++++++++++++++ src/remote/remote_protocol.c | 13 +++++++++- src/remote/remote_protocol.h | 11 +++++++- src/remote/remote_protocol.x | 8 +++++- 15 files changed, 234 insertions(+), 5 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index ac62f43..b1562b6 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -187,10 +187,41 @@ static int remoteRelayDomainEventRTCChange(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int action, + void *opaque) +{ + struct qemud_client *client = opaque; + remote_domain_event_watchdog_msg data; + + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain watchdog event %s %d %d", dom->name, dom->id, action); + + virMutexLock(&client->lock); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + data.action = action; + + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT_WATCHDOG, + (xdrproc_t)xdr_remote_domain_event_watchdog_msg, &data); + + virMutexUnlock(&client->lock); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index e5cc724..11520b9 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -847,3 +847,8 @@ .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* Async event DomainEventWatchdog => 169 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 97b37de..997af63 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -193,6 +193,17 @@ static int myDomainEventRTCChangeCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int action, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) watchdog action=%d\n", __func__, virDomainGetName(dom), + virDomainGetID(dom), action); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -312,6 +323,7 @@ int main(int argc, char **argv) int callback2ret = -1; int callback3ret = -1; int callback4ret = -1; + int callback5ret = -1; struct sigaction action_stop = { .sa_handler = stop @@ -359,11 +371,17 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_RTC_CHANGE, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventRTCChangeCallback), strdup("callback rtcchange"), myFreeFunc); + callback5ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_WATCHDOG, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventWatchdogCallback), + strdup("callback watchdog"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && - (callback4ret != -1)) { + (callback4ret != -1) && + (callback5ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -403,7 +421,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback2ret); virConnectDomainEventDeregisterAny(dconn, callback3ret); virConnectDomainEventDeregisterAny(dconn, callback4ret); - + virConnectDomainEventDeregisterAny(dconn, callback5ret); } DEBUG0("Closing connection"); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index edb0a6e..7bee89c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1846,6 +1846,18 @@ typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, + VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, + VIR_DOMAIN_EVENT_WATCHDOG_RESET, + VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, + VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, + VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, +} virDomainEventWatchdogAction; +typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, + virDomainPtr dom, + int action, + void *opaque); /* Use this to cast the event specific callback into the generic one * for use for virDomainEventRegister */ @@ -1856,6 +1868,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_LIFECYCLE = 0, /* virConnectDomainEventCallback */ VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_RTC_CHANGE = 2, /* virConnectDomainEventRTCChangeCallback */ + VIR_DOMAIN_EVENT_ID_WATCHDOG = 3, /* virConnectDomainEventWatchdogCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 79dfbac..4482599 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -67,6 +67,9 @@ struct _virDomainEvent { struct { long long offset; } rtcChange; + struct { + int action; + } watchdog; } data; }; @@ -562,6 +565,31 @@ virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, return ev; } +virDomainEventPtr virDomainEventWatchdogNewFromDom(virDomainPtr dom, + int action) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_WATCHDOG, + dom->id, dom->name, dom->uuid); + + if (ev) + ev->data.watchdog.action = action; + + return ev; +} +virDomainEventPtr virDomainEventWatchdogNewFromObj(virDomainObjPtr obj, + int action) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_WATCHDOG, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) + ev->data.watchdog.action = action; + + return ev; +} + /** * virDomainEventQueueFree: * @queue: pointer to the queue @@ -678,6 +706,12 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_WATCHDOG: + ((virConnectDomainEventWatchdogCallback)cb)(conn, dom, + event->data.watchdog.action, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index 8517ffe..b791614 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -118,6 +118,9 @@ virDomainEventPtr virDomainEventRebootNewFromObj(virDomainObjPtr obj); virDomainEventPtr virDomainEventRTCChangeNewFromDom(virDomainPtr dom, long long offset); virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, long long offset); +virDomainEventPtr virDomainEventWatchdogNewFromDom(virDomainPtr dom, int action); +virDomainEventPtr virDomainEventWatchdogNewFromObj(virDomainObjPtr obj, int action); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 582a98e..17902b1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -218,6 +218,8 @@ virDomainEventRebootNewFromDom; virDomainEventRebootNewFromObj; virDomainEventRTCChangeNewFromDom; virDomainEventRTCChangeNewFromObj; +virDomainEventWatchdogNewFromDom; +virDomainEventWatchdogNewFromObj; virDomainEventFree; virDomainEventDispatchDefaultFunc; virDomainEventDispatch; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a5cf246..ceba5db 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -906,11 +906,34 @@ qemuHandleDomainRTCChange(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuHandleDomainWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + int action) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + + virDomainObjLock(vm); + event = virDomainEventWatchdogNewFromObj(vm, action); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, .domainReset = qemuHandleDomainReset, .domainRTCChange = qemuHandleDomainRTCChange, + .domainWatchdog = qemuHandleDomainWatchdog, }; static int diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index c0e2384..d7116c9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -806,6 +806,21 @@ int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset) } +int qemuMonitorEmitWatchdog(qemuMonitorPtr mon, int action) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainWatchdog) + ret = mon->cb->domainWatchdog(mon, mon->vm, action); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 86d6b87..3558dba 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -89,6 +89,9 @@ struct _qemuMonitorCallbacks { int (*domainRTCChange)(qemuMonitorPtr mon, virDomainObjPtr vm, long long offset); + int (*domainWatchdog)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int action); }; @@ -126,6 +129,7 @@ int qemuMonitorEmitReset(qemuMonitorPtr mon); int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); int qemuMonitorEmitStop(qemuMonitorPtr mon); int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset); +int qemuMonitorEmitWatchdog(qemuMonitorPtr mon, int action); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index ce7796a..d7fbf38 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -50,6 +50,7 @@ static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data) static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data); struct { const char *type; @@ -60,6 +61,7 @@ struct { { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "STOP", qemuMonitorJSONHandleStop, }, { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, + { "WATCHDOG", qemuMonitorJSONHandleWatchdog, }, }; @@ -507,6 +509,28 @@ static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr d qemuMonitorEmitRTCChange(mon, offset); } +VIR_ENUM_DECL(qemuMonitorWatchdogAction) +VIR_ENUM_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, + "none", "pause", "reset", "poweroff" "shutdown", "debug"); + +static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *action; + int actionID; + if (!(action = virJSONValueObjectGetString(data, "action"))) { + VIR_WARN0("missing action in watchdog event"); + } + if (action) { + if ((actionID = qemuMonitorWatchdogActionTypeFromString(action)) < 0) { + VIR_WARN("unknown action %s in watchdog event", action); + actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE; + } + } else { + actionID = VIR_DOMAIN_EVENT_WATCHDOG_NONE; + } + qemuMonitorEmitRTCChange(mon, actionID); +} + int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index afaa26c..8652083 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6941,6 +6941,33 @@ remoteDomainReadEventRTCChange(virConnectPtr conn, XDR *xdr) } +static virDomainEventPtr +remoteDomainReadEventWatchdog(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_watchdog_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_watchdog_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall reboot event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + event = virDomainEventWatchdogNewFromDom(dom, msg.action); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_watchdog_msg, (char *) &msg); + + virDomainFree(dom); + return event; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -8462,6 +8489,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, event = remoteDomainReadEventRTCChange(conn, xdr); break; + case REMOTE_PROC_DOMAIN_EVENT_WATCHDOG: + event = remoteDomainReadEventWatchdog(conn, xdr); + break; + default: DEBUG("Unexpected event proc %d", hdr->proc); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index f78bea0..8a6c353 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3041,7 +3041,18 @@ xdr_remote_domain_event_rtc_change_msg (XDR *xdrs, remote_domain_event_rtc_chang if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) return FALSE; - if (!xdr_uint64_t (xdrs, &objp->offset)) + if (!xdr_int64_t (xdrs, &objp->offset)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_event_watchdog_msg (XDR *xdrs, remote_domain_event_watchdog_msg *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_int (xdrs, &objp->action)) return FALSE; return TRUE; } diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index 4afd5f3..f68989f 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1722,9 +1722,15 @@ typedef struct remote_domain_event_reboot_msg remote_domain_event_reboot_msg; struct remote_domain_event_rtc_change_msg { remote_nonnull_domain dom; - uint64_t offset; + int64_t offset; }; typedef struct remote_domain_event_rtc_change_msg remote_domain_event_rtc_change_msg; + +struct remote_domain_event_watchdog_msg { + remote_nonnull_domain dom; + int action; +}; +typedef struct remote_domain_event_watchdog_msg remote_domain_event_watchdog_msg; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1897,6 +1903,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, + REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169, }; typedef enum remote_procedure remote_procedure; @@ -2210,6 +2217,7 @@ extern bool_t xdr_remote_domain_events_register_any_args (XDR *, remote_domain_ extern bool_t xdr_remote_domain_events_deregister_any_args (XDR *, remote_domain_events_deregister_any_args*); extern bool_t xdr_remote_domain_event_reboot_msg (XDR *, remote_domain_event_reboot_msg*); extern bool_t xdr_remote_domain_event_rtc_change_msg (XDR *, remote_domain_event_rtc_change_msg*); +extern bool_t xdr_remote_domain_event_watchdog_msg (XDR *, remote_domain_event_watchdog_msg*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2497,6 +2505,7 @@ extern bool_t xdr_remote_domain_events_register_any_args (); extern bool_t xdr_remote_domain_events_deregister_any_args (); extern bool_t xdr_remote_domain_event_reboot_msg (); extern bool_t xdr_remote_domain_event_rtc_change_msg (); +extern bool_t xdr_remote_domain_event_watchdog_msg (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 0e0d59a..f793968 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1534,6 +1534,11 @@ struct remote_domain_event_rtc_change_msg { hyper offset; }; +struct remote_domain_event_watchdog_msg { + remote_nonnull_domain dom; + int action; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1724,7 +1729,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENTS_REGISTER_ANY = 165, REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, - REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168 + REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, + REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On Fri, Mar 19, 2010 at 03:38:59PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken as a result of the watchdog triggering
typedef enum { VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, VIR_DOMAIN_EVENT_WATCHDOG_RESET, VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, } virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, virDomainPtr dom, int action, void *opaque); [...] diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index edb0a6e..7bee89c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1846,6 +1846,18 @@ typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, + VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, + VIR_DOMAIN_EVENT_WATCHDOG_RESET, + VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, + VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, + VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, +} virDomainEventWatchdogAction;
Hum, what is NONE for ? Sounds more confusing than anything else to me. Also a short description would be useful, for example is the watchdog pause an user requested pause ? And what does the debug carries ?
+typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, + virDomainPtr dom, + int action, + void *opaque);
[...] ACK, but we really to provide the semantic for PAUSE and DEBUG at the API level not just wire the underlying QEMU events. Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

On Mon, Mar 22, 2010 at 05:37:02PM +0100, Daniel Veillard wrote:
On Fri, Mar 19, 2010 at 03:38:59PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_WATCHDOG
This event includes the action that is about to be taken as a result of the watchdog triggering
typedef enum { VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, VIR_DOMAIN_EVENT_WATCHDOG_RESET, VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, } virDomainEventWatchdogAction;
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, virDomainPtr dom, int action, void *opaque); [...] diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index edb0a6e..7bee89c 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1846,6 +1846,18 @@ typedef void (*virConnectDomainEventRTCChangeCallback)(virConnectPtr conn, virDomainPtr dom, long long utcoffset, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, + VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, + VIR_DOMAIN_EVENT_WATCHDOG_RESET, + VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, + VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, + VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, +} virDomainEventWatchdogAction;
Hum, what is NONE for ? Sounds more confusing than anything else to me. Also a short description would be useful, for example is the watchdog pause an user requested pause ? And what does the debug carries ?
NONE means that QEMU just ignores the watchdog, even when it triggers DEBUG means that QEMU ignores it, but logs a message on stderr (which ends up in /var/log/libvirt/qemu/$GUEST.log in our case) PAUSE means that QEMU stops the guest CPUs when the watchdog fires. Regards, Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

On Mon, Mar 22, 2010 at 04:43:25PM +0000, Daniel P. Berrange wrote:
On Mon, Mar 22, 2010 at 05:37:02PM +0100, Daniel Veillard wrote:
On Fri, Mar 19, 2010 at 03:38:59PM +0000, Daniel P. Berrange wrote:
+typedef enum { + VIR_DOMAIN_EVENT_WATCHDOG_NONE = 0, + VIR_DOMAIN_EVENT_WATCHDOG_PAUSE, + VIR_DOMAIN_EVENT_WATCHDOG_RESET, + VIR_DOMAIN_EVENT_WATCHDOG_POWEROFF, + VIR_DOMAIN_EVENT_WATCHDOG_SHUTDOWN, + VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, +} virDomainEventWatchdogAction;
Hum, what is NONE for ? Sounds more confusing than anything else to me. Also a short description would be useful, for example is the watchdog pause an user requested pause ? And what does the debug carries ?
NONE means that QEMU just ignores the watchdog, even when it triggers
DEBUG means that QEMU ignores it, but logs a message on stderr (which ends up in /var/log/libvirt/qemu/$GUEST.log in our case)
PAUSE means that QEMU stops the guest CPUs when the watchdog fires.
Ah okay, I was thinking PAUSE meant the watchdog had been paused momentarily (but there was no restart !) We really need the semantic to be explained in the header :) Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This introduces a new event type VIR_DOMAIN_EVENT_ID_IO_ERROR This event includes the action that is about to be taken as a result of the watchdog triggering typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction; In addition is has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file) Thus there is a new callback definition for this event type typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque); This is currently wired up to the QEMU block IO error events * daemon/remote.c: Dispatch IO error events to client * examples/domain-events/events-c/event-test.c: Watch for IO error events * include/libvirt/libvirt.h.in: Define new IO error event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle IO error events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for block IO errors and emit a libvirt IO error event * src/remote/remote_driver.c: Receive and dispatch IO error events to application * src/remote/remote_protocol.x: Wire protocol definition for IO error events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for BLOCK_IO_ERROR event from QEMU monitor --- daemon/remote.c | 35 +++++++++++++++ daemon/remote_dispatch_table.h | 5 ++ examples/domain-events/events-c/event-test.c | 23 ++++++++++- include/libvirt/libvirt.h.in | 12 +++++ src/conf/domain_event.c | 59 ++++++++++++++++++++++++++ src/conf/domain_event.h | 9 ++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 57 +++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 17 +++++++ src/qemu/qemu_monitor.h | 7 +++ src/qemu/qemu_monitor_json.c | 38 ++++++++++++++++- src/remote/remote_driver.c | 34 +++++++++++++++ src/remote/remote_protocol.c | 15 +++++++ src/remote/remote_protocol.h | 11 +++++ src/remote/remote_protocol.x | 10 ++++- 15 files changed, 330 insertions(+), 4 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index b1562b6..e2912a1 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -217,11 +217,46 @@ static int remoteRelayDomainEventWatchdog(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque) +{ + struct qemud_client *client = opaque; + remote_domain_event_io_error_msg data; + + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain io error %s %d %s %s %d", dom->name, dom->id, srcPath, devAlias, action); + + virMutexLock(&client->lock); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + data.srcPath = (char*)srcPath; + data.devAlias = (char*)devAlias; + data.action = action; + + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT_IO_ERROR, + (xdrproc_t)xdr_remote_domain_event_io_error_msg, &data); + + virMutexUnlock(&client->lock); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 11520b9..31d652d 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -852,3 +852,8 @@ .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* Async event DomainEventIoError => 170 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index 997af63..bb3c7bb 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -204,6 +204,19 @@ static int myDomainEventWatchdogCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventIOErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque ATTRIBUTE_UNUSED) +{ + printf("%s EVENT: Domain %s(%d) io error path=%s alias=%s action=%d\n", __func__, virDomainGetName(dom), + virDomainGetID(dom), srcPath, devAlias, action); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -324,6 +337,7 @@ int main(int argc, char **argv) int callback3ret = -1; int callback4ret = -1; int callback5ret = -1; + int callback6ret = -1; struct sigaction action_stop = { .sa_handler = stop @@ -376,12 +390,18 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_WATCHDOG, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventWatchdogCallback), strdup("callback watchdog"), myFreeFunc); + callback6ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_IO_ERROR, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventIOErrorCallback), + strdup("callback io error"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && (callback4ret != -1) && - (callback5ret != -1)) { + (callback5ret != -1) && + (callback6ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -422,6 +442,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback3ret); virConnectDomainEventDeregisterAny(dconn, callback4ret); virConnectDomainEventDeregisterAny(dconn, callback5ret); + virConnectDomainEventDeregisterAny(dconn, callback6ret); } DEBUG0("Closing connection"); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 7bee89c..bffd9ed 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1858,6 +1858,17 @@ typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, virDomainPtr dom, int action, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, + VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, + VIR_DOMAIN_EVENT_IO_ERROR_REPORT, +} virDomainEventIOErrorAction; +typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque); /* Use this to cast the event specific callback into the generic one * for use for virDomainEventRegister */ @@ -1869,6 +1880,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_RTC_CHANGE = 2, /* virConnectDomainEventRTCChangeCallback */ VIR_DOMAIN_EVENT_ID_WATCHDOG = 3, /* virConnectDomainEventWatchdogCallback */ + VIR_DOMAIN_EVENT_ID_IO_ERROR = 4, /* virConnectDomainEventIOErrorCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 4482599..77b52ef 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -70,6 +70,11 @@ struct _virDomainEvent { struct { int action; } watchdog; + struct { + char *srcPath; + char *devAlias; + int action; + } ioError; } data; }; @@ -458,6 +463,11 @@ void virDomainEventFree(virDomainEventPtr event) if (!event) return; + if (event->eventID == VIR_DOMAIN_EVENT_ID_IO_ERROR) { + VIR_FREE(event->data.ioError.srcPath); + VIR_FREE(event->data.ioError.devAlias); + } + VIR_FREE(event->dom.name); VIR_FREE(event); } @@ -590,6 +600,47 @@ virDomainEventPtr virDomainEventWatchdogNewFromObj(virDomainObjPtr obj, return ev; } +virDomainEventPtr virDomainEventIOErrorNewFromDom(virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_IO_ERROR, + dom->id, dom->name, dom->uuid); + + if (ev) { + ev->data.ioError.action = action; + if (!(ev->data.ioError.srcPath = strdup(srcPath)) || + !(ev->data.ioError.devAlias = strdup(devAlias))) { + virDomainEventFree(ev); + ev = NULL; + } + } + + return ev; +} +virDomainEventPtr virDomainEventIOErrorNewFromObj(virDomainObjPtr obj, + const char *srcPath, + const char *devAlias, + int action) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_IO_ERROR, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) { + ev->data.watchdog.action = action; + if (!(ev->data.ioError.srcPath = strdup(srcPath)) || + !(ev->data.ioError.devAlias = strdup(devAlias))) { + virDomainEventFree(ev); + ev = NULL; + } + } + + return ev; +} + /** * virDomainEventQueueFree: * @queue: pointer to the queue @@ -712,6 +763,14 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_IO_ERROR: + ((virConnectDomainEventIOErrorCallback)cb)(conn, dom, + event->data.ioError.srcPath, + event->data.ioError.devAlias, + event->data.ioError.action, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index b791614..dbf9288 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -121,6 +121,15 @@ virDomainEventPtr virDomainEventRTCChangeNewFromObj(virDomainObjPtr obj, long lo virDomainEventPtr virDomainEventWatchdogNewFromDom(virDomainPtr dom, int action); virDomainEventPtr virDomainEventWatchdogNewFromObj(virDomainObjPtr obj, int action); +virDomainEventPtr virDomainEventIOErrorNewFromDom(virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action); +virDomainEventPtr virDomainEventIOErrorNewFromObj(virDomainObjPtr obj, + const char *srcPath, + const char *devAlias, + int action); + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 17902b1..4578762 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -220,6 +220,8 @@ virDomainEventRTCChangeNewFromDom; virDomainEventRTCChangeNewFromObj; virDomainEventWatchdogNewFromDom; virDomainEventWatchdogNewFromObj; +virDomainEventIOErrorNewFromDom; +virDomainEventIOErrorNewFromObj; virDomainEventFree; virDomainEventDispatchDefaultFunc; virDomainEventDispatch; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ceba5db..71cca5c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -747,6 +747,26 @@ findDomainDiskByPath(virDomainObjPtr vm, return NULL; } +static virDomainDiskDefPtr +findDomainDiskByAlias(virDomainObjPtr vm, + const char *alias) +{ + int i; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk; + + disk = vm->def->disks[i]; + if (disk->info.alias != NULL && STREQ(disk->info.alias, alias)) + return disk; + } + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("no disk found with alias %s"), + alias); + return NULL; +} + static int getVolumeQcowPassphrase(virConnectPtr conn, virDomainDiskDefPtr disk, @@ -928,12 +948,49 @@ qemuHandleDomainWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuHandleDomainIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *diskAlias, + int action) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + const char *srcPath; + const char *devAlias; + virDomainDiskDefPtr disk; + + virDomainObjLock(vm); + disk = findDomainDiskByAlias(vm, diskAlias); + + if (disk) { + srcPath = disk->src; + devAlias = disk->info.alias; + } else { + srcPath = ""; + devAlias = ""; + } + + event = virDomainEventIOErrorNewFromObj(vm, srcPath, devAlias, action); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, .domainReset = qemuHandleDomainReset, .domainRTCChange = qemuHandleDomainRTCChange, .domainWatchdog = qemuHandleDomainWatchdog, + .domainIOError = qemuHandleDomainIOError, }; static int diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index d7116c9..f320df9 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -821,6 +821,23 @@ int qemuMonitorEmitWatchdog(qemuMonitorPtr mon, int action) } +int qemuMonitorEmitIOError(qemuMonitorPtr mon, + const char *diskAlias, + int action) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainIOError) + ret = mon->cb->domainIOError(mon, mon->vm, diskAlias, action); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 3558dba..64ae153 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -92,6 +92,10 @@ struct _qemuMonitorCallbacks { int (*domainWatchdog)(qemuMonitorPtr mon, virDomainObjPtr vm, int action); + int (*domainIOError)(qemuMonitorPtr mon, + virDomainObjPtr vm, + const char *diskAlias, + int action); }; @@ -130,6 +134,9 @@ int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); int qemuMonitorEmitStop(qemuMonitorPtr mon); int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset); int qemuMonitorEmitWatchdog(qemuMonitorPtr mon, int action); +int qemuMonitorEmitIOError(qemuMonitorPtr mon, + const char *diskAlias, + int action); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index d7fbf38..e2d7744 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -51,6 +51,7 @@ static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr d static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data); struct { const char *type; @@ -62,6 +63,7 @@ struct { { "STOP", qemuMonitorJSONHandleStop, }, { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, { "WATCHDOG", qemuMonitorJSONHandleWatchdog, }, + { "DISK_IO_ERROR", qemuMonitorJSONHandleIOError, }, }; @@ -510,8 +512,8 @@ static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr d } VIR_ENUM_DECL(qemuMonitorWatchdogAction) -VIR_ENUM_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG, - "none", "pause", "reset", "poweroff" "shutdown", "debug"); +VIR_ENUM_IMPL(qemuMonitorWatchdogAction, VIR_DOMAIN_EVENT_WATCHDOG_DEBUG + 1, + "none", "pause", "reset", "poweroff", "shutdown", "debug"); static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data) { @@ -531,6 +533,38 @@ static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr da qemuMonitorEmitRTCChange(mon, actionID); } +VIR_ENUM_DECL(qemuMonitorIOErrorAction) +VIR_ENUM_IMPL(qemuMonitorIOErrorAction, VIR_DOMAIN_EVENT_IO_ERROR_REPORT + 1, + "ignore", "stop", "report"); + + +static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *device; + const char *action; + int actionID; + + /* Throughout here we try our best to carry on upon errors, + since its imporatant to get as much info as possible out + to the application */ + + if ((action = virJSONValueObjectGetString(data, "action")) == NULL) { + VIR_WARN0("Missing action in disk io error event"); + action = "ignore"; + } + + if ((device = virJSONValueObjectGetString(data, "device")) == NULL) { + VIR_WARN0("missing device in disk io error event"); + } + + if ((actionID = qemuMonitorIOErrorActionTypeFromString(action)) < 0) { + VIR_WARN("unknown disk io error action '%s'", action); + actionID = VIR_DOMAIN_EVENT_IO_ERROR_NONE; + } + + qemuMonitorEmitIOError(mon, device, actionID); +} + int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 8652083..068ad9b 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6968,6 +6968,36 @@ remoteDomainReadEventWatchdog(virConnectPtr conn, XDR *xdr) } +static virDomainEventPtr +remoteDomainReadEventIOError(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_io_error_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_io_error_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall reboot event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + event = virDomainEventIOErrorNewFromDom(dom, + msg.srcPath, + msg.devAlias, + msg.action); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_io_error_msg, (char *) &msg); + + virDomainFree(dom); + return event; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -8493,6 +8523,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, event = remoteDomainReadEventWatchdog(conn, xdr); break; + case REMOTE_PROC_DOMAIN_EVENT_IO_ERROR: + event = remoteDomainReadEventIOError(conn, xdr); + break; + default: DEBUG("Unexpected event proc %d", hdr->proc); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 8a6c353..79dfb98 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3058,6 +3058,21 @@ xdr_remote_domain_event_watchdog_msg (XDR *xdrs, remote_domain_event_watchdog_ms } bool_t +xdr_remote_domain_event_io_error_msg (XDR *xdrs, remote_domain_event_io_error_msg *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->srcPath)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->devAlias)) + return FALSE; + if (!xdr_int (xdrs, &objp->action)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index f68989f..cddd284 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1731,6 +1731,14 @@ struct remote_domain_event_watchdog_msg { int action; }; typedef struct remote_domain_event_watchdog_msg remote_domain_event_watchdog_msg; + +struct remote_domain_event_io_error_msg { + remote_nonnull_domain dom; + remote_nonnull_string srcPath; + remote_nonnull_string devAlias; + int action; +}; +typedef struct remote_domain_event_io_error_msg remote_domain_event_io_error_msg; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1904,6 +1912,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169, + REMOTE_PROC_DOMAIN_EVENT_IO_ERROR = 170, }; typedef enum remote_procedure remote_procedure; @@ -2218,6 +2227,7 @@ extern bool_t xdr_remote_domain_events_deregister_any_args (XDR *, remote_domai extern bool_t xdr_remote_domain_event_reboot_msg (XDR *, remote_domain_event_reboot_msg*); extern bool_t xdr_remote_domain_event_rtc_change_msg (XDR *, remote_domain_event_rtc_change_msg*); extern bool_t xdr_remote_domain_event_watchdog_msg (XDR *, remote_domain_event_watchdog_msg*); +extern bool_t xdr_remote_domain_event_io_error_msg (XDR *, remote_domain_event_io_error_msg*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2506,6 +2516,7 @@ extern bool_t xdr_remote_domain_events_deregister_any_args (); extern bool_t xdr_remote_domain_event_reboot_msg (); extern bool_t xdr_remote_domain_event_rtc_change_msg (); extern bool_t xdr_remote_domain_event_watchdog_msg (); +extern bool_t xdr_remote_domain_event_io_error_msg (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index f793968..3cdc99b 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1539,6 +1539,13 @@ struct remote_domain_event_watchdog_msg { int action; }; +struct remote_domain_event_io_error_msg { + remote_nonnull_domain dom; + remote_nonnull_string srcPath; + remote_nonnull_string devAlias; + int action; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1730,7 +1737,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENTS_DEREGISTER_ANY = 166, REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, - REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169 + REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169, + REMOTE_PROC_DOMAIN_EVENT_IO_ERROR = 170 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On 03/19/2010 09:39 AM, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_IO_ERROR
This event includes the action that is about to be taken as a result of the watchdog triggering
typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction;
In addition is has the source path of the disk that had the
s/is/it/
+static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *device; + const char *action; + int actionID; + + /* Throughout here we try our best to carry on upon errors, + since its imporatant to get as much info as possible out
s/its imporatant/it's important/ -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Mar 19, 2010 at 03:39:00PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_IO_ERROR
This event includes the action that is about to be taken as a result of the watchdog triggering
typedef enum { VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, VIR_DOMAIN_EVENT_IO_ERROR_REPORT, } virDomainEventIOErrorAction;
okay
In addition is has the source path of the disk that had the error and its unique device alias. It does not include the target device name (/dev/sda), since this would preclude triggering IO errors from other file backed devices (eg serial ports connected to a file)
Thus there is a new callback definition for this event type
typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, virDomainPtr dom, const char *srcPath, const char *devAlias, int action, void *opaque);
okay [...]
--- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1858,6 +1858,17 @@ typedef void (*virConnectDomainEventWatchdogCallback)(virConnectPtr conn, virDomainPtr dom, int action, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_IO_ERROR_NONE = 0, + VIR_DOMAIN_EVENT_IO_ERROR_PAUSE, + VIR_DOMAIN_EVENT_IO_ERROR_REPORT, +} virDomainEventIOErrorAction;
again let's describe the parameters in the header as this is the only place (beside those list archives) where the semantic is described
+typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, + virDomainPtr dom, + const char *srcPath, + const char *devAlias, + int action, + void *opaque);
/* Use this to cast the event specific callback into the generic one * for use for virDomainEventRegister */ @@ -1869,6 +1880,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_REBOOT = 1, /* virConnectDomainEventGenericCallback */ VIR_DOMAIN_EVENT_ID_RTC_CHANGE = 2, /* virConnectDomainEventRTCChangeCallback */ VIR_DOMAIN_EVENT_ID_WATCHDOG = 3, /* virConnectDomainEventWatchdogCallback */ + VIR_DOMAIN_EVENT_ID_IO_ERROR = 4, /* virConnectDomainEventIOErrorCallback */
/* * NB: this enum value will increase over time as new events are
[...]
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ceba5db..71cca5c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -747,6 +747,26 @@ findDomainDiskByPath(virDomainObjPtr vm, return NULL; }
+static virDomainDiskDefPtr +findDomainDiskByAlias(virDomainObjPtr vm, + const char *alias) +{ + int i; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk; + + disk = vm->def->disks[i]; + if (disk->info.alias != NULL && STREQ(disk->info.alias, alias)) + return disk; + } + + qemuReportError(VIR_ERR_INTERNAL_ERROR, + _("no disk found with alias %s"), + alias); + return NULL; +} + static int getVolumeQcowPassphrase(virConnectPtr conn, virDomainDiskDefPtr disk, @@ -928,12 +948,49 @@ qemuHandleDomainWatchdog(qemuMonitorPtr mon ATTRIBUTE_UNUSED, }
+static int +qemuHandleDomainIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + const char *diskAlias, + int action) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + const char *srcPath; + const char *devAlias; + virDomainDiskDefPtr disk; + + virDomainObjLock(vm); + disk = findDomainDiskByAlias(vm, diskAlias); + + if (disk) { + srcPath = disk->src; + devAlias = disk->info.alias; + } else { + srcPath = ""; + devAlias = ""; + } + + event = virDomainEventIOErrorNewFromObj(vm, srcPath, devAlias, action); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, .domainReset = qemuHandleDomainReset, .domainRTCChange = qemuHandleDomainRTCChange, .domainWatchdog = qemuHandleDomainWatchdog, + .domainIOError = qemuHandleDomainIOError, };
okay, ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/

This introduces a new event type VIR_DOMAIN_EVENT_ID_GRAPHICS The same event can be emitted in 3 scenarioes typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase; Connect/disconnect are triggered at socket accept/close. The initailize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop This event comes with *alot* of potential information - IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities. This results in a very complicated callback :-( typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType; struct _virDomainEventGraphicsAddress { int family; const char *node; const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque); The wire protocol is similarly complex struct remote_domain_event_graphics_address { int family; remote_nonnull_string node; remote_nonnull_string service; }; const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; struct remote_domain_event_graphics_identity { remote_nonnull_string type; remote_nonnull_string name; }; struct remote_domain_event_graphics_msg { remote_nonnull_domain dom; int phase; remote_domain_event_graphics_address local; remote_domain_event_graphics_address remote; remote_nonnull_string authScheme; remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; }; This is currently implemented in QEMU for the VNC graphics protocol, but designed to be usable with SPICE graphics in the future too. * daemon/remote.c: Dispatch graphics events to client * examples/domain-events/events-c/event-test.c: Watch for graphics events * include/libvirt/libvirt.h.in: Define new graphics event ID and callback signature * src/conf/domain_event.c, src/conf/domain_event.h, src/libvirt_private.syms: Extend API to handle graphics events * src/qemu/qemu_driver.c: Connect to the QEMU monitor event for VNC events and emit a libvirt graphics event * src/remote/remote_driver.c: Receive and dispatch graphics events to application * src/remote/remote_protocol.x: Wire protocol definition for graphics events * src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h, src/qemu/qemu_monitor_json.c: Watch for VNC_CONNECTED, VNC_INITIALIZED & VNC_DISCONNETED events from QEMU monitor --- daemon/remote.c | 65 ++++++++++++++++++ daemon/remote_dispatch_table.h | 5 ++ examples/domain-events/events-c/event-test.c | 51 ++++++++++++++- include/libvirt/libvirt.h.in | 41 +++++++++++ src/conf/domain_event.c | 94 +++++++++++++++++++++++++- src/conf/domain_event.h | 15 ++++ src/libvirt_private.syms | 2 + src/qemu/qemu_driver.c | 93 +++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 30 ++++++++ src/qemu/qemu_monitor.h | 24 +++++++ src/qemu/qemu_monitor_json.c | 74 ++++++++++++++++++++ src/remote/remote_driver.c | 92 +++++++++++++++++++++++++ src/remote/remote_protocol.c | 45 ++++++++++++ src/remote/remote_protocol.h | 34 +++++++++ src/remote/remote_protocol.x | 26 +++++++- 15 files changed, 688 insertions(+), 3 deletions(-) diff --git a/daemon/remote.c b/daemon/remote.c index e2912a1..888be48 100644 --- a/daemon/remote.c +++ b/daemon/remote.c @@ -251,12 +251,77 @@ static int remoteRelayDomainEventIOError(virConnectPtr conn ATTRIBUTE_UNUSED, } +static int remoteRelayDomainEventGraphics(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject, + void *opaque) +{ + struct qemud_client *client = opaque; + remote_domain_event_graphics_msg data; + int i; + + if (!client) + return -1; + + REMOTE_DEBUG("Relaying domain graphics event %s %d %d - %d %s %s - %d %s %s - %s", dom->name, dom->id, phase, + local->family, local->service, local->node, + remote->family, remote->service, remote->node, + authScheme); + + REMOTE_DEBUG("Subject %d", subject->nidentity); + for (i = 0 ; i < subject->nidentity ; i++) { + REMOTE_DEBUG(" %s=%s", subject->identities[i].type, subject->identities[i].name); + } + + virMutexLock(&client->lock); + + /* build return data */ + memset(&data, 0, sizeof data); + make_nonnull_domain (&data.dom, dom); + data.phase = phase; + data.authScheme = (char*)authScheme; + + data.local.family = local->family; + data.local.node = (char *)local->node; + data.local.service = (char *)local->service; + + data.remote.family = remote->family; + data.remote.node = (char*)remote->node; + data.remote.service = (char*)remote->service; + + data.subject.subject_len = subject->nidentity; + if (VIR_ALLOC_N(data.subject.subject_val, data.subject.subject_len) < 0) { + VIR_WARN0("cannot allocate memory for graphics event subject"); + return -1; + } + for (i = 0 ; i < data.subject.subject_len ; i++) { + data.subject.subject_val[i].type = (char*)subject->identities[i].type; + data.subject.subject_val[i].name = (char*)subject->identities[i].name; + } + + remoteDispatchDomainEventSend (client, + REMOTE_PROC_DOMAIN_EVENT_GRAPHICS, + (xdrproc_t)xdr_remote_domain_event_graphics_msg, &data); + + VIR_FREE(data.subject.subject_val); + + virMutexUnlock(&client->lock); + + return 0; +} + + static virConnectDomainEventGenericCallback domainEventCallbacks[] = { VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventLifecycle), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventReboot), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventRTCChange), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventWatchdog), VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventIOError), + VIR_DOMAIN_EVENT_CALLBACK(remoteRelayDomainEventGraphics), }; verify(ARRAY_CARDINALITY(domainEventCallbacks) == VIR_DOMAIN_EVENT_ID_LAST); diff --git a/daemon/remote_dispatch_table.h b/daemon/remote_dispatch_table.h index 31d652d..6459038 100644 --- a/daemon/remote_dispatch_table.h +++ b/daemon/remote_dispatch_table.h @@ -857,3 +857,8 @@ .args_filter = (xdrproc_t) xdr_void, .ret_filter = (xdrproc_t) xdr_void, }, +{ /* Async event DomainEventGraphics => 171 */ + .fn = NULL, + .args_filter = (xdrproc_t) xdr_void, + .ret_filter = (xdrproc_t) xdr_void, +}, diff --git a/examples/domain-events/events-c/event-test.c b/examples/domain-events/events-c/event-test.c index bb3c7bb..53a3195 100644 --- a/examples/domain-events/events-c/event-test.c +++ b/examples/domain-events/events-c/event-test.c @@ -217,6 +217,47 @@ static int myDomainEventIOErrorCallback(virConnectPtr conn ATTRIBUTE_UNUSED, return 0; } +static int myDomainEventGraphicsCallback(virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject, + void *opaque ATTRIBUTE_UNUSED) +{ + int i; + printf("%s EVENT: Domain %s(%d) graphics ", __func__, virDomainGetName(dom), + virDomainGetID(dom)); + + switch (phase) { + case VIR_DOMAIN_EVENT_GRAPHICS_CONNECT: + printf("connected "); + break; + case VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE: + printf("initialized "); + break; + case VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT: + printf("disconnected "); + break; + } + + printf("local: family=%d node=%s service=%s ", + local->family, local->node, local->service); + printf("remote: family=%d node=%s service=%s ", + remote->family, remote->node, remote->service); + + printf("auth: %s ", authScheme); + for (i = 0 ; i < subject->nidentity ; i++) { + printf(" identity: %s=%s", + subject->identities[i].type, + subject->identities[i].name); + } + printf("\n"); + + return 0; +} + static void myFreeFunc(void *opaque) { char *str = opaque; @@ -338,6 +379,7 @@ int main(int argc, char **argv) int callback4ret = -1; int callback5ret = -1; int callback6ret = -1; + int callback7ret = -1; struct sigaction action_stop = { .sa_handler = stop @@ -395,13 +437,19 @@ int main(int argc, char **argv) VIR_DOMAIN_EVENT_ID_IO_ERROR, VIR_DOMAIN_EVENT_CALLBACK(myDomainEventIOErrorCallback), strdup("callback io error"), myFreeFunc); + callback7ret = virConnectDomainEventRegisterAny(dconn, + NULL, + VIR_DOMAIN_EVENT_ID_GRAPHICS, + VIR_DOMAIN_EVENT_CALLBACK(myDomainEventGraphicsCallback), + strdup("callback graphics"), myFreeFunc); if ((callback1ret != -1) && (callback2ret != -1) && (callback3ret != -1) && (callback4ret != -1) && (callback5ret != -1) && - (callback6ret != -1)) { + (callback6ret != -1) && + (callback7ret != -1)) { while(run) { struct pollfd pfd = { .fd = h_fd, .events = h_event, @@ -443,6 +491,7 @@ int main(int argc, char **argv) virConnectDomainEventDeregisterAny(dconn, callback4ret); virConnectDomainEventDeregisterAny(dconn, callback5ret); virConnectDomainEventDeregisterAny(dconn, callback6ret); + virConnectDomainEventDeregisterAny(dconn, callback7ret); } DEBUG0("Closing connection"); diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index bffd9ed..4205b91 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1870,6 +1870,46 @@ typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, int action, void *opaque); +typedef enum { + VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, + VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, + VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, +} virDomainEventGraphicsPhase; + +typedef enum { + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, +} virDomainEventGraphicsAddressType; + +struct _virDomainEventGraphicsAddress { + int family; + const char *node; + const char *service; +}; +typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; +typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; + +struct _virDomainEventGraphicsSubject { + int nidentity; + struct { + const char *type; + const char *name; + } *identities; +}; +typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; +typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; + +typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, + virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject, + void *opaque); + + + /* Use this to cast the event specific callback into the generic one * for use for virDomainEventRegister */ #define VIR_DOMAIN_EVENT_CALLBACK(cb) ((virConnectDomainEventGenericCallback)(cb)) @@ -1881,6 +1921,7 @@ typedef enum { VIR_DOMAIN_EVENT_ID_RTC_CHANGE = 2, /* virConnectDomainEventRTCChangeCallback */ VIR_DOMAIN_EVENT_ID_WATCHDOG = 3, /* virConnectDomainEventWatchdogCallback */ VIR_DOMAIN_EVENT_ID_IO_ERROR = 4, /* virConnectDomainEventIOErrorCallback */ + VIR_DOMAIN_EVENT_ID_GRAPHICS = 5, /* virConnectDomainEventGraphicsCallback */ /* * NB: this enum value will increase over time as new events are diff --git a/src/conf/domain_event.c b/src/conf/domain_event.c index 77b52ef..df3cced 100644 --- a/src/conf/domain_event.c +++ b/src/conf/domain_event.c @@ -75,6 +75,13 @@ struct _virDomainEvent { char *devAlias; int action; } ioError; + struct { + int phase; + virDomainEventGraphicsAddressPtr local; + virDomainEventGraphicsAddressPtr remote; + char *authScheme; + virDomainEventGraphicsSubjectPtr subject; + } graphics; } data; }; @@ -463,9 +470,32 @@ void virDomainEventFree(virDomainEventPtr event) if (!event) return; - if (event->eventID == VIR_DOMAIN_EVENT_ID_IO_ERROR) { + switch (event->eventID) { + case VIR_DOMAIN_EVENT_ID_IO_ERROR: VIR_FREE(event->data.ioError.srcPath); VIR_FREE(event->data.ioError.devAlias); + break; + + case VIR_DOMAIN_EVENT_ID_GRAPHICS: + if (event->data.graphics.local) { + VIR_FREE(event->data.graphics.local->node); + VIR_FREE(event->data.graphics.local->service); + VIR_FREE(event->data.graphics.local); + } + if (event->data.graphics.remote) { + VIR_FREE(event->data.graphics.remote->node); + VIR_FREE(event->data.graphics.remote->service); + VIR_FREE(event->data.graphics.remote); + } + VIR_FREE(event->data.graphics.authScheme); + if (event->data.graphics.subject) { + int i; + for (i = 0 ; i < event->data.graphics.subject->nidentity ; i++) { + VIR_FREE(event->data.graphics.subject->identities[i].type); + VIR_FREE(event->data.graphics.subject->identities[i].name); + } + VIR_FREE(event->data.graphics.subject); + } } VIR_FREE(event->dom.name); @@ -641,6 +671,58 @@ virDomainEventPtr virDomainEventIOErrorNewFromObj(virDomainObjPtr obj, return ev; } + +virDomainEventPtr virDomainEventGraphicsNewFromDom(virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_GRAPHICS, + dom->id, dom->name, dom->uuid); + + if (ev) { + ev->data.graphics.phase = phase; + if (!(ev->data.graphics.authScheme = strdup(authScheme))) { + virDomainEventFree(ev); + ev = NULL; + } + ev->data.graphics.local = local; + ev->data.graphics.remote = remote; + ev->data.graphics.subject = subject; + } + + return ev; +} + +virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject) +{ + virDomainEventPtr ev = + virDomainEventNewInternal(VIR_DOMAIN_EVENT_ID_GRAPHICS, + obj->def->id, obj->def->name, obj->def->uuid); + + if (ev) { + ev->data.graphics.phase = phase; + if (!(ev->data.graphics.authScheme = strdup(authScheme))) { + virDomainEventFree(ev); + ev = NULL; + } + ev->data.graphics.local = local; + ev->data.graphics.remote = remote; + ev->data.graphics.subject = subject; + } + + return ev; +} + + /** * virDomainEventQueueFree: * @queue: pointer to the queue @@ -771,6 +853,16 @@ void virDomainEventDispatchDefaultFunc(virConnectPtr conn, cbopaque); break; + case VIR_DOMAIN_EVENT_ID_GRAPHICS: + ((virConnectDomainEventGraphicsCallback)cb)(conn, dom, + event->data.graphics.phase, + event->data.graphics.local, + event->data.graphics.remote, + event->data.graphics.authScheme, + event->data.graphics.subject, + cbopaque); + break; + default: VIR_WARN("Unexpected event ID %d", event->eventID); break; diff --git a/src/conf/domain_event.h b/src/conf/domain_event.h index dbf9288..16c7870 100644 --- a/src/conf/domain_event.h +++ b/src/conf/domain_event.h @@ -130,6 +130,21 @@ virDomainEventPtr virDomainEventIOErrorNewFromObj(virDomainObjPtr obj, const char *devAlias, int action); +virDomainEventPtr virDomainEventGraphicsNewFromDom(virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject); +virDomainEventPtr virDomainEventGraphicsNewFromObj(virDomainObjPtr obj, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject); + + + int virDomainEventQueuePush(virDomainEventQueuePtr evtQueue, virDomainEventPtr event); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 4578762..0829eea 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -222,6 +222,8 @@ virDomainEventWatchdogNewFromDom; virDomainEventWatchdogNewFromObj; virDomainEventIOErrorNewFromDom; virDomainEventIOErrorNewFromObj; +virDomainEventGraphicsNewFromDom; +virDomainEventGraphicsNewFromObj; virDomainEventFree; virDomainEventDispatchDefaultFunc; virDomainEventDispatch; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 71cca5c..5dece1b 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -984,6 +984,98 @@ qemuHandleDomainIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuHandleDomainGraphics(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + int phase, + int localFamily, + const char *localNode, + const char *localService, + int remoteFamily, + const char *remoteNode, + const char *remoteService, + const char *authScheme, + const char *x509dname, + const char *saslUsername) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event; + virDomainEventGraphicsAddressPtr localAddr = NULL; + virDomainEventGraphicsAddressPtr remoteAddr = NULL; + virDomainEventGraphicsSubjectPtr subject = NULL; + int i; + + virDomainObjLock(vm); + + if (VIR_ALLOC(localAddr) < 0) + goto no_memory; + localAddr->family = localFamily; + if (!(localAddr->service = strdup(localService)) || + !(localAddr->node = strdup(localNode))) + goto no_memory; + + if (VIR_ALLOC(remoteAddr) < 0) + goto no_memory; + remoteAddr->family = remoteFamily; + if (!(remoteAddr->service = strdup(remoteService)) || + !(remoteAddr->node = strdup(remoteNode))) + goto no_memory; + + if (VIR_ALLOC(subject) < 0) + goto no_memory; + if (x509dname) { + if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0) + goto no_memory; + if (!(subject->identities[subject->nidentity].type = strdup("x509dname")) || + !(subject->identities[subject->nidentity].name = strdup(x509dname))) + goto no_memory; + subject->nidentity++; + } + if (saslUsername) { + if (VIR_REALLOC_N(subject->identities, subject->nidentity+1) < 0) + goto no_memory; + if (!(subject->identities[subject->nidentity].type = strdup("saslUsername")) || + !(subject->identities[subject->nidentity].name = strdup(saslUsername))) + goto no_memory; + subject->nidentity++; + } + + event = virDomainEventGraphicsNewFromObj(vm, phase, localAddr, remoteAddr, authScheme, subject); + virDomainObjUnlock(vm); + + if (event) { + qemuDriverLock(driver); + qemuDomainEventQueue(driver, event); + qemuDriverUnlock(driver); + } + + return 0; + +no_memory: + virReportOOMError(); + if (localAddr) { + VIR_FREE(localAddr->service); + VIR_FREE(localAddr->node); + VIR_FREE(localAddr); + } + if (remoteAddr) { + VIR_FREE(remoteAddr->service); + VIR_FREE(remoteAddr->node); + VIR_FREE(remoteAddr); + } + if (subject) { + for (i = 0 ; i < subject->nidentity ; i++) { + VIR_FREE(subject->identities[i].type); + VIR_FREE(subject->identities[i].name); + } + VIR_FREE(subject->identities); + VIR_FREE(subject); + } + + return -1; +} + + static qemuMonitorCallbacks monitorCallbacks = { .eofNotify = qemuHandleMonitorEOF, .diskSecretLookup = findVolumeQcowPassphrase, @@ -991,6 +1083,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainRTCChange = qemuHandleDomainRTCChange, .domainWatchdog = qemuHandleDomainWatchdog, .domainIOError = qemuHandleDomainIOError, + .domainGraphics = qemuHandleDomainGraphics, }; static int diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index f320df9..28d652e 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -838,6 +838,36 @@ int qemuMonitorEmitIOError(qemuMonitorPtr mon, } +int qemuMonitorEmitGraphics(qemuMonitorPtr mon, + int phase, + int localFamily, + const char *localNode, + const char *localService, + int remoteFamily, + const char *remoteNode, + const char *remoteService, + const char *authScheme, + const char *x509dname, + const char *saslUsername) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->domainGraphics) + ret = mon->cb->domainGraphics(mon, mon->vm, + phase, + localFamily, localNode, localService, + remoteFamily, remoteNode, remoteService, + authScheme, x509dname, saslUsername); + qemuMonitorLock(mon); + qemuMonitorUnref(mon); + return ret; +} + + + int qemuMonitorSetCapabilities(qemuMonitorPtr mon) { int ret; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index 64ae153..5d62177 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -96,6 +96,18 @@ struct _qemuMonitorCallbacks { virDomainObjPtr vm, const char *diskAlias, int action); + int (*domainGraphics)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int phase, + int localFamily, + const char *localNode, + const char *localService, + int remoteFamily, + const char *remoteNode, + const char *remoteService, + const char *authScheme, + const char *x509dname, + const char *saslUsername); }; @@ -137,6 +149,18 @@ int qemuMonitorEmitWatchdog(qemuMonitorPtr mon, int action); int qemuMonitorEmitIOError(qemuMonitorPtr mon, const char *diskAlias, int action); +int qemuMonitorEmitGraphics(qemuMonitorPtr mon, + int phase, + int localFamily, + const char *localNode, + const char *localService, + int remoteFamily, + const char *remoteNode, + const char *remoteService, + const char *authScheme, + const char *x509dname, + const char *saslUsername); + int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e2d7744..d216239 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -52,6 +52,9 @@ static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleWatchdog(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data); struct { const char *type; @@ -64,6 +67,9 @@ struct { { "RTC_CHANGE", qemuMonitorJSONHandleRTCChange, }, { "WATCHDOG", qemuMonitorJSONHandleWatchdog, }, { "DISK_IO_ERROR", qemuMonitorJSONHandleIOError, }, + { "VNC_CONNECTED", qemuMonitorJSONHandleVNCConnect, }, + { "VNC_INITIALIZED", qemuMonitorJSONHandleVNCInitialize, }, + { "VNC_DISCONNECTED", qemuMonitorJSONHandleVNCDisconnect, }, }; @@ -566,6 +572,74 @@ static void qemuMonitorJSONHandleIOError(qemuMonitorPtr mon, virJSONValuePtr dat } +VIR_ENUM_DECL(qemuMonitorGraphicsAddressFamily) +VIR_ENUM_IMPL(qemuMonitorGraphicsAddressFamily, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6 + 1, + "ipv4", "ipv6"); + +static void qemuMonitorJSONHandleVNC(qemuMonitorPtr mon, virJSONValuePtr data, int phase) +{ + const char *localNode, *localService, *localFamily; + const char *remoteNode, *remoteService, *remoteFamily; + const char *authScheme, *saslUsername, *x509dname; + int localFamilyID, remoteFamilyID; + virJSONValuePtr client; + virJSONValuePtr server; + + if (!(client = virJSONValueObjectGet(data, "client"))) { + VIR_WARN0("missing client info in VNC event"); + return; + } + if (!(server = virJSONValueObjectGet(data, "server"))) { + VIR_WARN0("missing server info in VNC event"); + return; + } + + authScheme = virJSONValueObjectGetString(server, "auth"); + + localFamily = virJSONValueObjectGetString(server, "family"); + localNode = virJSONValueObjectGetString(server, "host"); + localService = virJSONValueObjectGetString(server, "service"); + + remoteFamily = virJSONValueObjectGetString(client, "family"); + remoteNode = virJSONValueObjectGetString(client, "host"); + remoteService = virJSONValueObjectGetString(client, "service"); + + saslUsername = virJSONValueObjectGetString(client, "sasl_username"); + x509dname = virJSONValueObjectGetString(client, "x509_dname"); + + if ((localFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(localFamily)) < 0) { + VIR_WARN("unknown address family '%s'", localFamily); + localFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; + } + if ((remoteFamilyID = qemuMonitorGraphicsAddressFamilyTypeFromString(remoteFamily)) < 0) { + VIR_WARN("unknown address family '%s'", remoteFamily); + remoteFamilyID = VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4; + } + + qemuMonitorEmitGraphics(mon, phase, + localFamilyID, localNode, localService, + remoteFamilyID, remoteNode, remoteService, + authScheme, x509dname, saslUsername); +} + +static void qemuMonitorJSONHandleVNCConnect(qemuMonitorPtr mon, virJSONValuePtr data) +{ + qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_CONNECT); +} + + +static void qemuMonitorJSONHandleVNCInitialize(qemuMonitorPtr mon, virJSONValuePtr data) +{ + qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE); +} + + +static void qemuMonitorJSONHandleVNCDisconnect(qemuMonitorPtr mon, virJSONValuePtr data) +{ + qemuMonitorJSONHandleVNC(mon, data, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT); +} + + int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) { diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index 068ad9b..426e620 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -6998,6 +6998,94 @@ remoteDomainReadEventIOError(virConnectPtr conn, XDR *xdr) } +static virDomainEventPtr +remoteDomainReadEventGraphics(virConnectPtr conn, XDR *xdr) +{ + remote_domain_event_graphics_msg msg; + virDomainPtr dom; + virDomainEventPtr event = NULL; + virDomainEventGraphicsAddressPtr localAddr = NULL; + virDomainEventGraphicsAddressPtr remoteAddr = NULL; + virDomainEventGraphicsSubjectPtr subject = NULL; + int i; + + memset (&msg, 0, sizeof msg); + + /* unmarshall parameters, and process it*/ + if (! xdr_remote_domain_event_graphics_msg(xdr, &msg) ) { + error (conn, VIR_ERR_RPC, + _("unable to demarshall reboot event")); + return NULL; + } + + dom = get_nonnull_domain(conn,msg.dom); + if (!dom) + return NULL; + + if (VIR_ALLOC(localAddr) < 0) + goto no_memory; + localAddr->family = msg.local.family; + if (!(localAddr->service = strdup(msg.local.service)) || + !(localAddr->node = strdup(msg.local.node))) + goto no_memory; + + if (VIR_ALLOC(remoteAddr) < 0) + goto no_memory; + remoteAddr->family = msg.remote.family; + if (!(remoteAddr->service = strdup(msg.remote.service)) || + !(remoteAddr->node = strdup(msg.remote.node))) + goto no_memory; + + fprintf(stderr, "Got %d\n", msg.subject.subject_len); + if (VIR_ALLOC(subject) < 0) + goto no_memory; + if (VIR_ALLOC_N(subject->identities, msg.subject.subject_len) < 0) + goto no_memory; + subject->nidentity = msg.subject.subject_len; + for (i = 0 ; i < subject->nidentity ; i++) { + fprintf(stderr, " %s=%s\n", msg.subject.subject_val[i].type, + msg.subject.subject_val[i].name); + if (!(subject->identities[i].type = strdup(msg.subject.subject_val[i].type)) || + !(subject->identities[i].name = strdup(msg.subject.subject_val[i].name))) + goto no_memory; + } + + event = virDomainEventGraphicsNewFromDom(dom, + msg.phase, + localAddr, + remoteAddr, + msg.authScheme, + subject); + xdr_free ((xdrproc_t) &xdr_remote_domain_event_graphics_msg, (char *) &msg); + + virDomainFree(dom); + return event; + +no_memory: + xdr_free ((xdrproc_t) &xdr_remote_domain_event_graphics_msg, (char *) &msg); + + if (localAddr) { + VIR_FREE(localAddr->service); + VIR_FREE(localAddr->node); + VIR_FREE(localAddr); + } + if (remoteAddr) { + VIR_FREE(remoteAddr->service); + VIR_FREE(remoteAddr->node); + VIR_FREE(remoteAddr); + } + if (subject) { + for (i = 0 ; i < subject->nidentity ; i++) { + VIR_FREE(subject->identities[i].type); + VIR_FREE(subject->identities[i].name); + } + VIR_FREE(subject->identities); + VIR_FREE(subject); + } + return NULL; +} + + static virDrvOpenStatus ATTRIBUTE_NONNULL (1) remoteSecretOpen (virConnectPtr conn, virConnectAuthPtr auth, @@ -8527,6 +8615,10 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv, event = remoteDomainReadEventIOError(conn, xdr); break; + case REMOTE_PROC_DOMAIN_EVENT_GRAPHICS: + event = remoteDomainReadEventGraphics(conn, xdr); + break; + default: DEBUG("Unexpected event proc %d", hdr->proc); break; diff --git a/src/remote/remote_protocol.c b/src/remote/remote_protocol.c index 79dfb98..6571d88 100644 --- a/src/remote/remote_protocol.c +++ b/src/remote/remote_protocol.c @@ -3073,6 +3073,51 @@ xdr_remote_domain_event_io_error_msg (XDR *xdrs, remote_domain_event_io_error_ms } bool_t +xdr_remote_domain_event_graphics_address (XDR *xdrs, remote_domain_event_graphics_address *objp) +{ + + if (!xdr_int (xdrs, &objp->family)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->node)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->service)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_event_graphics_identity (XDR *xdrs, remote_domain_event_graphics_identity *objp) +{ + + if (!xdr_remote_nonnull_string (xdrs, &objp->type)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->name)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_domain_event_graphics_msg (XDR *xdrs, remote_domain_event_graphics_msg *objp) +{ + char **objp_cpp0 = (char **) (void *) &objp->subject.subject_val; + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_int (xdrs, &objp->phase)) + return FALSE; + if (!xdr_remote_domain_event_graphics_address (xdrs, &objp->local)) + return FALSE; + if (!xdr_remote_domain_event_graphics_address (xdrs, &objp->remote)) + return FALSE; + if (!xdr_remote_nonnull_string (xdrs, &objp->authScheme)) + return FALSE; + if (!xdr_array (xdrs, objp_cpp0, (u_int *) &objp->subject.subject_len, REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX, + sizeof (remote_domain_event_graphics_identity), (xdrproc_t) xdr_remote_domain_event_graphics_identity)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/src/remote/remote_protocol.h b/src/remote/remote_protocol.h index cddd284..33c8674 100644 --- a/src/remote/remote_protocol.h +++ b/src/remote/remote_protocol.h @@ -1739,6 +1739,33 @@ struct remote_domain_event_io_error_msg { int action; }; typedef struct remote_domain_event_io_error_msg remote_domain_event_io_error_msg; + +struct remote_domain_event_graphics_address { + int family; + remote_nonnull_string node; + remote_nonnull_string service; +}; +typedef struct remote_domain_event_graphics_address remote_domain_event_graphics_address; +#define REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX 20 + +struct remote_domain_event_graphics_identity { + remote_nonnull_string type; + remote_nonnull_string name; +}; +typedef struct remote_domain_event_graphics_identity remote_domain_event_graphics_identity; + +struct remote_domain_event_graphics_msg { + remote_nonnull_domain dom; + int phase; + remote_domain_event_graphics_address local; + remote_domain_event_graphics_address remote; + remote_nonnull_string authScheme; + struct { + u_int subject_len; + remote_domain_event_graphics_identity *subject_val; + } subject; +}; +typedef struct remote_domain_event_graphics_msg remote_domain_event_graphics_msg; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1913,6 +1940,7 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169, REMOTE_PROC_DOMAIN_EVENT_IO_ERROR = 170, + REMOTE_PROC_DOMAIN_EVENT_GRAPHICS = 171, }; typedef enum remote_procedure remote_procedure; @@ -2228,6 +2256,9 @@ extern bool_t xdr_remote_domain_event_reboot_msg (XDR *, remote_domain_event_re extern bool_t xdr_remote_domain_event_rtc_change_msg (XDR *, remote_domain_event_rtc_change_msg*); extern bool_t xdr_remote_domain_event_watchdog_msg (XDR *, remote_domain_event_watchdog_msg*); extern bool_t xdr_remote_domain_event_io_error_msg (XDR *, remote_domain_event_io_error_msg*); +extern bool_t xdr_remote_domain_event_graphics_address (XDR *, remote_domain_event_graphics_address*); +extern bool_t xdr_remote_domain_event_graphics_identity (XDR *, remote_domain_event_graphics_identity*); +extern bool_t xdr_remote_domain_event_graphics_msg (XDR *, remote_domain_event_graphics_msg*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_type (XDR *, remote_message_type*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -2517,6 +2548,9 @@ extern bool_t xdr_remote_domain_event_reboot_msg (); extern bool_t xdr_remote_domain_event_rtc_change_msg (); extern bool_t xdr_remote_domain_event_watchdog_msg (); extern bool_t xdr_remote_domain_event_io_error_msg (); +extern bool_t xdr_remote_domain_event_graphics_address (); +extern bool_t xdr_remote_domain_event_graphics_identity (); +extern bool_t xdr_remote_domain_event_graphics_msg (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_type (); extern bool_t xdr_remote_message_status (); diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 3cdc99b..23ba619 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1546,6 +1546,28 @@ struct remote_domain_event_io_error_msg { int action; }; +struct remote_domain_event_graphics_address { + int family; + remote_nonnull_string node; + remote_nonnull_string service; +}; + +const REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX = 20; + +struct remote_domain_event_graphics_identity { + remote_nonnull_string type; + remote_nonnull_string name; +}; + +struct remote_domain_event_graphics_msg { + remote_nonnull_domain dom; + int phase; + remote_domain_event_graphics_address local; + remote_domain_event_graphics_address remote; + remote_nonnull_string authScheme; + remote_domain_event_graphics_identity subject<REMOTE_DOMAIN_EVENT_GRAPHICS_IDENTITY_MAX>; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1738,7 +1760,9 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_EVENT_REBOOT = 167, REMOTE_PROC_DOMAIN_EVENT_RTC_CHANGE = 168, REMOTE_PROC_DOMAIN_EVENT_WATCHDOG = 169, - REMOTE_PROC_DOMAIN_EVENT_IO_ERROR = 170 + REMOTE_PROC_DOMAIN_EVENT_IO_ERROR = 170, + + REMOTE_PROC_DOMAIN_EVENT_GRAPHICS = 171 /* * Notice how the entries are grouped in sets of 10 ? -- 1.6.6.1

On 03/19/2010 09:39 AM, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_GRAPHICS
The same event can be emitted in 3 scenarioes
s/scenarioes/scenarios/
typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase;
Connect/disconnect are triggered at socket accept/close. The initailize phase is immediately after the protocol
s/initailize/initialize/
setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop
This event comes with *alot* of potential information
s/alot/a lot/ -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Fri, Mar 19, 2010 at 03:39:01PM +0000, Daniel P. Berrange wrote:
This introduces a new event type
VIR_DOMAIN_EVENT_ID_GRAPHICS
The same event can be emitted in 3 scenarioes
typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, } virDomainEventGraphicsPhase;
Connect/disconnect are triggered at socket accept/close. The initailize phase is immediately after the protocol setup and authentication has completed. ie when the client is authorized and about to start interacting with the graphical desktop
This event comes with *alot* of potential information
- IP address, port & address family of client - IP address, port & address family of server - Authentication scheme (arbitrary string) - Authenticated subject identity. A subject may have multiple identities with some authentication schemes. For example, vencrypt+sasl results in a x509dname and saslUsername identities.
This results in a very complicated callback :-(
typedef enum { VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, } virDomainEventGraphicsAddressType;
struct _virDomainEventGraphicsAddress { int family;
I assume it holds the IPV4/IPV6 enum, could that be indicated
const char *node;
that's a string address ?
const char *service; }; typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr;
struct _virDomainEventGraphicsSubject { int nidentity; struct { const char *type; const char *name; } *identities; }; typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr;
typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, virDomainPtr dom, int phase, virDomainEventGraphicsAddressPtr local, virDomainEventGraphicsAddressPtr remote, const char *authScheme, virDomainEventGraphicsSubjectPtr subject, void *opaque);
yeah that's very complex, I think all fields should be described and probably one or two example given, in the header [...]
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index bffd9ed..4205b91 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -1870,6 +1870,46 @@ typedef void (*virConnectDomainEventIOErrorCallback)(virConnectPtr conn, int action, void *opaque);
+typedef enum { + VIR_DOMAIN_EVENT_GRAPHICS_CONNECT = 0, + VIR_DOMAIN_EVENT_GRAPHICS_INITIALIZE, + VIR_DOMAIN_EVENT_GRAPHICS_DISCONNECT, +} virDomainEventGraphicsPhase; + +typedef enum { + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV4, + VIR_DOMAIN_EVENT_GRAPHICS_ADDRESS_IPV6, +} virDomainEventGraphicsAddressType; + +struct _virDomainEventGraphicsAddress { + int family; + const char *node; + const char *service; +}; +typedef struct _virDomainEventGraphicsAddress virDomainEventGraphicsAddress; +typedef virDomainEventGraphicsAddress *virDomainEventGraphicsAddressPtr; + +struct _virDomainEventGraphicsSubject { + int nidentity; + struct { + const char *type; + const char *name; + } *identities; +}; +typedef struct _virDomainEventGraphicsSubject virDomainEventGraphicsSubject; +typedef virDomainEventGraphicsSubject *virDomainEventGraphicsSubjectPtr; + +typedef void (*virConnectDomainEventGraphicsCallback)(virConnectPtr conn, + virDomainPtr dom, + int phase, + virDomainEventGraphicsAddressPtr local, + virDomainEventGraphicsAddressPtr remote, + const char *authScheme, + virDomainEventGraphicsSubjectPtr subject, + void *opaque); + +
Yeah I really think we need far more descriptions here, just guessing based on the structure or the fields names won't be sufficient to guess how to actually use this. [...]
+int qemuMonitorEmitGraphics(qemuMonitorPtr mon, + int phase, + int localFamily, + const char *localNode, + const char *localService, + int remoteFamily, + const char *remoteNode, + const char *remoteService, + const char *authScheme, + const char *x509dname, + const char *saslUsername)
maybe in the internal API we should detect the auth type before getting there and not enumerate with one arg per auth type. But it's minor at this point since that doesn't touch any API (even remote) ACK, Daniel -- Daniel Veillard | libxml Gnome XML XSLT toolkit http://xmlsoft.org/ daniel@veillard.com | Rpmfind RPM search engine http://rpmfind.net/ http://veillard.com/ | virtualization library http://libvirt.org/
participants (3)
-
Daniel P. Berrange
-
Daniel Veillard
-
Eric Blake