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

[Xen-devel] [PATCH 18/18] libxl: New event generation API



Replace the existing API for retrieving high-level events (events
about domains, etc.) from libxl with a new one.

This changes the definition and semantics of the `libxl_event'
structure, and replaces the calls for obtaining information about
domain death and disk eject events.

This is an incompatible change, sorry.  The alternative was to try to
provide both the previous horrid API and the new one, and would also
involve never using the name `libxl_event' for the new interface.

Signed-off-by: Ian Jackson <ian.jackson@xxxxxxxxxxxxx>
---
 tools/libxl/libxl.c          |  329 +++++++++++++++++++++++++++++++-----------
 tools/libxl/libxl.h          |   55 ++------
 tools/libxl/libxl_event.c    |  200 +++++++++++++++++++++++---
 tools/libxl/libxl_event.h    |  182 +++++++++++++++++++++++-
 tools/libxl/libxl_internal.c |    6 +
 tools/libxl/libxl_internal.h |   87 +++++++++++-
 tools/libxl/libxl_types.idl  |   35 ++++-
 tools/libxl/xl_cmdimpl.c     |  270 ++++++++++++++++++++---------------
 8 files changed, 885 insertions(+), 279 deletions(-)

diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 23a7539..e94cbf1 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -60,8 +60,11 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
      * only as an initialiser, not as an expression. */
     memcpy(&ctx->lock, &mutex_value, sizeof(ctx->lock));
 
+    LIBXL_TAILQ_INIT(&ctx->occurred);
+
     ctx->osevent_hooks = 0;
 
+    ctx->fd_polls = 0;
     ctx->fd_beforepolled = 0;
     LIBXL_LIST_INIT(&ctx->efds);
     LIBXL_TAILQ_INIT(&ctx->etimes);
@@ -70,6 +73,9 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
     LIBXL_SLIST_INIT(&ctx->watch_freeslots);
     libxl__ev_fd_init(&ctx->watch_efd);
 
+    LIBXL_TAILQ_INIT(&ctx->death_list);
+    libxl__ev_xswatch_init(&ctx->death_watch);
+
     if ( stat(XENSTORE_PID_FILE, &stat_buf) != 0 ) {
         LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Is xenstore daemon running?\n"
                      "failed to stat %s", XENSTORE_PID_FILE);
@@ -97,6 +103,20 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
     return 0;
 }
 
+static void free_disable_deaths(libxl__gc *gc,
+                                struct libxl__evgen_domain_death_list *l) {
+    libxl_evgen_domain_death *death;
+    while ((death = LIBXL_TAILQ_FIRST(l)))
+        libxl__evdisable_domain_death(gc, death);
+}
+
+static void discard_events(struct libxl__event_list *l) {
+    /* doesn't bother unlinking from the list, so l is corrupt on return */
+    libxl_event *ev;
+    LIBXL_TAILQ_FOREACH(ev, l, link)
+        libxl_event_free(0, ev);
+}
+
 int libxl_ctx_free(libxl_ctx *ctx)
 {
     if (!ctx) return 0;
@@ -106,6 +126,13 @@ int libxl_ctx_free(libxl_ctx *ctx)
 
     /* Deregister all libxl__ev_KINDs: */
 
+    free_disable_deaths(gc, &CTX->death_list);
+    free_disable_deaths(gc, &CTX->death_reported);
+
+    libxl_evgen_disk_eject *eject;
+    while ((eject = LIBXL_LIST_FIRST(&CTX->disk_eject_evgens)))
+        libxl__evdisable_disk_eject(gc, eject);
+
     for (i = 0; i < ctx->watch_nslots; i++)
         assert(!libxl__watch_slot_contents(gc, i));
     libxl__ev_fd_deregister(gc, &ctx->watch_efd);
@@ -119,9 +146,16 @@ int libxl_ctx_free(libxl_ctx *ctx)
     libxl_version_info_dispose(&ctx->version_info);
     if (ctx->xsh) xs_daemon_close(ctx->xsh);
 
+    free(ctx->fd_polls);
     free(ctx->fd_beforepolled);
     free(ctx->watch_slots);
 
+    discard_events(&ctx->occurred);
+    discard_events(&gc->occurred_for_callback);
+      /* There shouldn't be any of the latter - they would have had to
+       * have been generated during libxl_ctx_free - but throwing them
+       * away is more friendly than asserting. */
+
     GC_FREE;
     free(ctx);
     return 0;
@@ -620,117 +654,173 @@ int libxl_domain_shutdown(libxl_ctx *ctx, uint32_t 
domid, int req)
     return 0;
 }
 
-int libxl_get_wait_fd(libxl_ctx *ctx, int *fd)
-{
-    *fd = xs_fileno(ctx->xsh);
-    return 0;
-}
+static void domain_death_xswatch_callback(libxl__gc *gc, libxl__ev_xswatch *w,
+                                        const char *wpath, const char *epath) {
+    libxl_evgen_domain_death *evg;
+    uint32_t domid;
+    int rc;
 
-int libxl_wait_for_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_waiter 
*waiter)
-{
-    waiter->path = strdup("@releaseDomain");
-    if (asprintf(&(waiter->token), "%d", LIBXL_EVENT_TYPE_DOMAIN_DEATH) < 0)
-        return -1;
-    if (!xs_watch(ctx->xsh, waiter->path, waiter->token))
-        return -1;
-    return 0;
-}
+    CTX_LOCK;
 
