diff options
-rw-r--r-- | tools/libxl/libxl.c | 328 | ||||
-rw-r--r-- | tools/libxl/libxl.h | 55 | ||||
-rw-r--r-- | tools/libxl/libxl_event.c | 236 | ||||
-rw-r--r-- | tools/libxl/libxl_event.h | 183 | ||||
-rw-r--r-- | tools/libxl/libxl_internal.h | 77 | ||||
-rw-r--r-- | tools/libxl/libxl_types.idl | 34 | ||||
-rw-r--r-- | tools/libxl/xl_cmdimpl.c | 289 | ||||
-rw-r--r-- | tools/ocaml/libs/xl/genwrap.py | 1 |
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: |