[XEN PATCH 1/3] libxl: Implement QEMU command line probe

Anthony PERARD posted 3 patches 2 months, 4 weeks ago
There is a newer version of this series
[XEN PATCH 1/3] libxl: Implement QEMU command line probe
Posted by Anthony PERARD 2 months, 4 weeks ago
From: Anthony PERARD <anthony.perard@citrix.com>

Starting with QEMU 9.0, the option "-chroot", that we use for the
"dmrestrict" feature, is removed. We need to find out which to use
between "-chroot" and "-run-with chroot=dir".

This patch implement the machinery to spawn QEMU, and to run the QMP
command "query-command-line-options" but doesn't yet look at the
actual result. Whether or not to use "-run-with chroot=dir" will be
implemented in a follow up patch.

The command line used to spawn the qemu we want to probe is mostly
similar to the one we already use for the device model, "-machine
none" comes from libvirt.

This patch implement the probing on qemu-xen, even if we probably not
going to use the result. We could check the feature wanted for the
domain been created, but this could get complicated fairly quickly.
We already need to check the options "b_info->dm_restrict" for
"-chroot" and "state->dm_runas" for "-runas" (which is deprecated).

Signed-off-by: Anthony PERARD <anthony.perard@vates.tech>
---
 tools/libs/light/libxl_dm.c       | 207 ++++++++++++++++++++++++++++--
 tools/libs/light/libxl_internal.h |   1 +
 2 files changed, 198 insertions(+), 10 deletions(-)

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;
-- 


Anthony Perard | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech
Re: [XEN PATCH 1/3] libxl: Implement QEMU command line probe
Posted by Jason Andryuk 2 months, 3 weeks ago
On 2024-08-27 06:03, Anthony PERARD wrote:
> From: Anthony PERARD <anthony.perard@citrix.com>
> 
> Starting with QEMU 9.0, the option "-chroot", that we use for the
> "dmrestrict" feature, is removed. We need to find out which to use
> between "-chroot" and "-run-with chroot=dir".
> 
> This patch implement the machinery to spawn QEMU, and to run the QMP
> command "query-command-line-options" but doesn't yet look at the
> actual result. Whether or not to use "-run-with chroot=dir" will be
> implemented in a follow up patch.
> 
> The command line used to spawn the qemu we want to probe is mostly
> similar to the one we already use for the device model, "-machine
> none" comes from libvirt.
> 
> This patch implement the probing on qemu-xen, even if we probably not
> going to use the result. We could check the feature wanted for the
> domain been created, but this could get complicated fairly quickly.

"domain being created"?

> We already need to check the options "b_info->dm_restrict" for
> "-chroot" and "state->dm_runas" for "-runas" (which is deprecated).
> 
> Signed-off-by: Anthony PERARD <anthony.perard@vates.tech>

Reviewed-by: Jason Andryuk <jason.andryuk@amd.com>
Re: [XEN PATCH 1/3] libxl: Implement QEMU command line probe
Posted by Anthony PERARD 2 months, 3 weeks ago
On Tue, Aug 27, 2024 at 06:17:05PM -0400, Jason Andryuk wrote:
> On 2024-08-27 06:03, Anthony PERARD wrote:
> > From: Anthony PERARD <anthony.perard@citrix.com>
> > 
> > Starting with QEMU 9.0, the option "-chroot", that we use for the
> > "dmrestrict" feature, is removed. We need to find out which to use
> > between "-chroot" and "-run-with chroot=dir".
> > 
> > This patch implement the machinery to spawn QEMU, and to run the QMP
> > command "query-command-line-options" but doesn't yet look at the
> > actual result. Whether or not to use "-run-with chroot=dir" will be
> > implemented in a follow up patch.
> > 
> > The command line used to spawn the qemu we want to probe is mostly
> > similar to the one we already use for the device model, "-machine
> > none" comes from libvirt.
> > 
> > This patch implement the probing on qemu-xen, even if we probably not
> > going to use the result. We could check the feature wanted for the
> > domain been created, but this could get complicated fairly quickly.
> 
> "domain being created"?

Yes.

> > We already need to check the options "b_info->dm_restrict" for
> > "-chroot" and "state->dm_runas" for "-runas" (which is deprecated).
> > 
> > Signed-off-by: Anthony PERARD <anthony.perard@vates.tech>
> 
> Reviewed-by: Jason Andryuk <jason.andryuk@amd.com>

Thanks,

-- 

Anthony Perard | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech