@@ -87,6 +87,8 @@ int libxl_ctx_alloc(libxl_ctx **pctx, int version,
ctx->sigchld_selfpipe[1] = -1;
libxl__ev_fd_init(&ctx->sigchld_selfpipe_efd);
+ LIBXL_LIST_INIT(&ctx->dm_support_cache);
+
/* The mutex is special because we can't idempotently destroy it */
if (libxl__init_recursive_mutex(ctx, &ctx->lock) < 0) {
@@ -208,6 +210,8 @@ int libxl_ctx_free(libxl_ctx *ctx)
libxl__sigchld_notneeded(gc);
libxl__pipe_close(ctx->sigchld_selfpipe);
+ libxl__dm_support_cache_destroy(gc);
+
CTX_UNLOCK;
pthread_mutex_destroy(&ctx->lock);
@@ -2169,6 +2169,237 @@ out:
return ret;
}
+/*----- libxl__dm_support_* -----*/
+
+/* cache */
+
+static libxl__dm_support_cached *
+dm_support_cache_lookup(libxl__gc *gc, const char *dm)
+{
+ libxl__dm_support_cached *search;
+
+ LIBXL_LIST_FOREACH(search, &CTX->dm_support_cache, entry)
+ if (!strcmp(search->dm, dm))
+ return search;
+
+ return 0;
+}
+
+_hidden void libxl__dm_support_cache_destroy(libxl__gc *gc)
+{
+ libxl__dm_support_cached *iter, *next;
+
+ LIBXL_LIST_FOREACH_SAFE(iter, &CTX->dm_support_cache, entry, next) {
+ free(iter->dm);
+ free(iter);
+ }
+}
+
+_hidden bool libxl__dm_supported(libxl__gc *gc, const char *dm,
+ libxl__dm_support_check__index opt)
+{
+ libxl__dm_support_cached *cached = dm_support_cache_lookup(gc, dm);
+ assert(cached);
+ return !!(cached->bitmap & (1UL << opt));
+}
+
+/* `check', the long-running operation to check for support */
+
+static void dm_support_check_immed_cb(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc);
+static void dm_support_check_child_cb(libxl__egc *egc, libxl__ev_child*,
+ pid_t pid, int status);
+static void dm_support_check_done(libxl__egc *gc,
+ libxl__dm_support_check_state *ch,
+ int rc);
+
+_hidden void libxl__dm_support_check_init(libxl__dm_support_check_state *ch)
+{
+ FILLZERO(*ch);
+ libxl__ev_child_init(&ch->child);
+ libxl__ev_time_init(&ch->immediate);
+}
+
+static void dm_support_check_free(libxl__gc *gc,
+ libxl__dm_support_check_state *ch)
+{
+ if (ch->helpmsg) fclose(ch->helpmsg);
+ assert(!libxl__ev_child_inuse(&ch->child));
+ libxl__ev_time_deregister(gc, &ch->immediate);
+}
+
+_hidden int libxl__dm_support_check_start(libxl__dm_support_check_state *ch)
+{
+ int r, rc;
+
+ /* convenience aliases */
+ const char *const dm = ch->dm;
+ libxl__ao *const ao = ch->ao;
+ AO_GC;
+
+ libxl__dm_support_check_init(ch);
+
+ r = stat(ch->dm, &ch->stab);
+ if (r<0) {
+ LOGE(ERROR, "device model %s is not accessible", dm);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ libxl__dm_support_cached *cached = dm_support_cache_lookup(gc, dm);
+ if (cached &&
+ (ch->stab.st_mode == cached->stab.st_mode &&
+ ch->stab.st_dev == cached->stab.st_dev &&
+ ch->stab.st_ino == cached->stab.st_ino &&
+ ch->stab.st_mtime == cached->stab.st_mtime &&
+ ch->stab.st_ctime == cached->stab.st_ctime)) {
+
+ LOG(DEBUG, "returning cached support options for %s: %"PRIx32"",
+ dm, cached->bitmap);
+
+ rc = libxl__ev_time_register_rel(ao, &ch->immediate,
+ dm_support_check_immed_cb, 0);
+ if (rc) goto out;
+
+ /* we have queued a callback, our work in this function is done */
+ return 0;
+ }
+
+ ch->revalidate = cached;
+ if (cached) {
+ LOG(DEBUG, "revalidating cached support options for %s", dm);
+ }
+
+ ch->helpmsg = tmpfile();
+ if (!ch->helpmsg) {
+ LOGE(ERROR, "create tempfile for help message");
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ pid_t pid = libxl__ev_child_fork(gc, &ch->child,
+ dm_support_check_child_cb);
+ if (pid < 0) {
+ LOGE(ERROR, "fork for %s -help", dm);
+ rc = pid;
+ goto out;
+ }
+
+ if (!pid) {
+ const char *const args[] = { dm, "--help", NULL };
+ int outfd = fileno(ch->helpmsg);
+ r = putenv("LC_ALL=C");
+ if (r) libxl__alloc_failed(CTX,__func__,1,0);
+ libxl__exec(gc, -1,outfd,-1, dm, (char**)args, NULL);
+ }
+ return 0;
+
+ out:
+ dm_support_check_free(gc,ch);
+ return rc;
+}
+
+static void dm_support_check_immed_cb(libxl__egc *egc, libxl__ev_time *ev,
+ const struct timeval *requested_abs,
+ int rc)
+{
+ libxl__dm_support_check_state *ch = CONTAINER_OF(ev, *ch, immediate);
+ dm_support_check_done(egc, ch, rc);
+}
+
+static void dm_support_check_lookfor(uint32_t *result_io,
+ const void *data, int datalen,
+ const char *str, int strlen,
+ int opt)
+{
+ if (memmem(data,datalen, str,strlen))
+ *result_io |= (1UL << opt);
+}
+
+void dm_support_check_child_cb(libxl__egc *egc, libxl__ev_child *child,
+ pid_t pid, int status)
+{
+ libxl__dm_support_check_state *ch = CONTAINER_OF(child, *ch, child);
+ STATE_AO_GC(ch->ao);
+ int rc;
+
+ /* convenience aliases */
+ const char *const dm = ch->dm;
+
+ if (status) {
+ libxl_report_child_exitstatus(CTX, XTL_ERROR,
+ GCSPRINTF("%s -HELP", dm),
+ pid, status);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+
+ rewind(ch->helpmsg);
+
+ void *data;
+ int datalen;
+ rc = libxl_read_file_contents(CTX, "(temp file for dm help output)",
+ &data, &datalen);
+ if (rc) goto out;
+ libxl__ptr_add(gc, data);
+
+ if (!datalen) {
+ LOG(ERROR, "dm help output (from %s) was empty!", dm);
+ rc = ERROR_FAIL;
+ goto out;
+ }
+ ((char*)data)[datalen-1] = 0; /* a null-terminated string new */
+
+ uint32_t result = 0;
+
+#define DM_SUPPORT_CHECK_ENTRY(name, string) \
+ dm_support_check_lookfor(&result, \
+ data, datalen, \
+ string, sizeof(string)-1, \
+ libxl__dm_support_check__##name);
+ DM_SUPPORT_CHECK_LIST
+#undef DM_SUPPORT_CHECK_ENTRY
+
+ libxl__dm_support_cached *cached = ch->revalidate;
+ if (cached) {
+ if (cached->bitmap == result)
+ LOG(DEBUG, "confirmed cached support options for %s: %"PRIx32"",
+ dm, result);
+ else
+ LOG(DEBUG, "updating cached support options for %s:"
+ " %"PRIx32" -> %"PRIx32"", dm, cached->bitmap, result);
+ }
+ if (!cached) {
+ LOG(DEBUG, "multiple in-parallel checks for %s", dm);
+ cached = dm_support_cache_lookup(gc, dm);
+ /* if already there, take the one which finished last, willy-nilly */
+ }
+ if (!cached) {
+ LOG(DEBUG, "caching support options for %s: %"PRIx32"", dm, result);
+ cached = libxl__zalloc(NOGC, sizeof(*cached));
+ cached->dm = libxl__strdup(NOGC, dm);
+ LIBXL_LIST_INSERT_HEAD(&CTX->dm_support_cache, cached, entry);
+ }
+
+ cached->stab = ch->stab;
+ cached->bitmap = result;
+
+ rc = 0;
+
+ out:
+ dm_support_check_done(egc, ch, rc);
+}
+
+static void dm_support_check_done(libxl__egc *egc,
+ libxl__dm_support_check_state *ch,
+ int rc)
+{
+ STATE_AO_GC(ch->ao);
+ dm_support_check_free(gc, ch);
+ ch->callback(egc, ch, rc);
+}
+
/*
* Local variables:
* mode: C
@@ -181,6 +181,7 @@ typedef struct libxl__ao libxl__ao;
typedef struct libxl__aop_occurred libxl__aop_occurred;
typedef struct libxl__osevent_hook_nexus libxl__osevent_hook_nexus;
typedef struct libxl__osevent_hook_nexi libxl__osevent_hook_nexi;
+typedef struct libxl__dm_support_cached libxl__dm_support_cached;
_hidden void libxl__alloc_failed(libxl_ctx *, const char *func,
size_t nmemb, size_t size) __attribute__((noreturn));
@@ -398,6 +399,13 @@ struct libxl__poller {
bool fds_changed;
};
+struct libxl__dm_support_cached {
+ LIBXL_LIST_ENTRY(libxl__dm_support_cached) entry;
+ char *dm;
+ struct stat stab;
+ uint32_t bitmap;
+};
+
struct libxl__gc {
/* mini-GC */
int alloc_maxsize; /* -1 means this is the dummy non-gc gc */
@@ -471,6 +479,8 @@ struct libxl__ctx {
bool sigchld_user_registered;
LIBXL_LIST_ENTRY(libxl_ctx) sigchld_users_entry;
+ LIBXL_LIST_HEAD(, libxl__dm_support_cached) dm_support_cache;
+
libxl_version_info version_info;
};
@@ -3198,6 +3208,72 @@ _hidden void libxl__bootloader_init(libxl__bootloader_state *bl);
* If callback is passed rc==0, will have updated st->info appropriately */
_hidden void libxl__bootloader_run(libxl__egc*, libxl__bootloader_state *st);
+/*----- Device model support check -----*/
+
+/*
+ * This can be used to determine whether a particular qemu supports
+ * a particular option. (Strictly, whether a particular literal
+ * string appears in the help text.)
+ *
+ * Callers should:
+ * 1. Some time it is convenient:
+ * Set up a libxl__dm_support_check_state
+ * Invoke libxl__dm_support_check_start
+ * Await the callback and bomb if it fails
+ * 2. Any time after that, perhaps repeatedly
+ * Call libxl__dm_supported
+ */
+
+typedef struct libxl__dm_support_check_state libxl__dm_support_check_state;
+
+#define DM_SUPPORT_CHECK_LIST \
+ DM_SUPPORT_CHECK_ENTRY(emulator_id, "emulator_id") \
+ DM_SUPPORT_CHECK_ENTRY(xsrestrict, "xsrestrict")
+
+typedef enum {
+#define DM_SUPPORT_CHECK_ENTRY(name, string) \
+ libxl__dm_support_check__##name,
+DM_SUPPORT_CHECK_LIST
+#undef DM_SUPPORT_CHECK_ENTRY
+ libxl__dm_support_check__max
+} libxl__dm_support_check__index;
+
+typedef struct libxl__dm_support_check_state
+ libxl__dm_support_check_state;
+
+/* Cannot be called reentrantly */
+typedef void libxl__dm_support_check_callback(libxl__egc *egc,
+ libxl__dm_support_check_state *checking, int rc);
+
+_hidden void libxl__dm_support_cache_destroy(libxl__gc *gc);
+
+struct libxl__dm_support_check_state {
+ /* caller must fill these in, and they must all remain valid */
+ libxl__ao *ao;
+ const char *dm;
+ libxl__dm_support_check_callback *callback;
+ /* private */
+ FILE *helpmsg;
+ struct stat stab;
+ libxl__ev_child child;
+ libxl__ev_time immediate;
+ libxl__dm_support_cached *revalidate; /* points into dm_support_cache */
+};
+
+_hidden void libxl__dm_support_check_init(
+ libxl__dm_support_check_state *checking);
+
+_hidden int libxl__dm_support_check_start(
+ libxl__dm_support_check_state *checking);
+
+/*
+ * Eg
+ * libxl__dm_supported(gc, my_dm, libxl__dm_support_check__xsrestrict);
+ * (opt is from DM_SUPPORT_CHECK_LIST, above).
+ */
+_hidden bool libxl__dm_supported(libxl__gc *gc,
+ const char *dm, libxl__dm_support_check__index opt);
+
/*----- Domain destruction -----*/
/* Domain destruction has been split into two functions: