aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tools/libxl/libxl.c328
-rw-r--r--tools/libxl/libxl.h55
-rw-r--r--tools/libxl/libxl_event.c236
-rw-r--r--tools/libxl/libxl_event.h183
-rw-r--r--tools/libxl/libxl_internal.h77
-rw-r--r--tools/libxl/libxl_types.idl34
-rw-r--r--tools/libxl/xl_cmdimpl.c289
-rw-r--r--tools/ocaml/libs/xl/genwrap.py1
8 files changed, 927 insertions, 276 deletions
diff --git a/tools/libxl/libxl.c b/tools/libxl/libxl.c
index 3e492f09e8..5c80751468 100644
--- a/tools/libxl/libxl.c
+++ b/tools/libxl/libxl.c
@@ -45,8 +45,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_rindex = 0;
LIBXL_LIST_INIT(&ctx->efds);
LIBXL_TAILQ_INIT(&ctx->etimes);
@@ -55,6 +58,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);
@@ -86,6 +92,20 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
return rc;
}
+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;
@@ -95,6 +115,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);
@@ -108,9 +135,12 @@ 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_rindex);
free(ctx->watch_slots);
+ discard_events(&ctx->occurred);
+
GC_FREE;
free(ctx);
return 0;
@@ -646,117 +676,177 @@ int libxl_domain_reboot(libxl_ctx *ctx, uint32_t domid)
return ret;
}
-int libxl_get_wait_fd(libxl_ctx *ctx, int *fd)
-{
- *fd = xs_fileno(ctx->xsh);
- return 0;
-}
+static void domain_death_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
+ const char *wpath, const char *epath) {
+ EGC_GC;
+ 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;
+
+ evg = LIBXL_TAILQ_FIRST(&CTX->death_list);
+ if (!evg) goto out;
+
+ domid = evg->domid;
+
+ 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(egc, "xc_domain_getinfolist failed while"
+ " processing @releaseDomain watch event",
+ errno, 0);
+ goto out;
+ }
+ 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(egc, DOMAIN_DESTROY, evg->domid);
+ if (!ev) goto out;
+
+ libxl__event_occurred(egc, 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(egc, 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(egc, 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;
+ }
+ all_reported:
+ out:
+
+ CTX_UNLOCK;
}
-int libxl_wait_for_disk_ejects(libxl_ctx *ctx, uint32_t guest_domid, libxl_device_disk *disks, int num_disks, libxl_waiter *waiter)
-{
+int libxl_evenable_domain_death(libxl_ctx *ctx, uint32_t domid,
+ libxl_ev_user user, libxl_evgen_domain_death **evgen_out) {
GC_INIT(ctx);
- int i, rc = -1;
- uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
+ libxl_evgen_domain_death *evg, *evg_search;
+ int rc;
+
+ CTX_LOCK;
- if (!domid)
- domid = guest_domid;
+ evg = malloc(sizeof(*evg)); if (!evg) { rc = ERROR_NOMEM; goto out; }
+ memset(evg, 0, sizeof(*evg));
+ evg->domid = domid;
+ evg->user = user;
- 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)
- goto out;
- xs_watch(ctx->xsh, waiter[i].path, waiter[i].token);
+ 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; }
}
+
+ *evgen_out = evg;
rc = 0;
-out:
+
+ out:
+ CTX_UNLOCK;
GC_FREE;
return rc;
-}
+};
-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;
- }
- event->path = strdup(events[XS_WATCH_PATH]);
- event->token = strdup(events[XS_WATCH_TOKEN]);
- event->type = atoi(event->token);
- free(events);
- return 0;
-}
+void libxl__evdisable_domain_death(libxl__gc *gc,
+ libxl_evgen_domain_death *evg) {
+ CTX_LOCK;
-int libxl_stop_waiting(libxl_ctx *ctx, libxl_waiter *waiter)
-{
- if (!xs_unwatch(ctx->xsh, waiter->path, waiter->token))
- return ERROR_FAIL;
+ if (!evg->death_reported)
+ LIBXL_TAILQ_REMOVE(&CTX->death_list, evg, entry);
else
- return 0;
-}
+ LIBXL_TAILQ_REMOVE(&CTX->death_reported, evg, entry);
-int libxl_free_event(libxl_event *event)
-{
- free(event->path);
- free(event->token);
- return 0;
-}
+ free(evg);
-int libxl_free_waiter(libxl_waiter *waiter)
-{
- free(waiter->path);
- free(waiter->token);
- return 0;
-}
-
-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 (!LIBXL_TAILQ_FIRST(&CTX->death_list) &&
+ libxl__ev_xswatch_isregistered(&CTX->death_watch))
+ libxl__ev_xswatch_deregister(gc, &CTX->death_watch);
- if (info->running || (!info->shutdown && !info->dying))
- return ERROR_INVAL;
-
- return 1;
+ 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__egc *egc, libxl__ev_xswatch *w,
+ const char *wpath, const char *epath) {
+ EGC_GC;
+ libxl_evgen_disk_eject *evg = (void*)w;
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(egc, "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(egc, 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")) {
@@ -765,19 +855,83 @@ 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(egc, 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;
+
+ *evgen_out = evg;
+ 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)
{
GC_INIT(ctx);
diff --git a/tools/libxl/libxl.h b/tools/libxl/libxl.h
index b06772449d..4d3391f26b 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"
@@ -300,51 +308,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 ec66340874..69ad318a2d 100644
--- a/tools/libxl/libxl_event.c
+++ b/tools/libxl/libxl_event.c
@@ -510,9 +510,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_internal(libxl__gc *gc, int *nfds_io,
+ struct pollfd *fds, int *timeout_upd,
+ struct timeval now)
{
libxl__ev_fd *efd;
int rc;
@@ -524,9 +524,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_rindex
@@ -593,8 +590,18 @@ int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
}
out:
+ return rc;
+}
+
+int libxl_osevent_beforepoll(libxl_ctx *ctx, int *nfds_io,
+ struct pollfd *fds, int *timeout_upd,
+ struct timeval now)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ int rc = beforepoll_internal(gc, nfds_io, fds, timeout_upd, now);
CTX_UNLOCK;
- GC_FREE;
+ EGC_FREE;
return rc;
}
@@ -623,11 +630,11 @@ static int afterpoll_check_fd(libxl_ctx *ctx,
return revents;
}
-void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
- struct timeval now)
+static void afterpoll_internal(libxl__egc *egc,
+ int nfds, const struct pollfd *fds,
+ struct timeval now)
{
- EGC_INIT(ctx);
- CTX_LOCK;
+ EGC_GC;
libxl__ev_fd *efd;
LIBXL_LIST_FOREACH(efd, &CTX->efds, entry) {
@@ -653,12 +660,18 @@ void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
etime->func(egc, etime, &etime->abs);
}
+}
+void libxl_osevent_afterpoll(libxl_ctx *ctx, int nfds, const struct pollfd *fds,
+ struct timeval now)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ afterpoll_internal(egc, nfds, fds, now);
CTX_UNLOCK;
EGC_FREE;
}
-
/*
* osevent hook and callback machinery
*/
@@ -723,11 +736,10 @@ void libxl__event_disaster(libxl__egc *egc, 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";
@@ -736,9 +748,197 @@ void libxl__event_disaster(libxl__egc *egc, const char *msg, int errnoval,
exit(-1);
}
+static void egc_run_callbacks(libxl__egc *egc)
+{
+ EGC_GC;
+ libxl_event *ev, *ev_tmp;
+ LIBXL_TAILQ_FOREACH_SAFE(ev, &egc->occurred_for_callback, link, ev_tmp) {
+ LIBXL_TAILQ_REMOVE(&egc->occurred_for_callback, ev, link);
+ CTX->event_hooks->event_occurs(CTX->event_hooks_user, ev);
+ }
+}
+
void libxl__egc_cleanup(libxl__egc *egc)
{
- libxl__free_all(&egc->gc);
+ EGC_GC;
+ libxl__free_all(gc);
+
+ egc_run_callbacks(egc);
+}
+
+/*
+ * 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__egc *egc, libxl_event *event)
+{
+ EGC_GC;
+
+ if (CTX->event_hooks &&
+ (CTX->event_hooks->event_occurs_mask & (1UL << event->type))) {
+ /* libxl__egc_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(&egc->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__egc *egc,
+ libxl_event_type type, uint32_t domid)
+{
+ libxl_event *ev;
+
+ ev = malloc(sizeof(*ev));
+ if (!ev) {
+ LIBXL__EVENT_DISASTER(egc, "allocate new event", errno, type);
+ return NULL;
+ }
+
+ memset(ev, 0, sizeof(*ev));
+ ev->type = type;
+ ev->domid = domid;
+
+ return ev;
+}
+
+static int event_check_internal(libxl__egc *egc, libxl_event **event_r,
+ unsigned long typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ EGC_GC;
+ libxl_event *ev;
+ int rc;
+
+ LIBXL_TAILQ_FOREACH(ev, &CTX->occurred, link) {
+ if (!(typemask & ((uint64_t)1 << 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,
+ uint64_t typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ EGC_INIT(ctx);
+ CTX_LOCK;
+ int rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+ CTX_UNLOCK;
+ EGC_FREE;
+ return rc;
+}
+
+static int eventloop_iteration(libxl__egc *egc) {
+ EGC_GC;
+ int rc;
+ struct timeval now;
+
+ CTX_LOCK;
+
+ rc = libxl__gettimeofday(gc, &now);
+ if (rc) goto out;
+
+ int timeout;
+
+ for (;;) {
+ int nfds = CTX->fd_polls_allocd;
+ timeout = -1;
+ rc = beforepoll_internal(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)
+ return 0; /* will go round again if caller requires */
+
+ 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_internal(egc, CTX->fd_polls_allocd, CTX->fd_polls, now);
+
+ CTX_UNLOCK;
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+int libxl_event_wait(libxl_ctx *ctx, libxl_event **event_r,
+ uint64_t typemask,
+ libxl_event_predicate *pred, void *pred_user)
+{
+ int rc;
+
+ EGC_INIT(ctx);
+ CTX_LOCK;
+
+ for (;;) {
+ rc = event_check_internal(egc, event_r, typemask, pred, pred_user);
+ if (rc != ERROR_NOT_READY) goto out;
+
+ rc = eventloop_iteration(egc);
+ if (rc) goto out;
+
+ /* we unlock and cleanup the egc 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__egc_cleanup(egc);
+ CTX_LOCK;
+ }
+
+ out:
+ CTX_UNLOCK;
+ EGC_FREE;
+ return rc;
}
/*
diff --git a/tools/libxl/libxl_event.h b/tools/libxl/libxl_event.h
index 63ef65e2a0..f88911500d 100644
--- a/tools/libxl/libxl_event.h
+++ b/tools/libxl/libxl_event.h
@@ -18,6 +18,181 @@
#include <libxl.h>
+/*======================================================================*/
+
+/*
+ * Domain event handling - getting Xen events from libxl
+ *
+ * (Callers inside libxl may not call libxl_event_check or _wait.)
+ */
+
+#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,
+ uint64_t 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,
+ uint64_t 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. The event_occurs and disaster callbacks may occur on
+ * any thread in which the application calls libxl.
+ *
+ * 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 +211,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;
* }
* }
*
diff --git a/tools/libxl/libxl_internal.h b/tools/libxl/libxl_internal.h
index 26a6147a33..8e80f24a22 100644
--- a/tools/libxl/libxl_internal.h
+++ b/tools/libxl/libxl_internal.h
@@ -177,11 +177,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 libxl__ctx_lock and _unlock (or the convenience
* macors CTX_LOCK and CTX_UNLOCK) to manipulate this.
@@ -195,12 +229,16 @@ 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_rindex_allocd;
int *fd_rindex; /* see libxl_osevent_beforepoll */
LIBXL_LIST_HEAD(, libxl__ev_fd) efds;
@@ -212,6 +250,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 */
@@ -252,6 +297,7 @@ struct libxl__gc {
struct libxl__egc {
/* for event-generating functions only */
struct libxl__gc gc;
+ struct libxl__event_list occurred_for_callback;
};
#define LIBXL_INIT_GC(gc,ctx) do{ \
@@ -396,6 +442,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.
*/
@@ -439,6 +488,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__egc*, 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__egc*, 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(egc, type, domid) \
+ libxl__event_new((egc), LIBXL_EVENT_TYPE_##type, (domid));
+ /* Convenience macro. */
+
+/*
* In general, call this via the macro LIBXL__EVENT_DISASTER.
*
* Event-generating functions may call this if they might have wanted
@@ -995,12 +1063,15 @@ libxl__device_model_version_running(libxl__gc *gc, uint32_t domid);
/* egc initialisation and destruction: */
-#define LIBXL_INIT_EGC(egc,ctx) do{ \
- LIBXL_INIT_GC((egc).gc,ctx); \
- /* list of occurred events tbd */ \
+#define LIBXL_INIT_EGC(egc,ctx) do{ \
+ LIBXL_INIT_GC((egc).gc,ctx); \
+ LIBXL_TAILQ_INIT(&(egc).occurred_for_callback); \
} while(0)
_hidden void libxl__egc_cleanup(libxl__egc *egc);
+ /* Frees memory allocated within this egc's gc, and and report all
+ * occurred events via callback, if applicable. May reenter the
+ * application; see restrictions above. */
/* convenience macros: */
diff --git a/tools/libxl/libxl_types.idl b/tools/libxl/libxl_types.idl
index d69b043820..1a47c5ecf3 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"),
@@ -397,3 +392,32 @@ libxl_sched_sedf = Struct("sched_sedf", [
("extratime", integer),
("weight", integer),
], dispose_fn=None)
+
+libxl_event_type = Enumeration("event_type", [
+ (1, "DOMAIN_SHUTDOWN"),
+ (2, "DOMAIN_DESTROY"),
+ (3, "DISK_EJECT"),
+ ])
+
+libxl_ev_user = UInt(64)
+
+libxl_ev_link = Builtin("ev_link", passby=PASS_BY_REFERENCE, private=True)
+
+libxl_event = Struct("event",[
+ ("link", libxl_ev_link),
+ # 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 5c4dfe3684..b4a9d451b6 100644
--- a/tools/libxl/xl_cmdimpl.c
+++ b/tools/libxl/xl_cmdimpl.c
@@ -1226,14 +1226,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;
@@ -1250,11 +1252,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;
@@ -1319,7 +1324,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;
@@ -1432,6 +1437,40 @@ 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 void evdisable_disk_ejects(libxl_evgen_disk_eject **diskws,
+ int num_disks)
+{
+ int i;
+
+ for (i = 0; i < num_disks; i++) {
+ if (diskws[i])
+ libxl_evdisable_disk_eject(ctx, diskws[i]);
+ diskws[i] = NULL;
+ }
+}
+
static int create_domain(struct domain_create *dom_info)
{
libxl_domain_config d_config;
@@ -1445,10 +1484,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;
@@ -1661,14 +1701,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;
@@ -1705,92 +1745,105 @@ 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++) {
+ if (d_config.disks[i].removable) {
+ 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;
+ evdisable_disk_ejects(diskws, d_config.num_disks);
+ /* 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;
- 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);
+ if (ret != ERROR_NOT_READY) {
+ LOG("warning, libxl_event_check (cleanup) failed (rc=%d)",
+ ret);
}
- break;
+
+ /*
+ * 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:
@@ -1811,6 +1864,13 @@ waitpid_out:
waitpid(child_console_pid, &status, 0) < 0 && errno == EINTR)
goto waitpid_out;
+ if (deathw)
+ libxl_evdisable_domain_death(ctx, deathw);
+ if (diskws) {
+ evdisable_disk_ejects(diskws, d_config.num_disks);
+ free(diskws);
+ }
+
/*
* If we have daemonized then do not return to the caller -- this has
* already happened in the parent.
@@ -2273,6 +2333,7 @@ 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);
@@ -2287,37 +2348,39 @@ static void shutdown_domain(const char *p, int wait)
}
if (wait) {
- libxl_waiter waiter;
- int fd;
-
- libxl_wait_for_domain_death(ctx, domid, &waiter);
-
- libxl_get_wait_fd(ctx, &fd);
+ libxl_evgen_domain_death *deathw;
- 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);
}
}
diff --git a/tools/ocaml/libs/xl/genwrap.py b/tools/ocaml/libs/xl/genwrap.py
index 00583d2267..b46ee10925 100644
--- a/tools/ocaml/libs/xl/genwrap.py
+++ b/tools/ocaml/libs/xl/genwrap.py
@@ -275,6 +275,7 @@ if __name__ == '__main__':
"device_model_info",
"vcpuinfo",
"topologyinfo",
+ "event",
]
for t in blacklist: