diff --git a/tools/libs/light/libxl_dm.c b/tools/libs/light/libxl_dm.c index ff8ddeec9a..46babfed0b 100644 --- a/tools/libs/light/libxl_dm.c +++ b/tools/libs/light/libxl_dm.c @@ -2858,6 +2858,20 @@ static void device_model_qmp_cb(libxl__egc *egc, libxl__ev_qmp *ev, static void device_model_spawn_outcome(libxl__egc *egc, libxl__dm_spawn_state *dmss, int rc); +static void device_model_probe_startup_failed(libxl__egc *egc, + libxl__spawn_state *spawn, int rc); +static void device_model_probe_confirm(libxl__egc *egc, + libxl__spawn_state *spawn, const char *xsdata); +static void device_model_probe_detached(libxl__egc *egc, + libxl__spawn_state *spawn); +static void device_model_probe_cmdline(libxl__egc *egc, + libxl__ev_qmp *qmp, const libxl__json_object *response, int rc); +static void device_model_probe_quit(libxl__egc *egc, + libxl__ev_qmp *qmp, const libxl__json_object *response, int rc); +static void device_model_probe_spawn_outcome(libxl__egc *egc, + libxl__dm_spawn_state *dmss, int rc); +static void device_model_launch(libxl__egc *egc, + libxl__dm_spawn_state *dmss, int rc); static void device_model_postconfig_chardev(libxl__egc *egc, libxl__ev_qmp *qmp, const libxl__json_object *response, int rc); static void device_model_postconfig_vnc(libxl__egc *egc, @@ -2873,25 +2887,18 @@ void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) { /* convenience aliases */ const int domid = dmss->guest_domid; - libxl__domain_build_state *const state = dmss->build_state; libxl__spawn_state *const spawn = &dmss->spawn; STATE_AO_GC(dmss->spawn.ao); - libxl_ctx *ctx = CTX; libxl_domain_config *guest_config = dmss->guest_config; const libxl_domain_create_info *c_info = &guest_config->c_info; const libxl_domain_build_info *b_info = &guest_config->b_info; - const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); - char *path; - int logfile_w, null; int rc; - char **args, **arg, **envs; - xs_transaction_t t; - char *vm_path; - char **pass_stuff; const char *dm; - int dm_state_fd = -1; + int logfile_w = -1, null = -1; + int qmp_probe_fd = -1; + bool probe_spawned = false; dmss_init(dmss); @@ -2904,6 +2911,7 @@ void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) rc = ERROR_FAIL; goto out; } + dmss->dm = dm; if (access(dm, X_OK) < 0) { LOGED(ERROR, domid, "device model %s is not executable", dm); rc = ERROR_FAIL; @@ -2911,6 +2919,185 @@ void libxl__spawn_local_dm(libxl__egc *egc, libxl__dm_spawn_state *dmss) } rc = libxl__domain_get_device_model_uid(gc, dmss); + if (rc) + goto out; + + /* probe QEMU's available command line options */ + if (b_info->device_model_version + == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) { + + logfile_w = libxl__create_qemu_logfile( + gc, GCSPRINTF("qemu-probe-%s", c_info->name)); + if (logfile_w < 0) { + rc = logfile_w; + goto out; + } + null = open("/dev/null", O_RDONLY); + if (null < 0) { + LOGED(ERROR, domid, "unable to open /dev/null"); + rc = ERROR_FAIL; + goto out; + } + + rc = libxl__pre_open_qmp_socket(gc, domid, &qmp_probe_fd); + if (rc) goto out; + + flexarray_t *dm_args = flexarray_make(gc, 16, 1); + flexarray_vappend(dm_args, dm, + "-S", + "-no-user-config", + "-nodefaults", + "-nographic", + "-machine", "none,accel=xen", + NULL); + flexarray_vappend(dm_args, + "-chardev", + GCSPRINTF("socket,id=libxl-cmd,fd=%d,server=on,wait=off", + qmp_probe_fd), + "-mon", "chardev=libxl-cmd,mode=control", + NULL); + flexarray_append(dm_args, NULL); + char **exec_args = (char **) flexarray_contents(dm_args); + + const char *dom_path = libxl__xs_get_dompath(gc, domid); + + spawn->what = GCSPRINTF("domain %d qemu command line probe", domid); + spawn->xspath = "/dev/null"; /* No path to watch */ + spawn->timeout_ms = LIBXL_DEVICE_MODEL_START_TIMEOUT * 1000; + spawn->pidpath = GCSPRINTF("%s/image/device-model-pid", dom_path); + spawn->midproc_cb = libxl__spawn_record_pid; + spawn->confirm_cb = device_model_probe_confirm; + spawn->failure_cb = device_model_probe_startup_failed; + spawn->detached_cb = device_model_probe_detached; + + dmss->qmp.ao = ao; + dmss->qmp.callback = device_model_probe_cmdline; + dmss->qmp.domid = domid; + dmss->qmp.payload_fd = -1; + rc = libxl__ev_qmp_send(egc, &dmss->qmp, "query-command-line-options", NULL); + if (rc) goto out; + + rc = libxl__spawn_spawn(egc, spawn); + if (rc < 0) + goto out; + if (!rc) { /* inner child */ + setsid(); + libxl__exec(gc, null, logfile_w, logfile_w, dm, exec_args, NULL); + } + probe_spawned = true; + } else { + /* Continue with launching DM instead of probing it */ + probe_spawned = false; + } + rc = 0; +out: + if (qmp_probe_fd >= 0) + close(qmp_probe_fd); + if (null >= 0) + close(null); + if (logfile_w >= 0) + close(logfile_w); + if (rc || !probe_spawned) + device_model_launch(egc, dmss, rc); +} + +static void device_model_probe_startup_failed(libxl__egc *egc, + libxl__spawn_state *spawn, int rc) +{ + libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn); + device_model_probe_spawn_outcome(egc, dmss, rc); +} + +static void device_model_probe_confirm(libxl__egc *egc, + libxl__spawn_state *spawn, const char *xsdata) +{ + /* Nothing to do, confirmation is done via QMP instead */ +} + +static void device_model_probe_detached(libxl__egc *egc, + libxl__spawn_state *spawn) +{ + libxl__dm_spawn_state *dmss = CONTAINER_OF(spawn, *dmss, spawn); + device_model_probe_spawn_outcome(egc, dmss, 0); +} + +static void device_model_probe_cmdline(libxl__egc *egc, + libxl__ev_qmp *qmp, const libxl__json_object *response, int rc) +{ + libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp); + + if (rc) + goto out; + + /* + * query-command-line-options response: + * [ { 'option': 'str', 'parameters': [{ 'name': 'str', ... }] } ] + */ + + qmp->callback = device_model_probe_quit; + rc = libxl__ev_qmp_send(egc, qmp, "quit", NULL); + if (rc) goto out; + return; + +out: + libxl__spawn_initiate_failure(egc, &dmss->spawn, rc); +} + +static void device_model_probe_quit(libxl__egc *egc, + libxl__ev_qmp *qmp, const libxl__json_object *response, int rc) +{ + EGC_GC; + libxl__dm_spawn_state *dmss = CONTAINER_OF(qmp, *dmss, qmp); + + libxl__ev_qmp_dispose(gc, qmp); + libxl__spawn_initiate_detach(gc, &dmss->spawn); +} + +static void device_model_probe_spawn_outcome(libxl__egc *egc, + libxl__dm_spawn_state *dmss, int rc) +{ + EGC_GC; + libxl__ev_qmp_dispose(gc, &dmss->qmp); + + /* Ensure our QEMU command line probe is killed. */ + rc = libxl__kill_xs_path(gc, dmss->spawn.pidpath, + "qemu command-line probe"); + if (rc) { + LOGD(WARN, dmss->guest_domid, + "Killing qemu command-line probe pid from path %s", + dmss->spawn.pidpath); + } + + /* + * Ignore all failure from the QEMU command line probe, start the + * device model in any case. + */ + device_model_launch(egc, dmss, 0); +} + +static void device_model_launch(libxl__egc *egc, + libxl__dm_spawn_state *dmss, int rc) +{ + STATE_AO_GC(dmss->spawn.ao); + libxl_ctx *ctx = CTX; + libxl_domain_config *guest_config = dmss->guest_config; + const libxl_domain_create_info *c_info = &guest_config->c_info; + const libxl_domain_build_info *b_info = &guest_config->b_info; + const libxl_vnc_info *vnc = libxl__dm_vnc(guest_config); + char *path; + int logfile_w, null; + char **args, **arg, **envs; + xs_transaction_t t; + char *vm_path; + char **pass_stuff; + int dm_state_fd = -1; + + /* convenience aliases */ + const int domid = dmss->guest_domid; + libxl__domain_build_state *const state = dmss->build_state; + libxl__spawn_state *const spawn = &dmss->spawn; + const char *const dm = dmss->dm; + if (rc) goto out; diff --git a/tools/libs/light/libxl_internal.h b/tools/libs/light/libxl_internal.h index 3b58bb2d7f..e99adc56cb 100644 --- a/tools/libs/light/libxl_internal.h +++ b/tools/libs/light/libxl_internal.h @@ -4154,6 +4154,7 @@ struct libxl__dm_spawn_state { libxl__ev_qmp qmp; libxl__ev_time timeout; libxl__dm_resume_state dmrs; + const char *dm; /* filled in by user, must remain valid: */ uint32_t guest_domid; /* domain being served */ libxl_domain_config *guest_config;