[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Re: [libvirt] [PATCH 1/3] Domain events - primary implementation



This patch is somewhat large, and touches many files.
However - please remember to ignore generated files

Signed-off-by: Ben Guthro <bguthro virtualiron com>

 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 = {

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]