-int libxl_wait_for_disk_ejects(libxl_ctx *ctx, uint32_t guest_domid, 
libxl_device_disk *disks, int num_disks, libxl_waiter *waiter)
-{
-    GC_INIT(ctx);
-    int i, rc = -1;
-    uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
+    evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
+    if (!evg) goto out;
 
-    if (!domid)
-        domid = guest_domid;
+    domid = evg->domid;
 
-    for (i = 0; i < num_disks; i++) {
-        if (asprintf(&(waiter[i].path), "%s/device/vbd/%d/eject",
-                     libxl__xs_get_dompath(gc, domid),
-                     libxl__device_disk_dev_number(disks[i].vdev,
-                                                   NULL, NULL)) < 0)
-            goto out;
-        if (asprintf(&(waiter[i].token), "%d", LIBXL_EVENT_TYPE_DISK_EJECT) < 
0)
+    for (;;) {
+        int nentries = LIBXL_TAILQ_NEXT(evg, entry) ? 200 : 1;
+        xc_domaininfo_t domaininfos[nentries];
+        const xc_domaininfo_t *got = domaininfos, *gotend;
+
+        rc = xc_domain_getinfolist(CTX->xch, domid, nentries, domaininfos);
+        if (rc == -1) {
+            LIBXL__EVENT_DISASTER(gc, "xc_domain_getinfolist failed while"
+                                  " processing @releaseDomain watch event",
+                                  errno, 0);
             goto out;
-        xs_watch(ctx->xsh, waiter[i].path, waiter[i].token);
+        }
+        gotend = &domaininfos[rc];
+
+        for (;;) {
+            if (!evg)
+                goto all_reported;
+
+            if (!rc || got->domain > evg->domid) {
+                /* ie, the list doesn't contain evg->domid any more so
+                 * the domain has been destroyed */
+                libxl_evgen_domain_death *evg_next;
+
+                libxl_event *ev = NEW_EVENT(gc, DOMAIN_DESTROY, evg->domid);
+                if (!ev) goto out;
+
+                libxl__event_occurred(gc, ev);
+
+                evg->death_reported = 1;
+                evg_next = LIBXL_TAILQ_NEXT(evg, entry);
+                LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+                LIBXL_TAILQ_INSERT_HEAD(&CTX->death_reported, evg, entry);
+                evg = evg_next;
+
+                continue;
+            }
+            
+            if (got == gotend)
+                break;
+
+            if (got->domain < evg->domid) {
+                got++;
+                continue;
+            }
+
+            assert(evg->domid == got->domain);
+
+            if (!evg->shutdown_reported &&
+                (got->flags & XEN_DOMINF_shutdown)) {
+                libxl_event *ev = NEW_EVENT(gc, DOMAIN_SHUTDOWN, got->domain);
+                if (!ev) goto out;
+                
+                ev->u.domain_shutdown.shutdown_reason =
+                    (got->flags >> XEN_DOMINF_shutdownshift) &
+                    XEN_DOMINF_shutdownmask;
+                libxl__event_occurred(gc, ev);
+
+                evg->shutdown_reported = 1;
+            }
+            evg = LIBXL_TAILQ_NEXT(evg, entry);
+        }
+
+        assert(rc); /* rc==0 results in us eating all evgs and quitting */
+        domid = gotend[-1].domain;
     }
-    rc = 0;
-out:
-    GC_FREE;
-    return rc;
+ all_reported:
+ out:
+
+    CTX_UNLOCK;
 }
 
-int libxl_get_event(libxl_ctx *ctx, libxl_event *event)
-{
-    unsigned int num;
-    char **events = xs_read_watch(ctx->xsh, &num);
-    if (num != 2) {
-        free(events);
-        return ERROR_FAIL;
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+                libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
+    GC_INIT(ctx);
+    libxl_evgen_domain_death *evg, *evg_search;
+    int rc;
+    
+    CTX_LOCK;
+
+    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
+    memset(evg, 0, sizeof(*evg));
+    evg->domid = domid;
+    evg->user = user;
+
+    LIBXL_TAILQ_INSERT_SORTED(&ctx->death_list, entry, evg, evg_search, ,
+                              evg->domid > evg_search->domid);
+
+    if (!libxl__ev_xswatch_isregistered(&ctx->death_watch)) {
+        rc = libxl__ev_xswatch_register(gc, &ctx->death_watch,
+                        domain_death_xswatch_callback, "@releaseDomain");
+        if (rc) { libxl__evdisable_domain_death(gc, evg); goto out; }
     }
-    event->path = strdup(events[XS_WATCH_PATH]);
-    event->token = strdup(events[XS_WATCH_TOKEN]);
-    event->type = atoi(event->token);
-    free(events);
-    return 0;
-}
 
-int libxl_stop_waiting(libxl_ctx *ctx, libxl_waiter *waiter)
-{
-    if (!xs_unwatch(ctx->xsh, waiter->path, waiter->token))
-        return ERROR_FAIL;
-    else
-        return 0;
-}
+    rc = 0;
 
-int libxl_free_event(libxl_event *event)
-{
-    free(event->path);
-    free(event->token);
-    return 0;
-}
+ out:
+    CTX_UNLOCK;
+    return rc;
+};
 
-int libxl_free_waiter(libxl_waiter *waiter)
-{
-    free(waiter->path);
-    free(waiter->token);
-    return 0;
-}
+void libxl__evdisable_domain_death(libxl__gc *gc,
+                                   libxl_evgen_domain_death *evg) {
+    CTX_LOCK;
 
-int libxl_event_get_domain_death_info(libxl_ctx *ctx, uint32_t domid, 
libxl_event *event, libxl_dominfo *info)
-{
-    if (libxl_domain_info(ctx, info, domid) < 0)
-        return 0;
+    if (!evg->death_reported)
+        LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
+    else
+        LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
 
-    if (info->running || (!info->shutdown && !info->dying))
-        return ERROR_INVAL;
+    free(evg);
 
-    return 1;
+    if (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
+        libxl__ev_xswatch_isregistered(&CTX->death_watch))
+        libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
+
+    CTX_UNLOCK;
 }
 
-int libxl_event_get_disk_eject_info(libxl_ctx *ctx, uint32_t domid, 
libxl_event *event, libxl_device_disk *disk)
-{
+void libxl_evdisable_domain_death(libxl_ctx *ctx,
+                                  libxl_evgen_domain_death *evg) {
     GC_INIT(ctx);
-    char *path;
+    libxl__evdisable_domain_death(gc, evg);
+    GC_FREE;
+}
+
+static void disk_eject_xswatch_callback(libxl__gc *gc, libxl__ev_xswatch 
*watch,
+                                        const char *wpath, const char *epath) {
+    libxl_evgen_disk_eject *evg = (void*)watch;
     char *backend;
     char *value;
     char backend_type[BACKEND_STRING_SIZE+1];
 
-    value = libxl__xs_read(gc, XBT_NULL, event->path);
+    value = libxl__xs_read(gc, XBT_NULL, wpath);
 
-    if (!value || strcmp(value,  "eject")) {
-        GC_FREE;
-        return 0;
+    if (!value || strcmp(value,  "eject"))
+        return;
+
+    if (libxl__xs_write(gc, XBT_NULL, wpath, "")) {
+        LIBXL__EVENT_DISASTER(gc, "xs_write failed acknowledging eject",
+                              errno, LIBXL_EVENT_TYPE_DISK_EJECT);
+        return;
     }
 
-    path = strdup(event->path);
-    path[strlen(path) - 6] = '\0';
-    backend = libxl__xs_read(gc, XBT_NULL, libxl__sprintf(gc, "%s/backend", 
path));
+    libxl_event *ev = NEW_EVENT(gc, DISK_EJECT, evg->domid);
+    libxl_device_disk *disk = &ev->u.disk_eject.disk;
+    
+    backend = libxl__xs_read(gc, XBT_NULL,
+                             libxl__sprintf(gc, "%.*s/backend",
+                                            (int)strlen(wpath)-6, wpath));
 
     sscanf(backend,
-            "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE) 
"[a-z]/%*d/%*d",
-            &disk->backend_domid, backend_type);
+            "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
+           "[a-z]/%*d/%*d",
+           &disk->backend_domid, backend_type);
     if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
         disk->backend = LIBXL_DISK_BACKEND_TAP;
     } else if (!strcmp(backend_type, "qdisk")) {
@@ -739,19 +829,82 @@ int libxl_event_get_disk_eject_info(libxl_ctx *ctx, 
uint32_t domid, libxl_event
         disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
     }
 
-    disk->pdev_path = strdup("");
+    disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
     disk->format = LIBXL_DISK_FORMAT_EMPTY;
     /* this value is returned to the user: do not free right away */
-    disk->vdev = xs_read(ctx->xsh, XBT_NULL, libxl__sprintf(gc, "%s/dev", 
backend), NULL);
+    disk->vdev = xs_read(CTX->xsh, XBT_NULL,
+                         libxl__sprintf(gc, "%s/dev", backend), NULL);
     disk->removable = 1;
     disk->readwrite = 0;
     disk->is_cdrom = 1;
 
-    free(path);
+    libxl__event_occurred(gc, ev);
+}
+
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
+                              const char *vdev, libxl_ev_user user,
+                              libxl_evgen_disk_eject **evgen_out) {
+    GC_INIT(ctx);
+    CTX_LOCK;
+    int rc;
+    char *path;
+    libxl_evgen_disk_eject *evg = NULL;
+
+    evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
+    memset(evg, 0, sizeof(*evg));
+    evg->user = user;
+    evg->domid = guest_domid;
+    LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
+
+    evg->vdev = strdup(vdev);
+    if (!evg->vdev) { rc = ERROR_NOMEM; goto out; }
+
+    uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
+
+    if (!domid)
+        domid = guest_domid;
+
+    path = libxl__sprintf(gc, "%s/device/vbd/%d/eject",
+                 libxl__xs_get_dompath(gc, domid),
+                 libxl__device_disk_dev_number(vdev, NULL, NULL));
+    if (!path) { rc = ERROR_NOMEM; goto out; }
+
+    rc = libxl__ev_xswatch_register(gc, &evg->watch,
+                                    disk_eject_xswatch_callback, path);
+    if (rc) goto out;
+
+    CTX_UNLOCK;
     GC_FREE;
-    return 1;
+    return 0;
+
+ out:
+    if (evg)
+        libxl__evdisable_disk_eject(gc, evg);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
+    CTX_LOCK;
+
+    LIBXL_LIST_REMOVE(evg, entry);
+
+    if (libxl__ev_xswatch_isregistered(&evg->watch))
+        libxl__ev_xswatch_deregister(gc, &evg->watch);
+
+    free(evg->vdev);
+    free(evg);
+
+    CTX_UNLOCK;
 }
 
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
+    GC_INIT(ctx);
+    libxl__evdisable_disk_eject(gc, evg);
+    GC_FREE;
+}    
+
 int libxl_domain_destroy(libxl_ctx *ctx, uint32_t domid, int force)
 {
     GC_INIT(ctx);
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index 8d6c788..bdb7b56 100644
--- a/tools/libxl/libxl.h
+++ b/tools/libxl/libxl.h
@@ -53,7 +53,10 @@
  *    A public function may be called from within libxl; the call
  *    context initialisation macros will make sure that the internal
  *    caller's context is reused (eg, so that the same xenstore
- *    transaction is used).
+ *    transaction is used).  But in-libxl callers of libxl public
+ *    functions should note that any libxl public function may cause
+ *    recursively reentry into libxl via the application's event
+ *    callback hook.
  *
  *    Public functions have names like libxl_foobar.
  *
