This patch is somewhat large, and touches many files. However - please remember to ignore generated files Signed-off-by: Ben Guthro <bguthro@xxxxxxxxxxxxxxx> include/libvirt/libvirt.h | 44 ++++++ include/libvirt/libvirt.h.in | 44 ++++++ python/generator.py | 3 qemud/qemud.c | 2 qemud/qemud.h | 7 + qemud/remote.c | 145 +++++++++++++++++++++ qemud/remote_dispatch_localvars.h | 3 qemud/remote_dispatch_proc_switch.h | 15 ++ qemud/remote_dispatch_prototypes.h | 2 qemud/remote_protocol.c | 31 ++++ qemud/remote_protocol.h | 25 +++ qemud/remote_protocol.x | 20 ++- src/driver.h | 21 +++ src/event.c | 24 ++- src/event.h | 23 +-- src/internal.h | 27 ++++ src/libvirt.c | 239 +++++++++++++++++++++++++++++++++--- src/libvirt_sym.version | 4 src/lxc_driver.c | 3 src/openvz_driver.c | 3 src/qemu_conf.h | 1 src/qemu_driver.c | 61 ++++++++- src/remote_internal.c | 178 +++++++++++++++++++++++++- src/test.c | 3 24 files changed, 878 insertions(+), 50 deletions(-)
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h index d519452..9b6e1da 100644 --- a/include/libvirt/libvirt.h +++ b/include/libvirt/libvirt.h @@ -987,6 +987,50 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +/* + * Domain Event Notification + */ + +typedef enum { + VIR_DOMAIN_EVENT_ADDED, + VIR_DOMAIN_EVENT_REMOVED, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_SAVED, + VIR_DOMAIN_EVENT_RESTORED, +} virDomainEventType; + +typedef int (*virConnectDomainEventCallback)(virConnectPtr conn, + virDomainPtr dom, + int event, + void *opaque); + +int virConnectDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback cb, + void *opaque); + +int virConnectDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback cb); + +/** + * virEventHandleCallback: callback for receiving file handle events + * + * @fd: file handle on which the event occurred + * @events: bitset of events from POLLnnn constants + * @opaque: user data registered with handle + */ +typedef void (*virEventHandleCallback)(int fd, int events, void *opaque); + +typedef int (*virEventAddHandleFunc)(int, int, virEventHandleCallback, void *); +typedef void (*virEventUpdateHandleFunc)(int, int); +typedef int (*virEventRemoveHandleFunc)(int); + +void virEventRegisterHandleImpl(virEventAddHandleFunc addHandle, + virEventUpdateHandleFunc updateHandle, + virEventRemoveHandleFunc removeHandle); + #ifdef __cplusplus } #endif diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 24b5680..71f4797 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -987,6 +987,50 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +/* + * Domain Event Notification + */ + +typedef enum { + VIR_DOMAIN_EVENT_ADDED, + VIR_DOMAIN_EVENT_REMOVED, + VIR_DOMAIN_EVENT_STARTED, + VIR_DOMAIN_EVENT_SUSPENDED, + VIR_DOMAIN_EVENT_RESUMED, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_SAVED, + VIR_DOMAIN_EVENT_RESTORED, +} virDomainEventType; + +typedef int (*virConnectDomainEventCallback)(virConnectPtr conn, + virDomainPtr dom, + int event, + void *opaque); + +int virConnectDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback cb, + void *opaque); + +int virConnectDomainEventDeregister(virConnectPtr conn, + virConnectDomainEventCallback cb); + +/** + * virEventHandleCallback: callback for receiving file handle events + * + * @fd: file handle on which the event occurred + * @events: bitset of events from POLLnnn constants + * @opaque: user data registered with handle + */ +typedef void (*virEventHandleCallback)(int fd, int events, void *opaque); + +typedef int (*virEventAddHandleFunc)(int, int, virEventHandleCallback, void *); +typedef void (*virEventUpdateHandleFunc)(int, int); +typedef int (*virEventRemoveHandleFunc)(int); + +void virEventRegisterHandleImpl(virEventAddHandleFunc addHandle, + virEventUpdateHandleFunc updateHandle, + virEventRemoveHandleFunc removeHandle); + #ifdef __cplusplus } #endif diff --git a/python/generator.py b/python/generator.py index c706b19..6816cc3 100755 --- a/python/generator.py +++ b/python/generator.py @@ -332,6 +332,9 @@ skip_function = ( 'virCopyLastError', # Python API is called virGetLastError instead 'virConnectOpenAuth', # Python C code is manually written 'virDefaultErrorFunc', # Python virErrorFuncHandler impl calls this from C + 'virConnectDomainEventRegister', # TODO: generate python bindings for these below XXX + 'virConnectDomainEventDeregister', + 'virEventRegisterHandleImpl', ) diff --git a/qemud/qemud.c b/qemud/qemud.c index 9da27d2..2d7e959 100644 --- a/qemud/qemud.c +++ b/qemud/qemud.c @@ -1503,7 +1503,7 @@ static int qemudClientWrite(struct qemud_server *server, } -static void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { +void qemudDispatchClientWrite(struct qemud_server *server, struct qemud_client *client) { switch (client->mode) { case QEMUD_MODE_TX_PACKET: { if (qemudClientWrite(server, client) < 0) diff --git a/qemud/qemud.h b/qemud/qemud.h index 91cb939..63784cd 100644 --- a/qemud/qemud.h +++ b/qemud/qemud.h @@ -132,6 +132,10 @@ struct qemud_client { */ virConnectPtr conn; + /* This client supports events, and has registered for at least + one event type. This is a bitmask of requested event types */ + int events_registered; + struct qemud_client *next; }; @@ -179,6 +183,9 @@ void qemudLog(int priority, const char *fmt, ...) void remoteDispatchClientRequest (struct qemud_server *server, struct qemud_client *client); +void qemudDispatchClientWrite(struct qemud_server *server, + struct qemud_client *client); + #if HAVE_POLKIT int qemudGetSocketIdentity(int fd, uid_t *uid, pid_t *pid); #endif diff --git a/qemud/remote.c b/qemud/remote.c index 8acd95d..55ccc2c 100644 --- a/qemud/remote.c +++ b/qemud/remote.c @@ -75,6 +75,18 @@ typedef int (*dispatch_fn) (struct qemud_server *server, char *args, char *ret); +/* Prototypes */ +static void +remoteDispatchDomainEventSend (struct qemud_client *client, + virDomainPtr dom, + virDomainEventType event); + +static int +remoteRelayDomainEvent (virConnectPtr conn, + virDomainPtr dom, + int event, + void *opaque); + /* This function gets called from qemud when it detects an incoming * remote protocol message. At this point, client->buffer contains * the full call message (including length word which we skip). @@ -405,12 +417,33 @@ remoteDispatchError (struct qemud_client *client, remoteDispatchSendError (client, req, VIR_ERR_RPC, msg); } +static int remoteRelayDomainEvent (virConnectPtr conn ATTRIBUTE_UNUSED, + virDomainPtr dom, + int event, + void *opaque) +{ + struct qemud_server *server = opaque; + REMOTE_DEBUG("Relaying domain event %d", event); + + struct qemud_client *c = server->clients; + while(c) { + if ( c->conn == conn && + (c->events_registered & virDomainEvent) ) { + remoteDispatchDomainEventSend (c, dom, event); + qemudDispatchClientWrite(server,c); + } else { + REMOTE_DEBUG("Event class %d not registered for client", virDomainEvent); + } + c = c->next; + } + return 0; +} /*----- Functions. -----*/ static int -remoteDispatchOpen (struct qemud_server *server ATTRIBUTE_UNUSED, +remoteDispatchOpen (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, struct remote_open_args *args, void *ret ATTRIBUTE_UNUSED) { @@ -436,6 +469,11 @@ remoteDispatchOpen (struct qemud_server *server ATTRIBUTE_UNUSED, ? virConnectOpenReadOnly (name) : virConnectOpen (name); + /* Register event delivery callback */ + if(client->conn) { + REMOTE_DEBUG("%s","Registering to relay remote events"); + virConnectDomainEventRegister(client->conn, remoteRelayDomainEvent, server); + } return client->conn ? 0 : -1; } @@ -3620,6 +3658,111 @@ remoteDispatchStorageVolLookupByPath (struct qemud_server *server ATTRIBUTE_UNUS } +/************************** + * Async Events + **************************/ +static int remoteDispatchEventsDomainEvent (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client ATTRIBUTE_UNUSED, + remote_message_header *req ATTRIBUTE_UNUSED, + void *args ATTRIBUTE_UNUSED, + remote_events_domain_event_ret *ret ATTRIBUTE_UNUSED) +{ + /* This call gets dispatched from a client call. + * This does not make sense, as this should not be intiated + * from the client side in generated code. + */ + return -1; +} + +/*************************** + * Enabe / disable event classes + ***************************/ +static int remoteDispatchEventsEnable (struct qemud_server *server ATTRIBUTE_UNUSED, + struct qemud_client *client, + remote_message_header *req ATTRIBUTE_UNUSED, + remote_events_enable_args *args, + remote_events_enable_ret *ret) +{ + CHECK_CONN(client); + if(args->enable) { + client->events_registered |= args->event_class; + } else { + client->events_registered &= ~args->event_class; + } + ret->success = 1; + return 0; +} + +static void +remoteDispatchDomainEventSend (struct qemud_client *client, + virDomainPtr dom, + virDomainEventType event) +{ + remote_message_header rep; + XDR xdr; + int len; + remote_events_domain_event_ret data; + + if(!client) { + remoteDispatchError (client, NULL, "%s", _("Invalid Client")); + return; + } + + /* Future versions of the protocol may use different vers or prog. Try + * our hardest to send back a message that such clients could see. + */ + rep.prog = REMOTE_PROGRAM; + rep.vers = REMOTE_PROTOCOL_VERSION; + rep.proc = REMOTE_PROC_EVENTS_DOMAIN_EVENT; + rep.direction = REMOTE_MESSAGE; + rep.serial = 1; + rep.status = REMOTE_OK; + + /* Serialise the return header and event. */ + xdrmem_create (&xdr, client->buffer, sizeof client->buffer, XDR_ENCODE); + + len = 0; /* We'll come back and write this later. */ + if (!xdr_int (&xdr, &len)) { + remoteDispatchError (client, NULL, "%s", _("xdr_int failed (1)")); + xdr_destroy (&xdr); + return; + } + + if (!xdr_remote_message_header (&xdr, &rep)) { + xdr_destroy (&xdr); + return; + } + + /* build return data */ + make_nonnull_domain (&data.dom, dom); + data.event = (int) event; + + if (!xdr_remote_events_domain_event_ret(&xdr, &data)) { + remoteDispatchError (client, NULL, "%s", _("serialise return struct")); + xdr_destroy (&xdr); + return; + } + + len = xdr_getpos (&xdr); + if (xdr_setpos (&xdr, 0) == 0) { + remoteDispatchError (client, NULL, "%s", _("xdr_setpos failed")); + xdr_destroy (&xdr); + return; + } + + if (!xdr_int (&xdr, &len)) { + remoteDispatchError (client, NULL, "%s", _("xdr_int failed (2)")); + xdr_destroy (&xdr); + return; + } + + xdr_destroy (&xdr); + + /* Send it. */ + client->mode = QEMUD_MODE_TX_PACKET; + client->bufferLength = len; + client->bufferOffset = 0; +} /*----- Helpers. -----*/ /* get_nonnull_domain and get_nonnull_network turn an on-wire diff --git a/qemud/remote_dispatch_localvars.h b/qemud/remote_dispatch_localvars.h index 18d71e9..bb28109 100644 --- a/qemud/remote_dispatch_localvars.h +++ b/qemud/remote_dispatch_localvars.h @@ -96,6 +96,8 @@ remote_network_lookup_by_name_ret lv_remote_network_lookup_by_name_ret; remote_domain_memory_peek_args lv_remote_domain_memory_peek_args; remote_domain_memory_peek_ret lv_remote_domain_memory_peek_ret; remote_num_of_defined_domains_ret lv_remote_num_of_defined_domains_ret; +remote_events_enable_args lv_remote_events_enable_args; +remote_events_enable_ret lv_remote_events_enable_ret; remote_domain_block_stats_args lv_remote_domain_block_stats_args; remote_domain_block_stats_ret lv_remote_domain_block_stats_ret; remote_domain_detach_device_args lv_remote_domain_detach_device_args; @@ -112,6 +114,7 @@ remote_get_version_ret lv_remote_get_version_ret; remote_domain_suspend_args lv_remote_domain_suspend_args; remote_storage_pool_lookup_by_name_args lv_remote_storage_pool_lookup_by_name_args; remote_storage_pool_lookup_by_name_ret lv_remote_storage_pool_lookup_by_name_ret; +remote_events_domain_event_ret lv_remote_events_domain_event_ret; remote_network_set_autostart_args lv_remote_network_set_autostart_args; remote_network_get_autostart_args lv_remote_network_get_autostart_args; remote_network_get_autostart_ret lv_remote_network_get_autostart_ret; diff --git a/qemud/remote_dispatch_proc_switch.h b/qemud/remote_dispatch_proc_switch.h index 767b142..eeeb580 100644 --- a/qemud/remote_dispatch_proc_switch.h +++ b/qemud/remote_dispatch_proc_switch.h @@ -335,6 +335,21 @@ case REMOTE_PROC_DOMAIN_UNDEFINE: args = (char *) &lv_remote_domain_undefine_args; memset (&lv_remote_domain_undefine_args, 0, sizeof lv_remote_domain_undefine_args); break; +case REMOTE_PROC_EVENTS_DOMAIN_EVENT: + fn = (dispatch_fn) remoteDispatchEventsDomainEvent; + ret_filter = (xdrproc_t) xdr_remote_events_domain_event_ret; + ret = (char *) &lv_remote_events_domain_event_ret; + memset (&lv_remote_events_domain_event_ret, 0, sizeof lv_remote_events_domain_event_ret); + break; +case REMOTE_PROC_EVENTS_ENABLE: + fn = (dispatch_fn) remoteDispatchEventsEnable; + args_filter = (xdrproc_t) xdr_remote_events_enable_args; + args = (char *) &lv_remote_events_enable_args; + memset (&lv_remote_events_enable_args, 0, sizeof lv_remote_events_enable_args); + ret_filter = (xdrproc_t) xdr_remote_events_enable_ret; + ret = (char *) &lv_remote_events_enable_ret; + memset (&lv_remote_events_enable_ret, 0, sizeof lv_remote_events_enable_ret); + break; case REMOTE_PROC_FIND_STORAGE_POOL_SOURCES: fn = (dispatch_fn) remoteDispatchFindStoragePoolSources; args_filter = (xdrproc_t) xdr_remote_find_storage_pool_sources_args; diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h index 950ad05..ce345e9 100644 --- a/qemud/remote_dispatch_prototypes.h +++ b/qemud/remote_dispatch_prototypes.h @@ -47,6 +47,8 @@ static int remoteDispatchDomainSetVcpus (struct qemud_server *server, struct qem static int remoteDispatchDomainShutdown (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_shutdown_args *args, void *ret); static int remoteDispatchDomainSuspend (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_suspend_args *args, void *ret); static int remoteDispatchDomainUndefine (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_domain_undefine_args *args, void *ret); +static int remoteDispatchEventsDomainEvent (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_events_domain_event_ret *ret); +static int remoteDispatchEventsEnable (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_events_enable_args *args, remote_events_enable_ret *ret); static int remoteDispatchFindStoragePoolSources (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, remote_find_storage_pool_sources_args *args, remote_find_storage_pool_sources_ret *ret); static int remoteDispatchGetCapabilities (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_capabilities_ret *ret); static int remoteDispatchGetHostname (struct qemud_server *server, struct qemud_client *client, remote_message_header *req, void *args, remote_get_hostname_ret *ret); diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c index be1d6d8..b876127 100644 --- a/qemud/remote_protocol.c +++ b/qemud/remote_protocol.c @@ -1943,6 +1943,37 @@ xdr_remote_storage_vol_get_path_ret (XDR *xdrs, remote_storage_vol_get_path_ret } bool_t +xdr_remote_events_enable_args (XDR *xdrs, remote_events_enable_args *objp) +{ + + if (!xdr_int (xdrs, &objp->event_class)) + return FALSE; + if (!xdr_int (xdrs, &objp->enable)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_events_enable_ret (XDR *xdrs, remote_events_enable_ret *objp) +{ + + if (!xdr_int (xdrs, &objp->success)) + return FALSE; + return TRUE; +} + +bool_t +xdr_remote_events_domain_event_ret (XDR *xdrs, remote_events_domain_event_ret *objp) +{ + + if (!xdr_remote_nonnull_domain (xdrs, &objp->dom)) + return FALSE; + if (!xdr_int (xdrs, &objp->event)) + return FALSE; + return TRUE; +} + +bool_t xdr_remote_procedure (XDR *xdrs, remote_procedure *objp) { diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h index bcaa219..f0b511f 100644 --- a/qemud/remote_protocol.h +++ b/qemud/remote_protocol.h @@ -1081,6 +1081,23 @@ struct remote_storage_vol_get_path_ret { remote_nonnull_string name; }; typedef struct remote_storage_vol_get_path_ret remote_storage_vol_get_path_ret; + +struct remote_events_enable_args { + int event_class; + int enable; +}; +typedef struct remote_events_enable_args remote_events_enable_args; + +struct remote_events_enable_ret { + int success; +}; +typedef struct remote_events_enable_ret remote_events_enable_ret; + +struct remote_events_domain_event_ret { + remote_nonnull_domain dom; + int event; +}; +typedef struct remote_events_domain_event_ret remote_events_domain_event_ret; #define REMOTE_PROGRAM 0x20008086 #define REMOTE_PROTOCOL_VERSION 1 @@ -1189,6 +1206,8 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_FREE_MEMORY = 102, REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103, REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104, + REMOTE_PROC_EVENTS_ENABLE = 105, + REMOTE_PROC_EVENTS_DOMAIN_EVENT = 106, }; typedef enum remote_procedure remote_procedure; @@ -1394,6 +1413,9 @@ extern bool_t xdr_remote_storage_vol_get_info_args (XDR *, remote_storage_vol_g extern bool_t xdr_remote_storage_vol_get_info_ret (XDR *, remote_storage_vol_get_info_ret*); extern bool_t xdr_remote_storage_vol_get_path_args (XDR *, remote_storage_vol_get_path_args*); extern bool_t xdr_remote_storage_vol_get_path_ret (XDR *, remote_storage_vol_get_path_ret*); +extern bool_t xdr_remote_events_enable_args (XDR *, remote_events_enable_args*); +extern bool_t xdr_remote_events_enable_ret (XDR *, remote_events_enable_ret*); +extern bool_t xdr_remote_events_domain_event_ret (XDR *, remote_events_domain_event_ret*); extern bool_t xdr_remote_procedure (XDR *, remote_procedure*); extern bool_t xdr_remote_message_direction (XDR *, remote_message_direction*); extern bool_t xdr_remote_message_status (XDR *, remote_message_status*); @@ -1575,6 +1597,9 @@ extern bool_t xdr_remote_storage_vol_get_info_args (); extern bool_t xdr_remote_storage_vol_get_info_ret (); extern bool_t xdr_remote_storage_vol_get_path_args (); extern bool_t xdr_remote_storage_vol_get_path_ret (); +extern bool_t xdr_remote_events_enable_args (); +extern bool_t xdr_remote_events_enable_ret (); +extern bool_t xdr_remote_events_domain_event_ret (); extern bool_t xdr_remote_procedure (); extern bool_t xdr_remote_message_direction (); extern bool_t xdr_remote_message_status (); diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x index f848ae5..09acd80 100644 --- a/qemud/remote_protocol.x +++ b/qemud/remote_protocol.x @@ -965,6 +965,21 @@ struct remote_storage_vol_get_path_ret { remote_nonnull_string name; }; +/* Events */ +struct remote_events_enable_args { + int event_class; + int enable; +}; + +struct remote_events_enable_ret { + int success; +}; + +struct remote_events_domain_event_ret { + remote_nonnull_domain dom; + int event; +}; + /*----- Protocol. -----*/ /* Define the program number, protocol version and procedure numbers here. */ @@ -1086,7 +1101,10 @@ enum remote_procedure { REMOTE_PROC_NODE_GET_FREE_MEMORY = 102, REMOTE_PROC_DOMAIN_BLOCK_PEEK = 103, - REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104 + REMOTE_PROC_DOMAIN_MEMORY_PEEK = 104, + + REMOTE_PROC_EVENTS_ENABLE = 105, + REMOTE_PROC_EVENTS_DOMAIN_EVENT = 106 }; /* Custom RPC structure. */ diff --git a/src/driver.h b/src/driver.h index 655cd05..005fe03 100644 --- a/src/driver.h +++ b/src/driver.h @@ -40,6 +40,13 @@ typedef enum { VIR_DRV_OPEN_ERROR = -2, } virDrvOpenStatus; + +/* Event Classes. (bitmasked value) */ +typedef enum { + virDomainEvent = 1, + virNodeEvent = 2, /* NYI */ +} virEventClass; + /* Feature detection. This is a libvirt-private interface for determining * what features are supported by the driver. * @@ -280,6 +287,15 @@ typedef unsigned long long (*virDrvNodeGetFreeMemory) (virConnectPtr conn); +typedef int + (*virDrvEventsEnableEventClass) + (virConnectPtr conn, + virEventClass event_class, + int enable); + +typedef int + (*virDrvDomainEventEmitted) + (virDomainEventType evt); /** * _virDriver: * @@ -296,6 +312,8 @@ struct _virDriver { int no; /* the number virDrvNo */ const char * name; /* the name of the driver */ unsigned long ver; /* the version of the backend */ + virConnectPtr conns; /* the list of active connections */ + virDrvProbe probe; virDrvOpen open; virDrvClose close; @@ -352,6 +370,8 @@ struct _virDriver { virDrvDomainMemoryPeek domainMemoryPeek; virDrvNodeGetCellsFreeMemory nodeGetCellsFreeMemory; virDrvNodeGetFreeMemory getFreeMemory; + virDrvEventsEnableEventClass enableEventClass; + virDrvDomainEventEmitted domainEventEmitted; }; typedef int @@ -396,7 +416,6 @@ typedef int (*virDrvNetworkSetAutostart) (virNetworkPtr network, int autostart); - typedef struct _virNetworkDriver virNetworkDriver; typedef virNetworkDriver *virNetworkDriverPtr; diff --git a/src/event.c b/src/event.c index 49a9e61..9a39ab7 100644 --- a/src/event.c +++ b/src/event.c @@ -70,16 +70,28 @@ int virEventRemoveTimeout(int timer) { return removeTimeoutImpl(timer); } -void __virEventRegisterImpl(virEventAddHandleFunc addHandle, - virEventUpdateHandleFunc updateHandle, - virEventRemoveHandleFunc removeHandle, - virEventAddTimeoutFunc addTimeout, - virEventUpdateTimeoutFunc updateTimeout, - virEventRemoveTimeoutFunc removeTimeout) { +void virEventRegisterHandleImpl(virEventAddHandleFunc addHandle, + virEventUpdateHandleFunc updateHandle, + virEventRemoveHandleFunc removeHandle) { addHandleImpl = addHandle; updateHandleImpl = updateHandle; removeHandleImpl = removeHandle; +} + +void __virEventRegisterTimeoutImpl(virEventAddTimeoutFunc addTimeout, + virEventUpdateTimeoutFunc updateTimeout, + virEventRemoveTimeoutFunc removeTimeout) { addTimeoutImpl = addTimeout; updateTimeoutImpl = updateTimeout; removeTimeoutImpl = removeTimeout; } + +void __virEventRegisterImpl(virEventAddHandleFunc addHandle, + virEventUpdateHandleFunc updateHandle, + virEventRemoveHandleFunc removeHandle, + virEventAddTimeoutFunc addTimeout, + virEventUpdateTimeoutFunc updateTimeout, + virEventRemoveTimeoutFunc removeTimeout) { + virEventRegisterHandleImpl(addHandle, updateHandle, removeHandle); + virEventRegisterTimeoutImpl(addTimeout, updateTimeout, removeTimeout); +} diff --git a/src/event.h b/src/event.h index 758573c..b3a3849 100644 --- a/src/event.h +++ b/src/event.h @@ -23,17 +23,7 @@ #ifndef __VIR_EVENT_H__ #define __VIR_EVENT_H__ - - -/** - * virEventHandleCallback: callback for receiving file handle events - * - * @fd: file handle on which the event occurred - * @events: bitset of events from POLLnnn constants - * @opaque: user data registered with handle - */ -typedef void (*virEventHandleCallback)(int fd, int events, void *opaque); - +#include "internal.h" /** * virEventAddHandle: register a callback for monitoring file handle events * @@ -110,14 +100,14 @@ void virEventUpdateTimeout(int timer, int frequency); */ int virEventRemoveTimeout(int timer); -typedef int (*virEventAddHandleFunc)(int, int, virEventHandleCallback, void *); -typedef void (*virEventUpdateHandleFunc)(int, int); -typedef int (*virEventRemoveHandleFunc)(int); - typedef int (*virEventAddTimeoutFunc)(int, virEventTimeoutCallback, void *); typedef void (*virEventUpdateTimeoutFunc)(int, int); typedef int (*virEventRemoveTimeoutFunc)(int); +void __virEventRegisterTimeoutImpl(virEventAddTimeoutFunc addTimeout, + virEventUpdateTimeoutFunc updateTimeout, + virEventRemoveTimeoutFunc removeTimeout); + void __virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventUpdateHandleFunc updateHandle, virEventRemoveHandleFunc removeHandle, @@ -125,6 +115,7 @@ void __virEventRegisterImpl(virEventAddHandleFunc addHandle, virEventUpdateTimeoutFunc updateTimeout, virEventRemoveTimeoutFunc removeTimeout); -#define virEventRegisterImpl(ah,rh,at,rt) __virEventRegisterImpl(ah,rh,at,rt) +#define virEventRegisterTimeoutImpl(at,ut,rt) __virEventRegisterTimeoutImpl((at),(ut),(rt)) +#define virEventRegisterImpl(ah,uh,rh,at,ut,rt) __virEventRegisterImpl((ah),(uh),(rh),(at),(ut),(rt)) #endif /* __VIR_EVENT_H__ */ diff --git a/src/internal.h b/src/internal.h index a3d48fa..67a3e5b 100644 --- a/src/internal.h +++ b/src/internal.h @@ -191,6 +191,18 @@ extern int debugFlag; #define VIR_IS_STORAGE_VOL(obj) ((obj) && (obj)->magic==VIR_STORAGE_VOL_MAGIC) #define VIR_IS_CONNECTED_STORAGE_VOL(obj) (VIR_IS_STORAGE_VOL(obj) && VIR_IS_CONNECT((obj)->conn)) +/** + * Domain Event Callbacks + */ +struct _virDomainEventCallbackList { + virConnectPtr conn; + virConnectDomainEventCallback cb; + void *opaque; + struct _virDomainEventCallbackList *next; +}; + +typedef struct _virDomainEventCallbackList virDomainEventCallbackList; + /* * arbitrary limitations */ @@ -237,6 +249,12 @@ struct _virConnect { virHashTablePtr storagePools;/* hash table for known storage pools */ virHashTablePtr storageVols;/* hash table for known storage vols */ int refs; /* reference count */ + + /* Domain Callbacks */ + virDomainEventCallbackList *domainEventCallbacks; + + /* link to next conn of this driver type */ + struct _virConnect *next; }; /** @@ -377,4 +395,13 @@ char *virStringListJoin(const virStringList *list, const char *pre, const char *post, const char *sep); void virStringListFree(virStringList *list); +/* + * Domain Event Notification + */ + +void virDomainEventCallbackListFree(virDomainEventCallbackList *head); + +void virDispatchDomainEvent(virDomainPtr dom, int event); +void virBroadcastDomainEvent(virDomainPtr dom, int event); + #endif /* __VIR_INTERNAL_H__ */ diff --git a/src/libvirt.c b/src/libvirt.c index e06e9f3..9472646 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -785,6 +785,8 @@ do_open (const char *name, if (res == VIR_DRV_OPEN_ERROR) goto failed; else if (res == VIR_DRV_OPEN_SUCCESS) { ret->driver = virDriverTab[i]; + ret->next = ret->driver->conns; + ret->driver->conns = ret; break; } } @@ -957,6 +959,19 @@ virConnectClose(virConnectPtr conn) conn->networkDriver->close (conn); if (conn->storageDriver) conn->storageDriver->close (conn); + if (conn->domainEventCallbacks) + virDomainEventCallbackListFree(conn->domainEventCallbacks); + + /* Remove the connection from the list of active connections */ + virConnectPtr *last = &conn->driver->conns; + while(*last) { + virConnectPtr p = *last; + if( p == conn ) { + *last = p->next; + break; + } + last = &p->next; + } conn->driver->close (conn); if (virUnrefConnect(conn) < 0) @@ -1427,6 +1442,7 @@ virDomainLookupByName(virConnectPtr conn, const char *name) int virDomainDestroy(virDomainPtr domain) { + int ret; virConnectPtr conn; DEBUG("domain=%p", domain); @@ -1442,8 +1458,14 @@ virDomainDestroy(virDomainPtr domain) return (-1); } - if (conn->driver->domainDestroy) - return conn->driver->domainDestroy (domain); + if (conn->driver->domainDestroy) { + ret = conn->driver->domainDestroy (domain); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_STOPPED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_STOPPED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -1487,6 +1509,7 @@ virDomainFree(virDomainPtr domain) int virDomainSuspend(virDomainPtr domain) { + int ret; virConnectPtr conn; DEBUG("domain=%p", domain); @@ -1501,8 +1524,14 @@ virDomainSuspend(virDomainPtr domain) conn = domain->conn; - if (conn->driver->domainSuspend) - return conn->driver->domainSuspend (domain); + if (conn->driver->domainSuspend) { + ret = conn->driver->domainSuspend (domain); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_SUSPENDED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_SUSPENDED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -1521,6 +1550,7 @@ virDomainSuspend(virDomainPtr domain) int virDomainResume(virDomainPtr domain) { + int ret; virConnectPtr conn; DEBUG("domain=%p", domain); @@ -1535,8 +1565,14 @@ virDomainResume(virDomainPtr domain) conn = domain->conn; - if (conn->driver->domainResume) - return conn->driver->domainResume (domain); + if (conn->driver->domainResume) { + ret = conn->driver->domainResume (domain); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_RESUMED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_RESUMED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -1557,6 +1593,7 @@ virDomainResume(virDomainPtr domain) int virDomainSave(virDomainPtr domain, const char *to) { + int ret; char filepath[4096]; virConnectPtr conn; DEBUG("domain=%p, to=%s", domain, to); @@ -1595,8 +1632,14 @@ virDomainSave(virDomainPtr domain, const char *to) } - if (conn->driver->domainSave) - return conn->driver->domainSave (domain, to); + if (conn->driver->domainSave) { + ret = conn->driver->domainSave (domain, to); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_SAVED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_SAVED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -2805,6 +2848,7 @@ virDomainMemoryPeek (virDomainPtr dom, */ virDomainPtr virDomainDefineXML(virConnectPtr conn, const char *xml) { + virDomainPtr ret; DEBUG("conn=%p, xml=%s", conn, xml); if (!VIR_IS_CONNECT(conn)) { @@ -2820,8 +2864,14 @@ virDomainDefineXML(virConnectPtr conn, const char *xml) { return (NULL); } - if (conn->driver->domainDefineXML) - return conn->driver->domainDefineXML (conn, xml); + if (conn->driver->domainDefineXML) { + ret = conn->driver->domainDefineXML (conn, xml); + if(ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_ADDED)) + virBroadcastDomainEvent(ret, VIR_DOMAIN_EVENT_ADDED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return NULL; @@ -2838,6 +2888,7 @@ virDomainDefineXML(virConnectPtr conn, const char *xml) { int virDomainUndefine(virDomainPtr domain) { virConnectPtr conn; + int ret; DEBUG("domain=%p", domain); if (!VIR_IS_CONNECTED_DOMAIN(domain)) { @@ -2850,8 +2901,14 @@ virDomainUndefine(virDomainPtr domain) { return (-1); } - if (conn->driver->domainUndefine) - return conn->driver->domainUndefine (domain); + if (conn->driver->domainUndefine) { + ret = conn->driver->domainUndefine (domain); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_REMOVED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_REMOVED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; @@ -2926,6 +2983,7 @@ virConnectListDefinedDomains(virConnectPtr conn, char **const names, */ int virDomainCreate(virDomainPtr domain) { + int ret; virConnectPtr conn; DEBUG("domain=%p", domain); @@ -2943,9 +3001,14 @@ virDomainCreate(virDomainPtr domain) { return (-1); } - if (conn->driver->domainCreate) - return conn->driver->domainCreate (domain); - + if (conn->driver->domainCreate) { + ret = conn->driver->domainCreate (domain); + if(!ret && + conn->driver->domainEventEmitted && + !conn->driver->domainEventEmitted(VIR_DOMAIN_EVENT_STARTED)) + virBroadcastDomainEvent(domain, VIR_DOMAIN_EVENT_STARTED); + return ret; + } virLibConnError (conn, VIR_ERR_NO_SUPPORT, __FUNCTION__); return -1; } @@ -5318,3 +5381,148 @@ void virStringListFree(virStringList *list) list = p; } } + +/* + * Domain Event Notification + */ + +/** + * virDomainEventCallbackListFree: + * @head: event callback list head + * + * Free the memory in the domain event callback list + */ +void virDomainEventCallbackListFree(virDomainEventCallbackList *head) +{ + while (head) { + virDomainEventCallbackList *p = head->next; + VIR_FREE(head); + head = p; + } +} + +/** + * virConnectDomainEventRegister: + * @conn: pointer to the connection + * @cb: callback to the function handling domain events + * @opaque: opaque data to pass on to the callback + * + * Adds a Domain Event Callback + * + * Returns 0 on success, -1 on failure + */ +int virConnectDomainEventRegister(virConnectPtr conn, + virConnectDomainEventCallback cb, + void *opaque) +{ + int ret = 0; + int first = conn->domainEventCallbacks == NULL; + virDomainEventCallbackList *newNode; + + if (VIR_ALLOC(newNode) < 0) + return -1; + + newNode->next = conn->domainEventCallbacks; + newNode->conn = conn; + newNode->cb = cb; + newNode->opaque = opaque; + conn->domainEventCallbacks = newNode; + + /* Registering for a domain callback will enable delivery by default */ + if (conn->driver && conn->driver->enableEventClass && first) + ret = conn->driver->enableEventClass (conn, virDomainEvent, 1); + + DEBUG0("Registered domain event callback"); + return ret; +} + +/** + * virConnectDomainEventDeregister: + * @conn: pointer to the connection + * @cb: callback to the function handling domain events + * + * Removes a Domain Event Callback + * + * Returns 0 on success, -1 on failure + */ +int virConnectDomainEventDeregister(virConnectPtr conn, virConnectDomainEventCallback cb) +{ + int ret = -1; + virDomainEventCallbackList **last = &conn->domainEventCallbacks; + + while(*last) { + virDomainEventCallbackList *p = *last; + if( p->conn == conn && + p->cb == cb ) { + *last = p->next; + + VIR_FREE(p); + DEBUG0("Removed domain event callback"); + ret = 0; + break; + } + last = &p->next; + } + + /* De-registering for a domain callback will disable delivery of this event type*/ + if (conn->driver && + conn->driver->enableEventClass && + !conn->domainEventCallbacks) + ret = conn->driver->enableEventClass (conn, virDomainEvent, 0); + + return ret; +} + +/** + * virDispatchDomainEvent: + * @dom: the domain + * @event: the domain event code + * + * Internal function by which drivers to dispatch domain events. + */ +void virDispatchDomainEvent(virDomainPtr dom, + int event) +{ + virDomainEventCallbackList *p = dom->conn->domainEventCallbacks; + DEBUG("Dispatching callbacks for event %d to %p", event, dom->conn); + while(p) { + DEBUG(" Calling cb %p", p->cb); + (p->cb)(dom->conn, dom, event, p->opaque); + p = p->next; + } + DEBUG("Done dispatching events to %p", dom->conn); +} + +/** + * virDispatchDomainEvent: + * @dom: the domain + * @event: the domain event code + * + * Internal function by which drivers to broadcast domain events. + */ +void virBroadcastDomainEvent(virDomainPtr dom, + int event) +{ + virDomainPtr dom_bc; + virConnectPtr conn = dom->conn->driver->conns; + if(!conn) { + DEBUG0("Trying to dispatch to null connection list"); + return; + } + + while(conn) { + if(conn != dom->conn ) { + dom_bc = virDomainLookupByName(conn, dom->name); + } else { + dom_bc = dom; + } + + if(dom_bc) + virDispatchDomainEvent(dom_bc, event); + + if(dom_bc != dom) + virDomainFree(dom_bc); + + conn = conn->next; + } +} diff --git a/src/libvirt_sym.version b/src/libvirt_sym.version index b8c470c..243d4a7 100644 --- a/src/libvirt_sym.version +++ b/src/libvirt_sym.version @@ -146,6 +146,10 @@ virStorageVolGetXMLDesc; virStorageVolGetPath; + virEventRegisterHandleImpl; + virConnectDomainEventRegister; + virConnectDomainEventDeregister; + /* Symbols with __ are private only for use by the libvirtd daemon. They are not part of stable ABI diff --git a/src/lxc_driver.c b/src/lxc_driver.c index 30c3cab..ea80530 100644 --- a/src/lxc_driver.c +++ b/src/lxc_driver.c @@ -1238,6 +1238,7 @@ static virDriver lxcDriver = { VIR_DRV_LXC, /* the number virDrvNo */ "LXC", /* the name of the driver */ LIBVIR_VERSION_NUMBER, /* the version of the backend */ + NULL,/* conns */ lxcProbe, /* probe */ lxcOpen, /* open */ lxcClose, /* close */ @@ -1294,6 +1295,8 @@ static virDriver lxcDriver = { NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ + NULL, /* enableEventClass */ + NULL, /* domainEventSupported */ }; static virStateDriver lxcStateDriver = { diff --git a/src/openvz_driver.c b/src/openvz_driver.c index f68841e..a437818 100644 --- a/src/openvz_driver.c +++ b/src/openvz_driver.c @@ -958,6 +958,7 @@ static virDriver openvzDriver = { VIR_DRV_OPENVZ, "OPENVZ", LIBVIR_VERSION_NUMBER, + NULL,/* conns */ openvzProbe, /* probe */ openvzOpen, /* open */ openvzClose, /* close */ @@ -1014,6 +1015,8 @@ static virDriver openvzDriver = { NULL, /* domainMemoryPeek */ NULL, /* nodeGetCellsFreeMemory */ NULL, /* nodeGetFreeMemory */ + NULL, /* enableEventClass */ + NULL, /* domainEventSupported */ }; int openvzRegister(void) { diff --git a/src/qemu_conf.h b/src/qemu_conf.h index 88dfade..4d22119 100644 --- a/src/qemu_conf.h +++ b/src/qemu_conf.h @@ -68,6 +68,7 @@ struct qemud_driver { char *vncListen; virCapsPtr caps; + virDriverPtr vir_driver; }; diff --git a/src/qemu_driver.c b/src/qemu_driver.c index a88cb75..daabfa5 100644 --- a/src/qemu_driver.c +++ b/src/qemu_driver.c @@ -837,6 +837,8 @@ static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) { return -1; } +static virDomainPtr qemudDomainLookupByName(virConnectPtr conn, const char *name); + static int qemudStartVMDaemon(virConnectPtr conn, struct qemud_driver *driver, virDomainObjPtr vm, @@ -850,6 +852,7 @@ static int qemudStartVMDaemon(virConnectPtr conn, unsigned int qemuCmdFlags; fd_set keepfd; const char *emulator; + virDomainPtr dom; FD_ZERO(&keepfd); @@ -998,6 +1001,11 @@ static int qemudStartVMDaemon(virConnectPtr conn, qemudShutdownVMDaemon(conn, driver, vm); return -1; } + dom = qemudDomainLookupByName(conn,vm->def->name); + if(dom) + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_STARTED); + else + DEBUG0("Warning - dom is NULL at domain start"); } return ret; @@ -1032,11 +1040,22 @@ static int qemudVMData(struct qemud_driver *driver ATTRIBUTE_UNUSED, static void qemudShutdownVMDaemon(virConnectPtr conn ATTRIBUTE_UNUSED, struct qemud_driver *driver, virDomainObjPtr vm) { + virConnectPtr dconn; if (!virDomainIsActive(vm)) return; qemudLog(QEMUD_INFO, _("Shutting down VM '%s'\n"), vm->def->name); + if( driver && driver->vir_driver) { + dconn = driver->vir_driver->conns; + + if (dconn) { + virDomainPtr dom = qemudDomainLookupByName(dconn, vm->def->name); + /* Note dom should never be NULL here. Just being paranoid ... */ + if (dom) + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_STOPPED); + } + } kill(vm->pid, SIGTERM); qemudVMData(driver, vm, vm->stdout_fd); @@ -2127,6 +2146,7 @@ static int qemudDomainSuspend(virDomainPtr dom) { } vm->state = VIR_DOMAIN_PAUSED; qemudDebug("Reply %s", info); + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_SUSPENDED); VIR_FREE(info); return 0; } @@ -2155,6 +2175,7 @@ static int qemudDomainResume(virDomainPtr dom) { } vm->state = VIR_DOMAIN_RUNNING; qemudDebug("Reply %s", info); + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_RESUMED); VIR_FREE(info); return 0; } @@ -2196,7 +2217,7 @@ static int qemudDomainDestroy(virDomainPtr dom) { if (!vm->persistent) virDomainRemoveInactive(&driver->domains, vm); - + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_STOPPED); return 0; } @@ -2527,7 +2548,7 @@ static int qemudDomainSave(virDomainPtr dom, if (!vm->persistent) virDomainRemoveInactive(&driver->domains, vm); - + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_SAVED); return 0; } @@ -2728,6 +2749,7 @@ static int qemudDomainRestore(virConnectPtr conn, struct qemud_driver *driver = (struct qemud_driver *)conn->privateData; virDomainDefPtr def; virDomainObjPtr vm; + virDomainPtr dom; int fd; int ret; char *xml; @@ -2834,6 +2856,11 @@ static int qemudDomainRestore(virConnectPtr conn, vm->state = VIR_DOMAIN_RUNNING; } + dom = virDomainLookupByID(conn, def->id); + if(dom) { + virBroadcastDomainEvent(dom, VIR_DOMAIN_EVENT_RESTORED); + VIR_FREE(dom); + } return 0; } @@ -4008,10 +4035,38 @@ static int qemudNetworkSetAutostart(virNetworkPtr net, return 0; } +static int qemudDomainEventSupported(virDomainEventType evt) +{ + switch(evt) { + case VIR_DOMAIN_EVENT_STARTED: + case VIR_DOMAIN_EVENT_STOPPED: + case VIR_DOMAIN_EVENT_SUSPENDED: + case VIR_DOMAIN_EVENT_RESUMED: + case VIR_DOMAIN_EVENT_SAVED: + case VIR_DOMAIN_EVENT_RESTORED: + DEBUG("%s: %d", __FUNCTION__, (int)evt); + return true; + default: + return false; + } + return false; +} + +static int qemudEnableEventClass(virConnectPtr conn, + virEventClass event_class ATTRIBUTE_UNUSED, + int enable ATTRIBUTE_UNUSED) +{ + struct qemud_driver *d = conn->privateData; + d->vir_driver = conn->driver; + + return 0; +} + static virDriver qemuDriver = { VIR_DRV_QEMU, "QEMU", LIBVIR_VERSION_NUMBER, + NULL,/* conns */ qemudProbe, /* probe */ qemudOpen, /* open */ qemudClose, /* close */ @@ -4078,6 +4133,8 @@ static virDriver qemuDriver = { NULL, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ #endif + qemudEnableEventClass, /* enableEventClass */ + qemudDomainEventSupported, /* domainEventSupported */ }; static virNetworkDriver qemuNetworkDriver = { diff --git a/src/remote_internal.c b/src/remote_internal.c index 06b0f4f..2501e1f 100644 --- a/src/remote_internal.c +++ b/src/remote_internal.c @@ -34,6 +34,7 @@ #include <signal.h> #include <sys/types.h> #include <sys/stat.h> +#include <sys/poll.h> #include <fcntl.h> #ifdef HAVE_SYS_WAIT_H @@ -73,6 +74,7 @@ #include "remote_protocol.h" #include "memory.h" #include "util.h" +#include "event.h" /* Per-connection private data. */ #define MAGIC 999 /* private_data->magic if OK */ @@ -91,6 +93,7 @@ struct private_data { int localUses; /* Ref count for private data */ char *hostname; /* Original hostname */ FILE *debugLog; /* Debug remote protocol */ + PTHREAD_MUTEX_T (lock); /* Lock to handle async RPC calls */ #if HAVE_SASL sasl_conn_t *saslconn; /* SASL context */ const char *saslDecoded; @@ -156,6 +159,8 @@ static void make_nonnull_domain (remote_nonnull_domain *dom_dst, virDomainPtr do static void make_nonnull_network (remote_nonnull_network *net_dst, virNetworkPtr net_src); static void make_nonnull_storage_pool (remote_nonnull_storage_pool *pool_dst, virStoragePoolPtr vol_src); static void make_nonnull_storage_vol (remote_nonnull_storage_vol *vol_dst, virStorageVolPtr vol_src); +static void remoteDomainEventFired(int fd, int event, void *data); +static void remoteDomainProcessEvent(virConnectPtr conn, XDR *xdr); /*----------------------------------------------------------------------*/ @@ -680,6 +685,15 @@ doRemoteOpen (virConnectPtr conn, (xdrproc_t) xdr_void, (char *) NULL) == -1) goto failed; + pthread_mutex_init(&priv->lock, NULL); + DEBUG0("Adding Handler for remote events"); + /* Set up a callback to listen on the socket data */ + if (virEventAddHandle(priv->sock, + POLLIN | POLLERR | POLLHUP, + remoteDomainEventFired, + conn) < 0) { + DEBUG0("virEventAddHandle failed: No addHandleImpl defined. continuing without events."); + } /* Successful. */ retcode = VIR_DRV_OPEN_SUCCESS; @@ -4289,6 +4303,25 @@ remoteAuthPolkit (virConnectPtr conn, struct private_data *priv, int in_open, } #endif /* HAVE_POLKIT */ +static int remoteEventsEnableEventClass (virConnectPtr conn, + virEventClass event_class, + int enable) +{ + remote_events_enable_args args; + remote_events_enable_ret ret; + + args.event_class = event_class; + args.enable = enable; + + memset (&ret, 0, sizeof ret); + if (call ( conn, conn->privateData, 0, REMOTE_PROC_EVENTS_ENABLE, + (xdrproc_t) xdr_remote_events_enable_args, (char *) &args, + (xdrproc_t) xdr_remote_events_enable_ret, (char *) &ret) == -1) + return -1; + + return 0; +} + /*----------------------------------------------------------------------*/ static int really_write (virConnectPtr conn, struct private_data *priv, @@ -4362,19 +4395,30 @@ call (virConnectPtr conn, struct private_data *priv, } xdr_destroy (&xdr); + /* Lock on the connection semaphore, so we do not pull + * data off the wire if an async event fires while we + * are waiting on the response */ + pthread_mutex_lock(&priv->lock); + /* Send length word followed by header+args. */ if (really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1 || - really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) + really_write (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len-4) == -1) { + pthread_mutex_unlock(&priv->lock); return -1; + } +retry_read: /* Read and deserialise length word. */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer2, sizeof buffer2) == -1) { + pthread_mutex_unlock(&priv->lock); return -1; + } xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); if (!xdr_int (&xdr, &len)) { error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); + pthread_mutex_unlock(&priv->lock); return -1; } xdr_destroy (&xdr); @@ -4385,18 +4429,22 @@ call (virConnectPtr conn, struct private_data *priv, if (len < 0 || len > REMOTE_MESSAGE_MAX) { error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, _("packet received from server too large")); + pthread_mutex_unlock(&priv->lock); return -1; } /* Read reply header and what follows (either a ret or an error). */ - if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) + if (really_read (conn, priv, flags & REMOTE_CALL_IN_OPEN, buffer, len) == -1) { + pthread_mutex_unlock(&priv->lock); return -1; + } /* Deserialise reply header. */ xdrmem_create (&xdr, buffer, len, XDR_DECODE); if (!xdr_remote_message_header (&xdr, &hdr)) { error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, _("invalid header in reply")); + pthread_mutex_unlock(&priv->lock); return -1; } @@ -4407,6 +4455,7 @@ call (virConnectPtr conn, struct private_data *priv, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, _("unknown program (received %x, expected %x)"), hdr.prog, REMOTE_PROGRAM); + pthread_mutex_unlock(&priv->lock); return -1; } if (hdr.vers != REMOTE_PROTOCOL_VERSION) { @@ -4415,19 +4464,30 @@ call (virConnectPtr conn, struct private_data *priv, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, _("unknown protocol version (received %x, expected %x)"), hdr.vers, REMOTE_PROTOCOL_VERSION); + pthread_mutex_unlock(&priv->lock); return -1; } - /* If we extend the server to actually send asynchronous messages, then - * we'll need to change this so that it can recognise an asynch - * message being received at this point. - */ + if (hdr.proc == REMOTE_PROC_EVENTS_DOMAIN_EVENT && + hdr.direction == REMOTE_MESSAGE) { + /* An async message has come in while we were waiting for the + * response. Process it to pull it off the wire, and try again + */ + DEBUG0("Encountered an event while waiting for a response"); + + remoteDomainProcessEvent(conn, &xdr); + + DEBUG0("Retrying read"); + xdr_destroy (&xdr); + goto retry_read; + } if (hdr.proc != proc_nr) { __virRaiseError (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, NULL, NULL, VIR_FROM_REMOTE, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, _("unknown procedure (received %x, expected %x)"), hdr.proc, proc_nr); + pthread_mutex_unlock(&priv->lock); return -1; } if (hdr.direction != REMOTE_REPLY) { @@ -4436,6 +4496,7 @@ call (virConnectPtr conn, struct private_data *priv, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, _("unknown direction (received %x, expected %x)"), hdr.direction, REMOTE_REPLY); + pthread_mutex_unlock(&priv->lock); return -1; } if (hdr.serial != serial) { @@ -4443,6 +4504,7 @@ call (virConnectPtr conn, struct private_data *priv, VIR_ERR_RPC, VIR_ERR_ERROR, NULL, NULL, NULL, 0, 0, _("unknown serial (received %x, expected %x)"), hdr.serial, serial); + pthread_mutex_unlock(&priv->lock); return -1; } @@ -4458,6 +4520,7 @@ call (virConnectPtr conn, struct private_data *priv, return -1; } xdr_destroy (&xdr); + pthread_mutex_unlock(&priv->lock); return 0; case REMOTE_ERROR: @@ -4465,6 +4528,7 @@ call (virConnectPtr conn, struct private_data *priv, if (!xdr_remote_error (&xdr, &rerror)) { error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, VIR_ERR_RPC, _("unmarshalling remote_error")); + pthread_mutex_unlock(&priv->lock); return -1; } xdr_destroy (&xdr); @@ -4475,10 +4539,12 @@ call (virConnectPtr conn, struct private_data *priv, rerror.code == VIR_ERR_RPC && rerror.level == VIR_ERR_ERROR && STRPREFIX(*rerror.message, "unknown procedure")) { + pthread_mutex_unlock(&priv->lock); return -2; } server_error (flags & REMOTE_CALL_IN_OPEN ? NULL : conn, &rerror); xdr_free ((xdrproc_t) xdr_remote_error, (char *) &rerror); + pthread_mutex_unlock(&priv->lock); return -1; default: @@ -4487,6 +4553,7 @@ call (virConnectPtr conn, struct private_data *priv, _("unknown status (received %x)"), hdr.status); xdr_destroy (&xdr); + pthread_mutex_unlock(&priv->lock); return -1; } } @@ -4872,6 +4939,7 @@ static virDriver driver = { .domainMemoryPeek = remoteDomainMemoryPeek, .nodeGetCellsFreeMemory = remoteNodeGetCellsFreeMemory, .getFreeMemory = remoteNodeGetFreeMemory, + .enableEventClass = remoteEventsEnableEventClass }; static virNetworkDriver network_driver = { @@ -4957,3 +5025,99 @@ remoteRegister (void) return 0; } + +/** + * remoteDomainProcessEvent + * + * Read the data off the wire, and process the event + */ +void remoteDomainProcessEvent(virConnectPtr conn, + XDR *xdr) +{ + remote_events_domain_event_ret ret; + memset (&ret, 0, sizeof ret); + + virDomainPtr dom; + /* unmarshall parameters, and process it*/ + if (! xdr_remote_events_domain_event_ret(xdr, &ret) ) { + error (conn, VIR_ERR_RPC, _("remoteDomainProcessEvent: unmarshalling ret")); + return; + } + + dom = get_nonnull_domain(conn,ret.dom); + virDispatchDomainEvent(dom, ret.event); +} + +/** remoteDomainEventFired: + * + * The callback for monitoring the remote socket + * for event data + */ +void remoteDomainEventFired(int fd ATTRIBUTE_UNUSED, + int event ATTRIBUTE_UNUSED, + void *opaque) +{ + char buffer[REMOTE_MESSAGE_MAX]; + char buffer2[4]; + struct remote_message_header hdr; + XDR xdr; + int len; + + virConnectPtr conn = opaque; + struct private_data *priv = conn->privateData; + + /* This may be a response to a command, or it + * may be a domain event. + * grab a lock on the connection so we don't stomp on + * command replys + */ + DEBUG("%s : Event fired - grabbing lock", __FUNCTION__); + pthread_mutex_lock(&priv->lock); + + /* Read and deserialise length word. */ + if (really_read (conn, priv, 0, buffer2, sizeof buffer2) == -1) { + pthread_mutex_unlock(&priv->lock); + return; + } + + xdrmem_create (&xdr, buffer2, sizeof buffer2, XDR_DECODE); + if (!xdr_int (&xdr, &len)) { + error (conn, VIR_ERR_RPC, _("xdr_int (length word, reply)")); + pthread_mutex_unlock(&priv->lock); + return; + } + xdr_destroy (&xdr); + + /* Length includes length word - adjust to real length to read. */ + len -= 4; + + if (len < 0 || len > REMOTE_MESSAGE_MAX) { + error (conn, VIR_ERR_RPC, _("packet received from server too large")); + pthread_mutex_unlock(&priv->lock); + return; + } + + /* Read reply header and what follows (either a ret or an error). */ + if (really_read (conn, priv, 0, buffer, len) == -1) { + error (conn, VIR_ERR_RPC, _("error reading buffer from memory")); + return; + } + + /* Deserialise reply header. */ + xdrmem_create (&xdr, buffer, len, XDR_DECODE); + if (!xdr_remote_message_header (&xdr, &hdr)) { + error (conn, VIR_ERR_RPC, _("invalid header in event firing")); + pthread_mutex_unlock(&priv->lock); + return; + } + + if (hdr.proc == REMOTE_PROC_EVENTS_DOMAIN_EVENT && + hdr.direction == REMOTE_MESSAGE) { + DEBUG0("Encountered an async event"); + remoteDomainProcessEvent(conn, &xdr); + } else { + DEBUG0("invalid proc in event firing"); + error (conn, VIR_ERR_RPC, _("invalid proc in event firing")); + } + pthread_mutex_unlock(&priv->lock); +} \ No newline at end of file diff --git a/src/test.c b/src/test.c index 69c9090..ad5ea74 100644 --- a/src/test.c +++ b/src/test.c @@ -1539,6 +1539,7 @@ static virDriver testDriver = { VIR_DRV_TEST, "Test", LIBVIR_VERSION_NUMBER, + NULL,/* conns */ NULL, /* probe */ testOpen, /* open */ testClose, /* close */ @@ -1595,6 +1596,8 @@ static virDriver testDriver = { NULL, /* domainMemoryPeek */ testNodeGetCellsFreeMemory, /* nodeGetCellsFreeMemory */ NULL, /* getFreeMemory */ + NULL, /* enableEventClass */ + NULL, /* domainEventSupported */ }; static virNetworkDriver testNetworkDriver = {
-- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list