@@ -152,6 +155,8 @@ void libxl_key_value_list_dispose(libxl_key_value_list 
*kvl);
 
 typedef uint32_t libxl_hwcap[8];
 
+typedef uint64_t libxl_ev_user;
+
 typedef struct {
     uint32_t size;          /* number of bytes in map */
     uint8_t *map;
@@ -200,6 +205,9 @@ typedef struct {
     int v;
 } libxl_enum_string_table;
 
+struct libxl_event;
+typedef LIBXL_TAILQ_ENTRY(struct libxl_event) libxl_ev_link;
+
 typedef struct libxl__ctx libxl_ctx;
 
 #include "_libxl_types.h"
@@ -298,51 +306,6 @@ int libxl_run_bootloader(libxl_ctx *ctx,
 
   /* 0 means ERROR_ENOMEM, which we have logged */
 
-/* events handling */
-
-typedef struct {
-    /* event type */
-    libxl_event_type type;
-    /* data for internal use of the library */
-    char *path;
-    char *token;
-} libxl_event;
-
-typedef struct {
-    char *path;
-    char *token;
-} libxl_waiter;
-
-
-int libxl_get_wait_fd(libxl_ctx *ctx, int *fd);
-/* waiter is allocated by the caller */
-int libxl_wait_for_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_waiter 
*waiter);
-/* waiter is a preallocated array of num_disks libxl_waiter elements */
-int libxl_wait_for_disk_ejects(libxl_ctx *ctx, uint32_t domid, 
libxl_device_disk *disks, int num_disks, libxl_waiter *waiter);
-int libxl_get_event(libxl_ctx *ctx, libxl_event *event);
-int libxl_stop_waiting(libxl_ctx *ctx, libxl_waiter *waiter);
-int libxl_free_event(libxl_event *event);
-int libxl_free_waiter(libxl_waiter *waiter);
-
-/*
- * Returns:
- *  - 0 if the domain is dead but there is no cleanup to be done. e.g
- *    because someone else has already done it.
- *  - 1 if the domain is dead and there is cleanup to be done.
- *
- * Can return error if the domain exists and is still running.
- *
- * *info will contain valid domain state iff 1 is returned. In
- * particular if 1 is returned then info->shutdown_reason is
- * guaranteed to be valid since by definition the domain is
- * (shutdown||dying))
- */
-int libxl_event_get_domain_death_info(libxl_ctx *ctx, uint32_t domid, 
libxl_event *event, libxl_dominfo *info);
-
-/*
- * Returns true and fills *disk if the caller should eject the disk
- */
-int libxl_event_get_disk_eject_info(libxl_ctx *ctx, uint32_t domid, 
libxl_event *event, libxl_device_disk *disk);
 
 int libxl_domain_rename(libxl_ctx *ctx, uint32_t domid,
                         const char *old_name, const char *new_name);
diff --git a/tools/libxl/libxl_event.c b/tools/libxl/libxl_event.c
index dc6543f..5a93bdf 100644
--- a/tools/libxl/libxl_event.c
+++ b/tools/libxl/libxl_event.c
@@ -149,7 +149,8 @@ static void time_insert_finite(libxl__gc *gc, 
libxl__ev_time *ev)
 }
 
 static int time_register_finite(libxl__gc *gc, libxl__ev_time *ev,
-                                struct timeval abs) {
+                                struct timeval abs)
+{
     int rc;
     
     rc = OSEVENT_HOOK(timeout_register, &ev->for_app_reg, abs, ev);
@@ -512,9 +513,9 @@ void libxl__ev_xswatch_deregister(libxl__gc *gc, 
libxl__ev_xswatch *w)
  * osevent poll
  */
 
-int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
-                             struct pollfd *fds, int *timeout_upd,
-                             struct timeval now)
+static int beforepoll_unlocked(libxl__gc *gc, int *nfds_io,
+                               struct pollfd *fds, int *timeout_upd,
+                               struct timeval now)
 {
     libxl__ev_fd *efd;
     int i;
@@ -526,9 +527,6 @@ int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
      * the fds array corresponds to a slot in fd_beforepolled.
      */
 
-    GC_INIT(ctx);
-    CTX_LOCK;
-
     if (*nfds_io) {
         /*
          * As an optimisation, we don't touch fd_beforepolled_used
@@ -600,17 +598,26 @@ int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
             *timeout_upd = our_timeout;
     }
 
-    CTX_UNLOCK;
-    GC_FREE;
     return rc;
 }
 
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd 
*fds,
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+                             struct pollfd *fds, int *timeout_upd,
                              struct timeval now)
 {
-    int i;
     GC_INIT(ctx);
     CTX_LOCK;
+    int rc = beforepoll_unlocked(gc, nfds_io, fds, timeout_upd, now);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+static void afterpoll_unlocked(libxl__gc *gc,
+                               int nfds, const struct pollfd *fds,
+                               struct timeval now)
+{
+    int i;
 
     assert(nfds <= CTX->fd_beforepolled_used);
 
@@ -646,12 +653,18 @@ void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, 
const struct pollfd *fds,
 
         etime->func(gc, etime, &etime->abs);
     }
+}
 
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd 
*fds,
+                             struct timeval now)
+{
+    GC_INIT(ctx);
+    CTX_LOCK;
+    afterpoll_unlocked(gc, nfds, fds, now);
     CTX_UNLOCK;
     GC_FREE;
 }
 
-
 /*
  * osevent hook and callback machinery
  */
@@ -714,11 +727,10 @@ void libxl__event_disaster(libxl__gc *gc, const char 
*msg, int errnoval,
                type ? libxl_event_type_to_string(type) : "",
                type ? ")" : "");
 
-    /*
-     * FIXME: This should call the "disaster" hook supplied to
-     * libxl_event_register_callbacks, which will be introduced in the
-     * next patch.
-     */
+    if (CTX->event_hooks && CTX->event_hooks->disaster) {
+        CTX->event_hooks->disaster(CTX->event_hooks_user, type, msg, errnoval);
+        return;
+    }
 
     const char verybad[] =
         "DISASTER in event loop not handled by libxl application";
@@ -728,6 +740,160 @@ void libxl__event_disaster(libxl__gc *gc, const char 
*msg, int errnoval,
 }
 
 /*
+ * Event retrieval etc.
+ */
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+                  const libxl_event_hooks *hooks, void *user)
+{
+    ctx->event_hooks = hooks;
+    ctx->event_hooks_user = user;
+}
+
+void libxl__event_occurred(libxl__gc *gc, libxl_event *event)
+{
+    if (CTX->event_hooks &&
+        (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
+        /* libxl__gc_cleanup will call the callback, just before exit
+         * from libxl.  This helps avoid reentrancy bugs: parts of
+         * libxl that call libxl__event_occurred do not have to worry
+         * that libxl might be reentered at that point. */
+        LIBXL_TAILQ_INSERT_TAIL(&gc->occurred_for_callback, event, link);
+        return;
+    } else {
+        LIBXL_TAILQ_INSERT_TAIL(&CTX->occurred, event, link);
+    }
+}
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event)
+{
+    /* Exceptionally, this function may be called from libxl, with ctx==0 */
+    libxl_event_dispose(event);
+    free(event);
+}
+
+libxl_event *libxl__event_new(libxl__gc *gc,
+                              libxl_event_type type, uint32_t domid)
+{
+    libxl_event *ev;
+
+    ev = malloc(sizeof(*ev));
+    if (!ev) {
+        LIBXL__EVENT_DISASTER(gc, "allocate new event", errno, type);
+        return NULL;
+    }
+
+    memset(ev, 0, sizeof(*ev));
+    ev->type = type;
+    ev->domid = domid;
+
+    return ev;
+}
+
+static int event_check_unlocked(libxl__gc *gc, libxl_event **event_r,
+                                unsigned long typemask,
+                                libxl_event_predicate *pred, void *pred_user)
+{
+    libxl_event *ev;
+    int rc;
+
+    LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
+        if (!(typemask & (1UL << ev->type)))
+            continue;
+
+        if (pred && !pred(ev, pred_user))
+            continue;
+
+        /* got one! */
+        LIBXL_TAILQ_REMOVE(&CTX->occurred, ev, link);
+        *event_r = ev;
+        rc = 0;
+        goto out;
+    }
+    rc = ERROR_NOT_READY;
+
+ out:
+    return rc;
+}
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+                      unsigned long typemask,
+                      libxl_event_predicate *pred, void *pred_user)
+{
+    GC_INIT(ctx);
+    CTX_LOCK;
+    int rc = event_check_unlocked(gc, event_r, typemask, pred, pred_user);
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+                     unsigned long typemask,
+                     libxl_event_predicate *pred, void *pred_user)
+{
+    int rc;
+    struct timeval now;
+
+    GC_INIT(ctx);
+    CTX_LOCK;
+
+    for (;;) {
+        rc = event_check_unlocked(gc, event_r, typemask, pred, pred_user);
+        if (rc != ERROR_NOT_READY) goto out;
+
+        rc = libxl__gettimeofday(gc, &now);
+        if (rc) goto out;
+
+        int timeout;
+
+        for (;;) {
+            int nfds = CTX->fd_polls_allocd;
+            timeout = -1;
+            rc = beforepoll_unlocked(gc, &nfds, CTX->fd_polls, &timeout, now);
+            if (!rc) break;
+            if (rc != ERROR_BUFFERFULL) goto out;
+            
+            struct pollfd *newarray =
+                (nfds > INT_MAX / sizeof(struct pollfd) / 2) ? 0 :
+                realloc(CTX->fd_polls, sizeof(*newarray) * nfds);
+            
+            if (!newarray) { rc = ERROR_NOMEM; goto out; }
+
+            CTX->fd_polls = newarray;
+            CTX->fd_polls_allocd = nfds;
+        }
+
+        rc = poll(CTX->fd_polls, CTX->fd_polls_allocd, timeout);
+        if (rc < 0) {
+            if (errno == EINTR) continue;
+            LIBXL__LOG_ERRNOVAL(CTX, LIBXL__LOG_ERROR, errno, "poll failed");
+            rc = ERROR_FAIL;
+            goto out;
+        }
+
+        rc = libxl__gettimeofday(gc, &now);
+        if (rc) goto out;
+
+        afterpoll_unlocked(gc, CTX->fd_polls_allocd, CTX->fd_polls, now);
+
+        /* we unlock and free the gc each time we go through this loop,
+         * so that (a) we don't accumulate garbage and (b) any events
+         * which are to be dispatched by callback are actually delivered
+         * in a timely fashion.
+         */
+        CTX_UNLOCK;
+        libxl__gc_cleanup(gc);
+        CTX_LOCK;
+    }
+
+ out:
+    CTX_UNLOCK;
+    GC_FREE;
+    return rc;
+}
+
+/*
  * Local variables:
  * mode: C
  * c-basic-offset: 4
diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
index cc4a348..3001100 100644
--- a/tools/libxl/libxl_event.h
+++ b/tools/libxl/libxl_event.h
@@ -18,6 +18,178 @@
 
 #include <libxl.h>
 
+/*======================================================================*/
+
+/*
+ * Domain event handling - getting Xen events from libxl
+ */
+
+#define LIBXL_EVENTMASK_ALL (~(unsigned long)0)
+
+typedef int libxl_event_predicate(const libxl_event*, void *user);
+  /* Return value is 0 if the event is unwanted or non-0 if it is.
+   * Predicates are not allowed to fail.
+   */
+
+int libxl_event_check(libxl_ctx *ctx, libxl_event **event_r,
+                      unsigned long typemask,
+                      libxl_event_predicate *predicate, void *predicate_user);
+  /* Searches for an event, already-happened, which matches typemask
+   * and predicate.  predicate==0 matches any event.
+   * libxl_event_check returns the event, which must then later be
+   * freed by the caller using libxl_event_free.
+   *
+   * Returns ERROR_NOT_READY if no such event has happened.
+   */
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+                     unsigned long typemask,
+                     libxl_event_predicate *predicate, void *predicate_user);
+  /* Like libxl_event_check but blocks if no suitable events are
+   * available, until some are.  Uses libxl_osevent_beforepoll/
+   * _afterpoll so may be inefficient if very many domains are being
+   * handled by a single program.
+   */
+
+void libxl_event_free(libxl_ctx *ctx, libxl_event *event);
+
+
+/* Alternatively or additionally, the application may also use this: */
+
+typedef struct libxl_event_hooks {
+    uint64_t event_occurs_mask;
+    void (*event_occurs)(void *user, const libxl_event *event);
+    void (*disaster)(void *user, libxl_event_type type,
+                     const char *msg, int errnoval);
+} libxl_event_hooks;
+
+void libxl_event_register_callbacks(libxl_ctx *ctx,
+                                    const libxl_event_hooks *hooks, void 
*user);
+  /*
+   * Arranges that libxl will henceforth call event_occurs for any
+   * events whose type is set in event_occurs_mask, rather than
+   * queueing the event for retrieval by libxl_event_check/wait.
+   * Events whose bit is clear in mask are not affected.
+   *
+   * event becomes owned by the application and must be freed, either
+   * by event_occurs or later.
+   *
+   * event_occurs may be NULL if mask is 0.
+   *
+   * libxl_event_register_callback also provides a way for libxl to
+   * report to the application that there was a problem reporting
+   * events; this can occur due to lack of host memory during event
+   * handling, or other wholly unrecoverable errors from system calls
+   * made by libxl.  This will not happen for frivolous reasons - only
+   * if the system, or the Xen components of it, are badly broken.
+   *
+   * msg and errnoval will describe the action that libxl was trying
+   * to do, and type specifies the type of libxl events which may be
+   * missing.  type may be 0 in which case events of all types may be
+   * missing.
+   *
+   * disaster may be NULL.  If it is, or if _register_callbacks has
+   * not been called, errors of this kind are fatal to the entire
+   * application: libxl will print messages to its logs and to stderr
+   * and call exit(-1).
+   *
+   * If disaster returns, it may be the case that some or all future
+   * libxl calls will return errors; likewise it may be the case that
+   * no more events (of the specified type, if applicable) can be
+   * produced.  An application which supplies a disaster function
+   * should normally react either by exiting, or by (when it has
+   * returned to its main event loop) shutting down libxl with
+   * libxl_ctx_free and perhaps trying to restart it with
+   * libxl_ctx_init.
+   *
+   * In any case before calling disaster, libxl will have logged a
+   * message with level XTL_CRITICAL.
+   *
+   * Reentrancy: it IS permitted to call libxl from within
+   * event_occurs.  It is NOT permitted to call libxl from within
+   * disaster.
+   *
+   * libxl_event_register_callbacks may be called as many times, with
+   * different parameters, as the application likes; the most recent
+   * call determines the libxl behaviour.  However it is NOT safe to
+   * call _register_callbacks concurrently with, or reentrantly from,
+   * any other libxl function.
+   *
+   * Calls to _register_callbacks do not affect events which have
+   * already occurred.
+   */
+
+
+/*
+ * Events are only generated if they have been requested.
+ * The following functions request the generation of specific events.
+ *
+ * Each set of functions for controlling event generation has this form:
+ *
+ *   typedef struct libxl__evgen_FOO libxl__evgen_FOO;
+ *   int libxl_evenable_FOO(libxl_ctx *ctx, FURTHER PARAMETERS,
+ *                          libxl_ev_user user, libxl__evgen_FOO **evgen_out);
+ *   void libxl_evdisable_FOO(libxl_ctx *ctx, libxl__evgen_FOO *evgen);
+ *
+ * The evenable function arranges that the events (as described in the
+ * doc comment for the individual function) will start to be generated
+ * by libxl.  On success, *evgen_out is set to a non-null pointer to
+ * an opaque struct.
+ *
+ * The user value is returned in the generated events and may be
+ * used by the caller for whatever it likes.  The type ev_user is
+ * guaranteed to be an unsigned integer type which is at least
+ * as big as uint64_t and is also guaranteed to be big enough to
+ * contain any intptr_t value.
+ *
+ * If it becomes desirable to stop generation of the relevant events,
+ * or to reclaim the resources in libxl associated with the evgen
+ * structure, the same evgen value should be passed to the evdisable
+ * function.  However, note that events which occurred prior to the
+ * evdisable call may still be returned.
+ *
+ * The caller may enable identical events more than once.  If they do
+ * so, each actual occurrence will generate several events to be
+ * returned by libxl_event_check, with the appropriate user value(s).
+ * Aside from this, each occurrence of each event is returned by
+ * libxl_event_check exactly once.
+ *
+ * An evgen is associated with the libxl_ctx used for its creation.
+ * After libxl_ctx_free, all corresponding evgen handles become
+ * invalid and must no longer be passed to evdisable.
+ *
+ * Events enabled with evenable prior to a fork and libxl_ctx_postfork
+ * are no longer generated after the fork/postfork; however the evgen
+ * structures are still valid and must be passed to evdisable if the
+ * memory they use should not be leaked.
+ *
+ * Applications should ensure that they eventually retrieve every
+ * event using libxl_event_check or libxl_event_wait, since events
+ * which occur but are not retreived by the application will be queued
+ * inside libxl indefinitely.  libxl_event_check/_wait may be O(n)
+ * where n is the number of queued events which do not match the
+ * criteria specified in the arguments to check/wait.
+ */
+
+typedef struct libxl__evgen_domain_death libxl_evgen_domain_death;
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+                         libxl_ev_user, libxl_evgen_domain_death **evgen_out);
+void libxl_evdisable_domain_death(libxl_ctx *ctx, libxl_evgen_domain_death*);
+  /* Arranges for the generation of DOMAIN_SHUTDOWN and DOMAIN_DESTROY
+   * events.  A domain which is destroyed before it shuts down
+   * may generate only a DESTROY event.
+   */
+
+typedef struct libxl__evgen_disk_eject libxl_evgen_disk_eject;
+int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t domid, const char *vdev,
+                        libxl_ev_user, libxl_evgen_disk_eject **evgen_out);
+void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject*);
+  /* Arranges for the generation of DISK_EJECT events.  A copy of the
+   * string *vdev will be made for libxl's internal use, and a pointer
+   * to this (or some other) copy will be returned as the vdev
+   * member of event.u.
+   */
+
 
 /*======================================================================*/
 
@@ -36,10 +208,10 @@
  *      poll();
  *      libxl_osevent_afterpoll(...);
  *      for (;;) {
- *        r=libxl_event_check(...);
- *        if (r==LIBXL_NOT_READY) break;
- *        if (r) handle failure;
- *        do something with the event;
+ *          r = libxl_event_check(...);
+ *          if (r==LIBXL_NOT_READY) break;
+ *          if (r) goto error_out;
+ *          do something with the event;
  *      }
  *   }
  *
@@ -171,6 +343,8 @@ void libxl_osevent_register_hooks(libxl_ctx *ctx,
    * Of these we would normally recommend (a).
    *
    * The value *hooks is not copied and must outlast the libxl_ctx.
+   *
+   * The value of user is stored by libxl and passed to the callbacks.
    */
 
 /* It is NOT legal to call _occurred_ reentrantly within any libxl
diff --git a/tools/libxl/libxl_internal.c b/tools/libxl/libxl_internal.c
index 232cc4a..afae0dd 100644
--- a/tools/libxl/libxl_internal.c
+++ b/tools/libxl/libxl_internal.c
@@ -74,6 +74,12 @@ void libxl__gc_cleanup(libxl__gc *gc)
     free(gc->alloc_ptrs);
     gc->alloc_ptrs = 0;
     gc->alloc_maxsize = 0;
+
+    libxl_event *ev, *ev_tmp;
+    LIBXL_TAILQ_FOREACH_SAFE(ev, &gc->occurred_for_callback, link, ev_tmp) {
+        LIBXL_TAILQ_REMOVE(&gc->occurred_for_callback, ev, link);
+        CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
+    }
 }
 
 void *libxl__zalloc(libxl__gc *gc, int bytes)
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index c4a5fa1..2f4d437 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -154,11 +154,45 @@ typedef struct libxl__ev_watch_slot {
     
 libxl__ev_xswatch *libxl__watch_slot_contents(libxl__gc *gc, int slotnum);
 
+
+/*
+ * evgen structures, which are the state we use for generating
+ * events for the caller.
+ *
+ * In general in each case there's an internal and an external
+ * version of the _evdisable_FOO function; the internal one is
+ * used during cleanup.
+ */
+
+struct libxl__evgen_domain_death {
+    uint32_t domid;
+    unsigned shutdown_reported:1, death_reported:1;
+    LIBXL_TAILQ_ENTRY(libxl_evgen_domain_death) entry;
+        /* on list .death_reported ? CTX->death_list : CTX->death_reported */
+    libxl_ev_user user;
+};
+_hidden void
+libxl__evdisable_domain_death(libxl__gc*, libxl_evgen_domain_death*);
+
+struct libxl__evgen_disk_eject {
+    libxl__ev_xswatch watch;
+    uint32_t domid;
+    LIBXL_LIST_ENTRY(libxl_evgen_disk_eject) entry;
+    libxl_ev_user user;
+    char *vdev;
+};
+_hidden void
+libxl__evdisable_disk_eject(libxl__gc*, libxl_evgen_disk_eject*);
+
+
 struct libxl__ctx {
     xentoollog_logger *lg;
     xc_interface *xch;
     struct xs_handle *xsh;
 
+    const libxl_event_hooks *event_hooks;
+    void *event_hooks_user;
+
     pthread_mutex_t lock; /* protects data structures hanging off the ctx */
       /* Always use CTX_LOCK and CTX_UNLOCK to manipulate this.
        *
@@ -171,12 +205,17 @@ struct libxl__ctx {
        * documented in the libxl public interface.
        */
 
+    LIBXL_TAILQ_HEAD(libxl__event_list, libxl_event) occurred;
+
     int osevent_in_hook;
     const libxl_osevent_hooks *osevent_hooks;
     void *osevent_user;
       /* See the comment for OSEVENT_HOOK_INTERN in libxl_event.c
        * for restrictions on the use of the osevent fields. */
 
+    struct pollfd *fd_polls;
+    int fd_polls_allocd;
+
     int fd_beforepolled_allocd, fd_beforepolled_used;
     libxl__ev_fd **fd_beforepolled; /* see libxl_osevent_beforepoll */
     LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
@@ -188,6 +227,13 @@ struct libxl__ctx {
     uint32_t watch_counter; /* helps disambiguate slot reuse */
     libxl__ev_fd watch_efd;
 
+    LIBXL_TAILQ_HEAD(libxl__evgen_domain_death_list, libxl_evgen_domain_death)
+        death_list /* sorted by domid */,
+        death_reported;
+    libxl__ev_xswatch death_watch;
+    
+    LIBXL_LIST_HEAD(, libxl_evgen_disk_eject) disk_eject_evgens;
+
     /* for callers who reap children willy-nilly; caller must only
      * set this after libxl_init and before any other call - or
      * may leave them untouched */
@@ -223,12 +269,14 @@ struct libxl__gc {
     int alloc_maxsize;
     void **alloc_ptrs;
     libxl_ctx *owner;
+    struct libxl__event_list occurred_for_callback;
 };
 
-#define LIBXL_INIT_GC(gc,ctx) do{               \
-        (gc).alloc_maxsize = 0;                 \
-        (gc).alloc_ptrs = 0;                    \
-        (gc).owner = (ctx);                     \
+#define LIBXL_INIT_GC(gc,ctx) do{                       \
+        (gc).alloc_maxsize = 0;                         \
+        (gc).alloc_ptrs = 0;                            \
+        (gc).owner = (ctx);                             \
+        LIBXL_TAILQ_INIT(&(gc).occurred_for_callback);  \
     } while(0)
 
 static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
@@ -250,7 +298,9 @@ static inline libxl_ctx *libxl__gc_owner(libxl__gc *gc)
  */
 /* register @ptr in @gc for free on exit from outermost libxl callframe. */
 _hidden int libxl__ptr_add(libxl__gc *gc, void *ptr);
-/* if this is the outermost libxl callframe then free all pointers in @gc */
+/* if this is the outermost libxl callframe then free all pointers in @gc
+ *  and report all occurred events via callback, if applicable.
+ * May reenters the application so must not be called with ctx locked. */
 _hidden void libxl__gc_cleanup(libxl__gc *gc);
 /* allocate and zero @bytes. (similar to a gc'd malloc(3)+memzero()) */
 _hidden void *libxl__zalloc(libxl__gc *gc, int bytes);
@@ -360,6 +410,9 @@ _hidden char *libxl__xs_libxl_path(libxl__gc *gc, uint32_t 
domid);
  *
  * Callers of libxl__ev_KIND_register must ensure that the
  * registration is undone, with _deregister, in libxl_ctx_free.
+ * This means that normally each kind of libxl__evgen (ie each
+ * application-requested event source) needs to be on a list so that
+ * it can be automatically deregistered as promised in libxl_event.h.
  */
 
 
@@ -403,6 +456,25 @@ static inline int libxl__ev_xswatch_isregistered(const 
libxl__ev_xswatch *xw)
 
 
 
+/*
+ * Other event-handling support provided by the libxl event core to
+ * the rest of libxl.
+ */
+
+_hidden void libxl__event_occurred(libxl__gc*, libxl_event *event);
+  /* Arranges to notify the application that the event has occurred.
+   * event should be suitable for passing to libxl_event_free. */
+
+_hidden libxl_event *libxl__event_new(libxl__gc*, libxl_event_type,
+                                      uint32_t domid);
+  /* Convenience function.
+   * Allocates a new libxl_event, fills in domid and type.
+   * If allocation fails, calls _disaster, and returns NULL. */
+
+#define NEW_EVENT(gc, type, domid)                              \
+    libxl__event_new((gc), LIBXL_EVENT_TYPE_##type, (domid));
+    /* Convenience macro. */
+
 _hidden void libxl__event_disaster(libxl__gc*, const char *msg, int errnoval,
                                    libxl_event_type type /* may be 0 */,
                                    const char *file, int line,
@@ -415,6 +487,9 @@ _hidden void libxl__event_disaster(libxl__gc*, const char 
*msg, int errnoval,
    * libxl__ev_FOO_callback or an application event), but are
    * prevented from doing so due to eg lack of memory.
    *
+   * See the "disaster" member of libxl_event_hooks and associated
+   * comment in libxl_event.h.
+   *
    * NB that this function may return and the caller isn't supposed to
    * then crash, although it may fail (and henceforth leave things in
    * a state where many or all calls fail).
@@ -895,7 +970,7 @@ libxl__device_model_version_running(libxl__gc *gc, uint32_t 
domid);
  */
 
 #define GC_INIT(ctx)  libxl__gc gc[1]; LIBXL_INIT_GC(gc[0],ctx)
-#define GC_FREE       libxl__gc_cleanup(gc)
+#define GC_FREE       libxl__gc_cleanup(gc) /* MUST NOT CALL WITH CTX LOCKED */
 #define CTX           libxl__gc_owner(gc)
 
 
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index d59d2cb..5a713f0 100644
--- a/tools/libxl/libxl_types.idl
+++ b/tools/libxl/libxl_types.idl
@@ -75,11 +75,6 @@ libxl_action_on_shutdown = Enumeration("action_on_shutdown", 
[
     (6, "COREDUMP_RESTART"),
     ])
 
-libxl_event_type = Enumeration("event_type", [
-    (1, "DOMAIN_DEATH"),
-    (2, "DISK_EJECT"),
-    ])
-
 libxl_button = Enumeration("button", [
     (1, "POWER"),
     (2, "SLEEP"),
@@ -381,3 +376,33 @@ libxl_sched_credit = Struct("sched_credit", [
     ("weight", integer),
     ("cap", integer),
     ], dispose_fn=None)
+
+libxl_event_type = Enumeration("event_type", [
+    (1, "DOMAIN_SHUTDOWN"),
+    (2, "DOMAIN_DESTROY"),
+    (3, "DISK_EJECT"),
+    ])
+
+libxl_ev_user = Number("libxl_ev_user")
+
+libxl_ev_link = Builtin("ev_link", passby=PASS_BY_REFERENCE, private=True)
+
+libxl_event = Struct("event",[
+    ("link",     libxl_ev_link,0,
+     "for use by libxl; caller may use this once the event has been"
+     " returned by libxl_event_{check,wait}"),
+    ("domid",    libxl_domid),
+    ("domuuid",  libxl_uuid),
+    ("for_user", libxl_ev_user),
+    ("type",     libxl_event_type),
+    ("u", KeyedUnion(None, libxl_event_type, "type",
+          [("domain_shutdown", Struct(None, [
+                                             ("shutdown_reason", uint8),
+                                      ])),
+           ("domain_destroy", Struct(None, [])),
+           ("disk_eject", Struct(None, [
+                                        ("vdev", string),
+                                        ("disk", libxl_device_disk),
+                                 ])),
+           ]))])
+
diff --git a/tools/libxl/xl_cmdimpl.c b/tools/libxl/xl_cmdimpl.c
index f1e729c..e5738b7 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1221,14 +1221,16 @@ skip_vfb:
     xlu_cfg_destroy(config);
 }
 
-/* Returns 1 if domain should be restarted, 2 if domain should be renamed then 
restarted  */
-static int handle_domain_death(libxl_ctx *ctx, uint32_t domid, libxl_event 
*event,
-                               libxl_domain_config *d_config, libxl_dominfo 
*info)
+/* Returns 1 if domain should be restarted,
+ * 2 if domain should be renamed then restarted, or 0 */
+static int handle_domain_death(libxl_ctx *ctx, uint32_t domid,
+                               libxl_event *event,
+                               libxl_domain_config *d_config)
 {
     int restart = 0;
     libxl_action_on_shutdown action;
 
-    switch (info->shutdown_reason) {
+    switch (event->u.domain_shutdown.shutdown_reason) {
     case SHUTDOWN_poweroff:
         action = d_config->on_poweroff;
         break;
@@ -1245,11 +1247,14 @@ static int handle_domain_death(libxl_ctx *ctx, uint32_t 
domid, libxl_event *even
         action = d_config->on_watchdog;
         break;
     default:
-        LOG("Unknown shutdown reason code %d. Destroying domain.", 
info->shutdown_reason);
+        LOG("Unknown shutdown reason code %d. Destroying domain.",
+            event->u.domain_shutdown.shutdown_reason);
         action = LIBXL_ACTION_ON_SHUTDOWN_DESTROY;
     }
 
-    LOG("Action for shutdown reason code %d is %s", info->shutdown_reason, 
action_on_shutdown_names[action]);
+    LOG("Action for shutdown reason code %d is %s",
+        event->u.domain_shutdown.shutdown_reason,
+        action_on_shutdown_names[action]);
 
     if (action == LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_DESTROY || action == 
LIBXL_ACTION_ON_SHUTDOWN_COREDUMP_RESTART) {
         char *corefile;
@@ -1314,7 +1319,7 @@ static void replace_string(char **str, const char *val)
 
 
 static int preserve_domain(libxl_ctx *ctx, uint32_t domid, libxl_event *event,
-                           libxl_domain_config *d_config, libxl_dominfo *info)
+                           libxl_domain_config *d_config)
 {
     time_t now;
     struct tm tm;
@@ -1426,6 +1431,27 @@ static int autoconnect_console(libxl_ctx *ctx, uint32_t 
domid, void *priv)
     _exit(1);
 }
 
+static int domain_wait_event(libxl_event **event_r) {
+    int ret;
+    for (;;) {
+        ret = libxl_event_wait(ctx, event_r, LIBXL_EVENTMASK_ALL, 0,0);
+        if (ret) {
+            LOG("Domain %d, failed to get event, quitting (rc=%d)", domid, 
ret);
+            return ret;
+        }
+        if ((*event_r)->domid != domid) {
+            char *evstr = libxl_event_to_json(ctx, *event_r);
+            LOG("INTERNAL PROBLEM - ignoring unexpected event for"
+                " domain %d (expected %d): event=%s",
+                (*event_r)->domid, domid, evstr);
+            free(evstr);
+            libxl_event_free(ctx, *event_r);
+            continue;
+        }
+        return ret;
+    }
+}
+
 static int create_domain(struct domain_create *dom_info)
 {
     libxl_domain_config d_config;
@@ -1439,10 +1465,11 @@ static int create_domain(struct domain_create *dom_info)
     const char *restore_file = dom_info->restore_file;
     int migrate_fd = dom_info->migrate_fd;
 
-    int fd, i;
+    int i;
     int need_daemon = daemonize;
     int ret, rc;
-    libxl_waiter *w1 = NULL, *w2 = NULL;
+    libxl_evgen_domain_death *deathw = NULL;
+    libxl_evgen_disk_eject **diskws = NULL; /* one per disk */
     void *config_data = 0;
     int config_len = 0;
     int restore_fd = -1;
@@ -1647,14 +1674,14 @@ start:
                 if (errno != EINTR) {
                     perror("failed to wait for daemonizing child");
                     ret = ERROR_FAIL;
-                    goto error_out;
+                    goto out;
                 }
             }
             if (status) {
                 libxl_report_child_exitstatus(ctx, XTL_ERROR,
                            "daemonizing child", child1, status);
                 ret = ERROR_FAIL;
-                goto error_out;
+                goto out;
             }
             ret = domid;
             goto out;
@@ -1691,92 +1718,106 @@ start:
     }
     LOG("Waiting for domain %s (domid %d) to die [pid %ld]",
         d_config.c_info.name, domid, (long)getpid());
-    w1 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter) * d_config.num_disks);
-    w2 = (libxl_waiter*) xmalloc(sizeof(libxl_waiter));
-    libxl_wait_for_disk_ejects(ctx, domid, d_config.disks, d_config.num_disks, 
w1);
-    libxl_wait_for_domain_death(ctx, domid, w2);
-    libxl_get_wait_fd(ctx, &fd);
-    while (1) {
-        int ret;
-        fd_set rfds;
-        libxl_dominfo info;
-        libxl_event event;
-        libxl_device_disk disk;
 
-        FD_ZERO(&rfds);
-        FD_SET(fd, &rfds);
+    ret = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
+    if (ret) goto out;
 
-        ret = select(fd + 1, &rfds, NULL, NULL, NULL);
-        if (!ret)
-            continue;
-        libxl_get_event(ctx, &event);
-        switch (event.type) {
-            case LIBXL_EVENT_TYPE_DOMAIN_DEATH:
-                ret = libxl_event_get_domain_death_info(ctx, domid, &event, 
&info);
-
-                if (ret < 0) {
-                    libxl_free_event(&event);
-                    continue;
+    if (!diskws) {
+        diskws = xmalloc(sizeof(*diskws) * d_config.num_disks);
+        for (i = 0; i < d_config.num_disks; i++)
+            diskws[i] = NULL;
+    }
+    for (i = 0; i < d_config.num_disks; i++) {
+        ret = libxl_evenable_disk_eject(ctx, domid, d_config.disks[i].vdev,
+                                        0, &diskws[i]);
+        if (ret) goto out;
+    }
+    while (1) {
+        libxl_event *event;
+        ret = domain_wait_event(&event);
+        if (ret) goto out;
+
+        switch (event->type) {
+
+        case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
+            LOG("Domain %d has shut down, reason code %d 0x%x", domid,
+                event->u.domain_shutdown.shutdown_reason,
+                event->u.domain_shutdown.shutdown_reason);
+            switch (handle_domain_death(ctx, domid, event, &d_config)) {
+            case 2:
+                if (!preserve_domain(ctx, domid, event, &d_config)) {
+                    /* If we fail then exit leaving the old domain in place. */
+                    ret = -1;
+                    goto out;
                 }
 
-                LOG("Domain %d is dead", domid);
-
-                if (ret) {
-                    switch (handle_domain_death(ctx, domid, &event, &d_config, 
&info)) {
-                    case 2:
-                        if (!preserve_domain(ctx, domid, &event, &d_config, 
&info)) {
-                            /* If we fail then exit leaving the old domain in 
place. */
-                            ret = -1;
-                            goto out;
-                        }
-
-                        /* Otherwise fall through and restart. */
-                    case 1:
-
-                        for (i = 0; i < d_config.num_disks; i++)
-                            libxl_free_waiter(&w1[i]);
-                        libxl_free_waiter(w2);
-                        free(w1);
-                        free(w2);
-
-                        /*
-                         * Do not attempt to reconnect if we come round again 
due to a
-                         * guest reboot -- the stdin/out will be disconnected 
by then.
-                         */
-                        dom_info->console_autoconnect = 0;
-
-                        /* Some settings only make sense on first boot. */
-                        paused = 0;
-                        if (common_domname
-                            && strcmp(d_config.c_info.name, common_domname)) {
-                            d_config.c_info.name = strdup(common_domname);
-                        }
-
-                        /*
-                         * XXX FIXME: If this sleep is not there then domain
-                         * re-creation fails sometimes.
-                         */
-                        LOG("Done. Rebooting now");
-                        sleep(2);
-                        goto start;
-                    case 0:
-                        LOG("Done. Exiting now");
-                        ret = 0;
-                        goto out;
-                    }
-                } else {
-                    LOG("Unable to get domain death info, quitting");
-                    goto out;
+                /* Otherwise fall through and restart. */
+            case 1:
+                libxl_event_free(ctx, event);
+                libxl_evdisable_domain_death(ctx, deathw);
+                deathw = NULL;
+                for (i = 0; i < d_config.num_disks; i++) {
+                    libxl_evdisable_disk_eject(ctx, diskws[i]);
+                    diskws[i] = NULL;
                 }
-                break;
-            case LIBXL_EVENT_TYPE_DISK_EJECT:
-                if (libxl_event_get_disk_eject_info(ctx, domid, &event, 
&disk)) {
-                    libxl_cdrom_insert(ctx, domid, &disk);
-                    libxl_device_disk_dispose(&disk);
+                /* discard any other events which may have been generated */
+                while (!(ret = libxl_event_check(ctx, &event,
+                                                 LIBXL_EVENTMASK_ALL, 0,0))) {
+                    libxl_event_free(ctx, event);
                 }
-                break;
+                if (ret != ERROR_NOT_READY) {
+                    LOG("warning, libxl_event_check (cleanup) failed (rc=%d)",
+                        ret);
+                }
+
+                /*
+                 * Do not attempt to reconnect if we come round again due to a
+                 * guest reboot -- the stdin/out will be disconnected by then.
+                 */
+                dom_info->console_autoconnect = 0;
+
+                /* Some settings only make sense on first boot. */
+                paused = 0;
+                if (common_domname
+                    && strcmp(d_config.c_info.name, common_domname)) {
+                    d_config.c_info.name = strdup(common_domname);
+                }
+
+                /*
+                 * XXX FIXME: If this sleep is not there then domain
+                 * re-creation fails sometimes.
+                 */
+                LOG("Done. Rebooting now");
+                sleep(2);
+                goto start;
+
+            case 0:
+                LOG("Done. Exiting now");
+                ret = 0;
+                goto out;
+
+            default:
+                abort();
+            }
+
+        case LIBXL_EVENT_TYPE_DOMAIN_DESTROY:
+            LOG("Domain %d has been destroyed.", domid);
+            ret = 0;
+            goto out;
+
+        case LIBXL_EVENT_TYPE_DISK_EJECT:
+            /* XXX what is this for? */
+            libxl_cdrom_insert(ctx, domid, &event->u.disk_eject.disk);
+            break;
+
+        default:;
+            char *evstr = libxl_event_to_json(ctx, event);
+            LOG("warning, got unexpected event type %d, event=%s",
+                event->type, evstr);
+            free(evstr);
         }
-        libxl_free_event(&event);
+
+        libxl_event_free(ctx, event);
     }
 
 error_out:
@@ -2259,43 +2300,46 @@ static void destroy_domain(const char *p)
 static void shutdown_domain(const char *p, int wait)
 {
     int rc;
+    libxl_event *event;
 
     find_domain(p);
     rc=libxl_domain_shutdown(ctx, domid, 0);
     if (rc) { fprintf(stderr,"shutdown failed (rc=%d)\n",rc);exit(-1); }
 
     if (wait) {
-        libxl_waiter waiter;
-        int fd;
-
-        libxl_wait_for_domain_death(ctx, domid, &waiter);
+        libxl_evgen_domain_death *deathw;
 
-        libxl_get_wait_fd(ctx, &fd);
-
-        while (wait) {
-            fd_set rfds;
-            libxl_event event;
-            libxl_dominfo info;
+        rc = libxl_evenable_domain_death(ctx, domid, 0, &deathw);
+        if (rc) {
+            fprintf(stderr,"wait for death failed (evgen, rc=%d)\n",rc);
+            exit(-1);
+        }
 
-            FD_ZERO(&rfds);
-            FD_SET(fd, &rfds);
+        for (;;) {
+            rc = domain_wait_event(&event);
+            if (rc) exit(-1);
 
-            if (!select(fd + 1, &rfds, NULL, NULL, NULL))
-                continue;
+            switch (event->type) {
 
-            libxl_get_event(ctx, &event);
+            case LIBXL_EVENT_TYPE_DOMAIN_DESTROY:
+                LOG("Domain %d has been destroyed", domid);
+                goto done;
 
-            if (event.type == LIBXL_EVENT_TYPE_DOMAIN_DEATH) {
-                if (libxl_event_get_domain_death_info(ctx, domid, &event, 
&info) < 0)
-                    continue;
+            case LIBXL_EVENT_TYPE_DOMAIN_SHUTDOWN:
+                LOG("Domain %d has been shut down, reason code %d %x", domid,
+                    event->u.domain_shutdown.shutdown_reason,
+                    event->u.domain_shutdown.shutdown_reason);
+                goto done;
 
-                LOG("Domain %d is dead", domid);
-                wait = 0;
+            default:
+                LOG("Unexpected event type %d", event->type);
+                break;
             }
-
-            libxl_free_event(&event);
+            libxl_event_free(ctx, event);
         }
-        libxl_free_waiter(&waiter);
+    done:
+        libxl_event_free(ctx, event);
+        libxl_evdisable_domain_death(ctx, deathw);
     }
 }
 
-- 
1.7.2.5


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.