[Libguestfs] [PATCH 3/4] launch: direct: Reimplement command line handling using qemuopts library.

Richard W.M. Jones rjones at redhat.com
Thu Apr 27 15:03:26 UTC 2017


---
 lib/Makefile.am     |   3 +
 lib/launch-direct.c | 498 ++++++++++++++++++++++++++++++----------------------
 lib/qemu.c          |   4 +
 3 files changed, 292 insertions(+), 213 deletions(-)

diff --git a/lib/Makefile.am b/lib/Makefile.am
index 063706f..b22fbd1 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -56,6 +56,7 @@ lib_LTLIBRARIES = libguestfs.la
 libguestfs_la_SOURCES = \
 	../common/errnostring/errnostring.h \
 	../common/protocol/guestfs_protocol.h \
+	../common/qemuopts/qemuopts.h \
 	../common/utils/guestfs-internal-frontend.h \
 	../common/utils/guestfs-internal-frontend-cleanups.h \
 	guestfs.h \
@@ -136,6 +137,7 @@ libguestfs_la_CPPFLAGS = \
 	-DLIBOSINFO_DB_PATH='"$(datadir)/libosinfo/db"' \
 	-I$(top_srcdir)/common/errnostring -I$(top_builddir)/common/errnostring \
 	-I$(top_srcdir)/common/protocol -I$(top_builddir)/common/protocol \
+	-I$(top_srcdir)/common/qemuopts -I$(top_builddir)/common/qemuopts \
 	-I$(top_srcdir)/common/utils -I$(top_builddir)/common/utils \
 	-I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib
 
@@ -151,6 +153,7 @@ libguestfs_la_CFLAGS = \
 libguestfs_la_LIBADD = \
 	../common/errnostring/liberrnostring.la \
 	../common/protocol/libprotocol.la \
+	../common/qemuopts/libqemuopts.la \
 	../common/utils/libutils.la \
 	$(PCRE_LIBS) $(MAGIC_LIBS) \
 	$(LIBVIRT_LIBS) $(LIBXML2_LIBS) \
diff --git a/lib/launch-direct.c b/lib/launch-direct.c
index aa9c292..4d5d6b9 100644
--- a/lib/launch-direct.c
+++ b/lib/launch-direct.c
@@ -47,6 +47,7 @@
 #include "guestfs.h"
 #include "guestfs-internal.h"
 #include "guestfs_protocol.h"
+#include "qemuopts.h"
 
 /* Per-handle data. */
 struct backend_direct_data {
@@ -61,7 +62,6 @@ struct backend_direct_data {
 
 static int is_openable (guestfs_h *g, const char *path, int flags);
 static char *make_appliance_dev (guestfs_h *g);
-static void print_qemu_command_line (guestfs_h *g, char **argv);
 
 static char *
 create_cow_overlay_direct (guestfs_h *g, void *datav, struct drive *drv)
@@ -157,11 +157,181 @@ debian_kvm_warning (guestfs_h *g)
 #endif /* __linux__ */
 }
 
+/* Some macros which make using qemuopts a bit easier. */
+#define flag(flag)                                                      \
+  do {                                                                  \
+    if (qemuopts_add_flag (qopts, (flag)) == -1) goto qemuopts_error;   \
+  } while (0)
+#define arg(flag, value)                                                \
+  do {                                                                  \
+    if (qemuopts_add_arg (qopts, (flag), (value)) == -1) goto qemuopts_error; \
+  } while (0)
+#define arg_format(flag, fs, ...)                                       \
+  do {                                                                  \
+    if (qemuopts_add_arg_format (qopts, (flag), (fs), ##__VA_ARGS__) == -1) \
+      goto qemuopts_error;                                              \
+  } while (0)
+#define arg_noquote(flag, value)                                        \
+  do {                                                                  \
+    if (qemuopts_add_arg_noquote (qopts, (flag), (value)) == -1)        \
+      goto qemuopts_error;                                              \
+  } while (0)
+#define start_list(flag)                                                \
+  if (qemuopts_start_arg_list (qopts, (flag)) == -1) goto qemuopts_error; \
+  do
+#define append_list(value)                                       \
+  do {                                                           \
+    if (qemuopts_append_arg_list (qopts, (value)) == -1)         \
+      goto qemuopts_error;                                       \
+  } while (0)
+#define append_list_format(fs, ...)                                     \
+  do {                                                                  \
+    if (qemuopts_append_arg_list_format (qopts, (fs), ##__VA_ARGS__) == -1) \
+      goto qemuopts_error;                                              \
+  } while (0)
+#define end_list()                                                      \
+  while (0);                                                            \
+  do {                                                                  \
+    if (qemuopts_end_arg_list (qopts) == -1) goto qemuopts_error;       \
+  } while (0)
+
+/**
+ * Add the standard elements of the C<-drive> parameter.
+ */
+static int
+add_drive_standard_params (guestfs_h *g, struct backend_direct_data *data,
+                           struct qemuopts *qopts,
+                           size_t i, struct drive *drv)
+{
+  if (!drv->overlay) {
+    CLEANUP_FREE char *file = NULL;
+
+    /* file= parameter. */
+    file = guestfs_int_drive_source_qemu_param (g, &drv->src);
+    append_list_format ("file=%s", file);
+
+    if (drv->readonly)
+      append_list ("snapshot=on");
+    append_list_format ("cache=%s",
+                        drv->cachemode ? drv->cachemode : "writeback");
+    if (drv->src.format)
+      append_list_format ("format=%s", drv->src.format);
+    if (drv->disk_label)
+      append_list_format ("serial=%s", drv->disk_label);
+    if (drv->copyonread)
+      append_list ("copy-on-read=on");
+
+    /* Discard mode. */
+    switch (drv->discard) {
+    case discard_disable:
+      /* Since the default is always discard=ignore, don't specify it
+       * on the command line.  This also avoids unnecessary breakage
+       * with qemu < 1.5 which didn't have the option at all.
+       */
+      break;
+    case discard_enable:
+      if (!guestfs_int_discard_possible (g, drv, &data->qemu_version))
+        return -1;
+      /*FALLTHROUGH*/
+    case discard_besteffort:
+      /* I believe from reading the code that this is always safe as
+       * long as qemu >= 1.5.
+       */
+      if (guestfs_int_version_ge (&data->qemu_version, 1, 5, 0))
+        append_list ("discard=unmap");
+      break;
+    }
+  }
+  else {
+    /* Writable qcow2 overlay on top of read-only drive. */
+    append_list_format ("file=%s", drv->overlay);
+    append_list ("cache=unsafe");
+    append_list ("format=qcow2");
+    if (drv->disk_label)
+      append_list_format ("serial=%s", drv->disk_label);
+  }
+
+  append_list_format ("id=hd%zu", i);
+
+  return 0;
+
+  /* This label is called implicitly from the qemuopts macros on error. */
+ qemuopts_error:
+  perrorf (g, "qemuopts");
+  return -1;
+}
+
+static int
+add_drive (guestfs_h *g, struct backend_direct_data *data,
+           struct qemuopts *qopts, size_t i, struct drive *drv)
+{
+  /* If there's an explicit 'iface', use it.  Otherwise default to
+   * virtio-scsi.
+   */
+  if (drv->iface && STREQ (drv->iface, "virtio")) { /* virtio-blk */
+    start_list ("-drive") {
+      if (add_drive_standard_params (g, data, qopts, i, drv) == -1)
+        return -1;
+      append_list ("if=none");
+    } end_list ();
+    start_list ("-device") {
+      append_list (VIRTIO_BLK);
+      append_list_format ("drive=hd%zu", i);
+    } end_list ();
+  }
+#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__)
+  else if (drv->iface && STREQ (drv->iface, "ide")) {
+    error (g, "'ide' interface does not work on ARM or PowerPC");
+    return -1;
+  }
+#endif
+  else if (drv->iface) {
+    start_list ("-drive") {
+      if (add_drive_standard_params (g, data, qopts, i, drv) == -1)
+        return -1;
+      append_list_format ("if=%s", drv->iface);
+    } end_list ();
+  }
+  else /* default case: virtio-scsi */ {
+    start_list ("-drive") {
+      if (add_drive_standard_params (g, data, qopts, i, drv) == -1)
+        return -1;
+      append_list ("if=none");
+    } end_list ();
+    start_list ("-device") {
+      append_list ("scsi-hd");
+      append_list_format ("drive=hd%zu", i);
+    } end_list ();
+  }
+
+  return 0;
+
+  /* This label is called implicitly from the qemuopts macros on error. */
+ qemuopts_error:
+  perrorf (g, "qemuopts");
+  return -1;
+}
+
+static int
+add_drives (guestfs_h *g, struct backend_direct_data *data,
+            struct qemuopts *qopts)
+{
+  size_t i;
+  struct drive *drv;
+
+  ITER_DRIVES (g, i, drv) {
+    if (add_drive (g, data, qopts, i, drv) == -1)
+      return -1;
+  }
+
+  return 0;
+}
+
 static int
 launch_direct (guestfs_h *g, void *datav, const char *arg)
 {
   struct backend_direct_data *data = datav;
-  CLEANUP_FREE_STRINGSBUF DECLARE_STRINGSBUF (cmdline);
+  struct qemuopts *qopts = NULL;
   int daemon_accept_sock = -1, console_sock = -1;
   int r;
   int flags;
@@ -174,12 +344,12 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   CLEANUP_FREE char *appliance_dev = NULL;
   uint32_t size;
   CLEANUP_FREE void *buf = NULL;
-  struct drive *drv;
-  size_t i;
   struct hv_param *hp;
   bool has_kvm;
   int force_tcg;
   const char *cpu_model;
+  CLEANUP_FREE char *append = NULL;
+  CLEANUP_FREE_STRING_LIST char **argv = NULL;
 
   /* At present you must add drives before starting the appliance.  In
    * future when we enable hotplugging you won't need to do this.
@@ -267,30 +437,28 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
    * forking, because after fork we are not allowed to use
    * non-signal-safe functions such as malloc.
    */
-#define ADD_CMDLINE(str)			\
-  guestfs_int_add_string (g, &cmdline, (str))
-#define ADD_CMDLINE_STRING_NODUP(str)			\
-  guestfs_int_add_string_nodup (g, &cmdline, (str))
-#define ADD_CMDLINE_PRINTF(fs,...)				\
-  guestfs_int_add_sprintf (g, &cmdline, (fs), ##__VA_ARGS__)
-
-  ADD_CMDLINE (g->hv);
+  qopts = qemuopts_create ();
+  if (qopts == NULL) {
+  qemuopts_error:
+    perrorf (g, "qemuopts");
+    goto cleanup0;
+  }
+  if (qemuopts_set_binary (qopts, g->hv) == -1) goto qemuopts_error;
 
   /* CVE-2011-4127 mitigation: Disable SCSI ioctls on virtio-blk
    * devices.
    */
-  ADD_CMDLINE ("-global");
-  ADD_CMDLINE (VIRTIO_BLK ".scsi=off");
+  arg ("-global", VIRTIO_BLK ".scsi=off");
 
   if (guestfs_int_qemu_supports (g, data->qemu_data, "-nodefconfig"))
-    ADD_CMDLINE ("-nodefconfig");
+    flag ("-nodefconfig");
 
   /* This oddly named option doesn't actually enable FIPS.  It just
    * causes qemu to do the right thing if FIPS is enabled in the
    * kernel.  So like libvirt, we pass it unconditionally.
    */
   if (guestfs_int_qemu_supports (g, data->qemu_data, "-enable-fips"))
-    ADD_CMDLINE ("-enable-fips");
+    flag ("-enable-fips");
 
   /* Newer versions of qemu (from around 2009/12) changed the
    * behaviour of monitors so that an implicit '-monitor stdio' is
@@ -301,60 +469,47 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
    * let's use that to avoid this and any future surprises.
    */
   if (guestfs_int_qemu_supports (g, data->qemu_data, "-nodefaults"))
-    ADD_CMDLINE ("-nodefaults");
+    flag ("-nodefaults");
 
   /* This disables the host-side display (SDL, Gtk). */
-  ADD_CMDLINE ("-display");
-  ADD_CMDLINE ("none");
+  arg ("-display", "none");
 
   /* See guestfs.pod / gdb */
   if (guestfs_int_get_backend_setting_bool (g, "gdb") > 0) {
-    ADD_CMDLINE ("-S");
-    ADD_CMDLINE ("-s");
+    flag ("-S");
+    flag ("-s");
     warning (g, "qemu debugging is enabled, connect gdb to tcp::1234 to begin");
   }
 
-  ADD_CMDLINE ("-machine");
-  ADD_CMDLINE_PRINTF (
+  start_list ("-machine") {
 #ifdef MACHINE_TYPE
-                      MACHINE_TYPE ","
+    append_list (MACHINE_TYPE);
 #endif
 #ifdef __aarch64__
-                      "%s"      /* gic-version */
+    if (has_kvm && !force_tcg)
+      append_list ("gic-version=host");
 #endif
-                      "accel=%s",
-#ifdef __aarch64__
-                      has_kvm && !force_tcg ? "gic-version=host," : "",
-#endif
-                      !force_tcg ? "kvm:tcg" : "tcg");
+    append_list_format ("accel=%s", !force_tcg ? "kvm:tcg" : "tcg");
+  } end_list ();
 
   cpu_model = guestfs_int_get_cpu_model (has_kvm && !force_tcg);
-  if (cpu_model) {
-    ADD_CMDLINE ("-cpu");
-    ADD_CMDLINE (cpu_model);
-  }
+  if (cpu_model)
+    arg ("-cpu", cpu_model);
 
-  if (g->smp > 1) {
-    ADD_CMDLINE ("-smp");
-    ADD_CMDLINE_PRINTF ("%d", g->smp);
-  }
+  if (g->smp > 1)
+    arg_format ("-smp", "%d", g->smp);
 
-  ADD_CMDLINE ("-m");
-  ADD_CMDLINE_PRINTF ("%d", g->memsize);
+  arg_format ("-m", "%d", g->memsize);
 
   /* Force exit instead of reboot on panic */
-  ADD_CMDLINE ("-no-reboot");
+  flag ("-no-reboot");
 
   /* These are recommended settings, see RHBZ#1053847. */
-  ADD_CMDLINE ("-rtc");
-  ADD_CMDLINE ("driftfix=slew");
-  if (guestfs_int_qemu_supports (g, data->qemu_data, "-no-hpet")) {
-    ADD_CMDLINE ("-no-hpet");
-  }
-  if (guestfs_int_version_ge (&data->qemu_version, 1, 3, 0)) {
-    ADD_CMDLINE ("-global");
-    ADD_CMDLINE ("kvm-pit.lost_tick_policy=discard");
-  }
+  arg ("-rtc", "driftfix=slew");
+  if (guestfs_int_qemu_supports (g, data->qemu_data, "-no-hpet"))
+    flag ("-no-hpet");
+  if (guestfs_int_version_ge (&data->qemu_version, 1, 3, 0))
+    arg ("-global", "kvm-pit.lost_tick_policy=discard");
 
   /* UEFI (firmware) if required. */
   if (guestfs_int_get_uefi (g, &uefi_code, &uefi_vars, &uefi_flags) == -1)
@@ -372,19 +527,24 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
     goto cleanup0;
   }
   if (uefi_code) {
-    ADD_CMDLINE ("-drive");
-    ADD_CMDLINE_PRINTF ("if=pflash,format=raw,file=%s,readonly", uefi_code);
+    start_list ("-drive") {
+      append_list ("if=pflash");
+      append_list ("format=raw");
+      append_list_format ("file=%s", uefi_code);
+      append_list ("readonly");
+    } end_list ();
     if (uefi_vars) {
-      ADD_CMDLINE ("-drive");
-      ADD_CMDLINE_PRINTF ("if=pflash,format=raw,file=%s", uefi_vars);
+      start_list ("-drive") {
+        append_list ("if=pflash");
+        append_list ("format=raw");
+        append_list_format ("file=%s", uefi_vars);
+      } end_list ();
     }
   }
 
   /* Kernel and initrd. */
-  ADD_CMDLINE ("-kernel");
-  ADD_CMDLINE (kernel);
-  ADD_CMDLINE ("-initrd");
-  ADD_CMDLINE (initrd);
+  arg ("-kernel", kernel);
+  arg ("-initrd", initrd);
 
   /* Add a random number generator (backend for virtio-rng).  This
    * isn't strictly necessary but means we won't need to hang around
@@ -392,121 +552,50 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
    */
   if (guestfs_int_qemu_supports_device (g, data->qemu_data,
                                         "virtio-rng-pci")) {
-    ADD_CMDLINE ("-object");
-    ADD_CMDLINE ("rng-random,filename=/dev/urandom,id=rng0");
-    ADD_CMDLINE ("-device");
-    ADD_CMDLINE ("virtio-rng-pci,rng=rng0");
+    start_list ("-object") {
+      append_list ("rng-random");
+      append_list ("filename=/dev/urandom");
+      append_list ("id=rng0");
+    } end_list ();
+    start_list ("-device") {
+      append_list ("virtio-rng-pci");
+      append_list ("rng=rng0");
+    } end_list ();
   }
 
   /* Create the virtio-scsi bus. */
-  ADD_CMDLINE ("-device");
-  ADD_CMDLINE (VIRTIO_SCSI ",id=scsi");
-
-  /* Add drives */
-  ITER_DRIVES (g, i, drv) {
-    CLEANUP_FREE char *file = NULL, *escaped_file = NULL, *param = NULL;
-
-    if (!drv->overlay) {
-      const char *discard_mode = "";
-
-      switch (drv->discard) {
-      case discard_disable:
-        /* Since the default is always discard=ignore, don't specify it
-         * on the command line.  This also avoids unnecessary breakage
-         * with qemu < 1.5 which didn't have the option at all.
-         */
-        break;
-      case discard_enable:
-        if (!guestfs_int_discard_possible (g, drv, &data->qemu_version))
-          goto cleanup0;
-        /*FALLTHROUGH*/
-      case discard_besteffort:
-        /* I believe from reading the code that this is always safe as
-         * long as qemu >= 1.5.
-         */
-        if (guestfs_int_version_ge (&data->qemu_version, 1, 5, 0))
-          discard_mode = ",discard=unmap";
-        break;
-      }
-
-      /* Make the file= parameter. */
-      file = guestfs_int_drive_source_qemu_param (g, &drv->src);
-      escaped_file = guestfs_int_qemu_escape_param (g, file);
-
-      /* Make the first part of the -drive parameter, everything up to
-       * the if=... at the end.
-       */
-      param = safe_asprintf
-        (g, "file=%s%s,cache=%s%s%s%s%s%s%s,id=hd%zu",
-         escaped_file,
-         drv->readonly ? ",snapshot=on" : "",
-         drv->cachemode ? drv->cachemode : "writeback",
-         discard_mode,
-         drv->src.format ? ",format=" : "",
-         drv->src.format ? drv->src.format : "",
-         drv->disk_label ? ",serial=" : "",
-         drv->disk_label ? drv->disk_label : "",
-         drv->copyonread ? ",copy-on-read=on" : "",
-         i);
-    }
-    else {
-      /* Writable qcow2 overlay on top of read-only drive. */
-      escaped_file = guestfs_int_qemu_escape_param (g, drv->overlay);
-      param = safe_asprintf
-        (g, "file=%s,cache=unsafe,format=qcow2%s%s,id=hd%zu",
-         escaped_file,
-         drv->disk_label ? ",serial=" : "",
-         drv->disk_label ? drv->disk_label : "",
-         i);
-    }
-
-    /* If there's an explicit 'iface', use it.  Otherwise default to
-     * virtio-scsi.
-     */
-    if (drv->iface && STREQ (drv->iface, "virtio")) { /* virtio-blk */
-      ADD_CMDLINE ("-drive");
-      ADD_CMDLINE_PRINTF ("%s,if=none" /* sic */, param);
-      ADD_CMDLINE ("-device");
-      ADD_CMDLINE_PRINTF (VIRTIO_BLK ",drive=hd%zu", i);
-    }
-#if defined(__arm__) || defined(__aarch64__) || defined(__powerpc__)
-    else if (drv->iface && STREQ (drv->iface, "ide")) {
-      error (g, "'ide' interface does not work on ARM or PowerPC");
-      goto cleanup0;
-    }
-#endif
-    else if (drv->iface) {
-      ADD_CMDLINE ("-drive");
-      ADD_CMDLINE_PRINTF ("%s,if=%s", param, drv->iface);
-    }
-    else /* virtio-scsi */ {
-      ADD_CMDLINE ("-drive");
-      ADD_CMDLINE_PRINTF ("%s,if=none" /* sic */, param);
-      ADD_CMDLINE ("-device");
-      ADD_CMDLINE_PRINTF ("scsi-hd,drive=hd%zu", i);
-    }
-  }
+  start_list ("-device") {
+    append_list (VIRTIO_SCSI);
+    append_list ("id=scsi");
+  } end_list ();
+
+  /* Add drives (except for the appliance drive). */
+  if (add_drives (g, data, qopts) == -1)
+    goto cleanup0;
 
   /* Add the ext2 appliance drive (after all the drives). */
   if (has_appliance_drive) {
-    ADD_CMDLINE ("-drive");
-    ADD_CMDLINE_PRINTF ("file=%s,snapshot=on,id=appliance,"
-                        "cache=unsafe,if=none,format=raw",
-                        appliance);
-
-    ADD_CMDLINE ("-device");
-    ADD_CMDLINE ("scsi-hd,drive=appliance");
+    start_list ("-drive") {
+      append_list_format ("file=%s", appliance);
+      append_list ("snapshot=on");
+      append_list ("id=appliance");
+      append_list ("cache=unsafe");
+      append_list ("if=none");
+      append_list ("format=raw");
+    } end_list ();
+    start_list ("-device") {
+      append_list ("scsi-hd");
+      append_list ("drive=appliance");
+    } end_list ();
 
     appliance_dev = make_appliance_dev (g);
   }
 
   /* Create the virtio serial bus. */
-  ADD_CMDLINE ("-device");
-  ADD_CMDLINE (VIRTIO_SERIAL);
+  arg ("-device", VIRTIO_SERIAL);
 
   /* Create the serial console. */
-  ADD_CMDLINE ("-serial");
-  ADD_CMDLINE ("stdio");
+  arg ("-serial", "stdio");
 
   if (g->verbose &&
       guestfs_int_qemu_supports_device (g, data->qemu_data,
@@ -517,30 +606,39 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
      * https://bugs.launchpad.net/qemu/+bug/1021649
      * QEmu has included sgabios upstream since just before 1.0.
      */
-    ADD_CMDLINE ("-device");
-    ADD_CMDLINE ("sga");
+    arg ("-device", "sga");
   }
 
   /* Set up virtio-serial for the communications channel. */
-  ADD_CMDLINE ("-chardev");
-  ADD_CMDLINE_PRINTF ("socket,path=%s,id=channel0", data->guestfsd_sock);
-  ADD_CMDLINE ("-device");
-  ADD_CMDLINE ("virtserialport,chardev=channel0,name=org.libguestfs.channel.0");
+  start_list ("-chardev") {
+    append_list ("socket");
+    append_list_format ("path=%s", data->guestfsd_sock);
+    append_list ("id=channel0");
+  } end_list ();
+  start_list ("-device") {
+    append_list ("virtserialport");
+    append_list ("chardev=channel0");
+    append_list ("name=org.libguestfs.channel.0");
+  } end_list ();
 
   /* Enable user networking. */
   if (g->enable_network) {
-    ADD_CMDLINE ("-netdev");
-    ADD_CMDLINE ("user,id=usernet,net=169.254.0.0/16");
-    ADD_CMDLINE ("-device");
-    ADD_CMDLINE (VIRTIO_NET ",netdev=usernet");
+    start_list ("-netdev") {
+      append_list ("user");
+      append_list ("id=usernet");
+      append_list ("net=169.254.0.0/16");
+    } end_list ();
+    start_list ("-device") {
+      append_list (VIRTIO_NET);
+      append_list ("netdev=usernet");
+    } end_list ();
   }
 
-  ADD_CMDLINE ("-append");
   flags = 0;
   if (!has_kvm || force_tcg)
     flags |= APPLIANCE_COMMAND_LINE_IS_TCG;
-  ADD_CMDLINE_STRING_NODUP
-    (guestfs_int_appliance_command_line (g, appliance_dev, flags));
+  append = guestfs_int_appliance_command_line (g, appliance_dev, flags);
+  arg ("-append", append);
 
   /* Note: custom command line parameters must come last so that
    * qemu -set parameters can modify previously added options.
@@ -548,13 +646,14 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
 
   /* Add any qemu parameters. */
   for (hp = g->hv_params; hp; hp = hp->next) {
-    ADD_CMDLINE (hp->hv_param);
-    if (hp->hv_value)
-      ADD_CMDLINE (hp->hv_value);
+    if (!hp->hv_value)
+      flag (hp->hv_param);
+    else
+      arg_noquote (hp->hv_param, hp->hv_value);
   }
 
-  /* Finish off the command line. */
-  guestfs_int_end_stringsbuf (g, &cmdline);
+  /* Get the argv list from the command line. */
+  argv = qemuopts_to_argv (qopts);
 
   r = fork ();
   if (r == -1) {
@@ -609,7 +708,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
 
     /* Dump the command line (after setting up stderr above). */
     if (g->verbose)
-      print_qemu_command_line (g, cmdline.argv);
+      qemuopts_to_channel (qopts, stderr);
 
     /* Put qemu in a new process group. */
     if (g->pgroup)
@@ -620,7 +719,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
 
     TRACE0 (launch_run_qemu);
 
-    execv (g->hv, cmdline.argv); /* Run qemu. */
+    execv (g->hv, argv);        /* Run qemu. */
     perror (g->hv);
     _exit (EXIT_FAILURE);
   }
@@ -628,6 +727,9 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   /* Parent (library). */
   data->pid = r;
 
+  qemuopts_free (qopts);
+  qopts = NULL;
+
   /* Fork the recovery process off which will kill qemu if the parent
    * process fails to do so (eg. if the parent segfaults).
    */
@@ -635,6 +737,7 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   if (g->recovery_proc) {
     r = fork ();
     if (r == 0) {
+      size_t i;
       struct sigaction sa;
       pid_t qemu_pid = data->pid;
       pid_t parent_pid = getppid ();
@@ -774,6 +877,8 @@ launch_direct (guestfs_h *g, void *datav, const char *arg)
   data->qemu_data = NULL;
 
  cleanup0:
+  if (qopts != NULL)
+    qemuopts_free (qopts);
   if (daemon_accept_sock >= 0)
     close (daemon_accept_sock);
   if (console_sock >= 0)
@@ -813,39 +918,6 @@ make_appliance_dev (guestfs_h *g)
   return safe_strdup (g, dev);  /* Caller frees. */
 }
 
-/* This is called from the forked subprocess just before qemu runs, so
- * it can just print the message straight to stderr, where it will be
- * picked up and funnelled through the usual appliance event API.
- */
-static void
-print_qemu_command_line (guestfs_h *g, char **argv)
-{
-  int i = 0;
-  int needs_quote;
-
-  struct timeval tv;
-  gettimeofday (&tv, NULL);
-  fprintf (stderr, "[%05" PRIi64 "ms] ",
-           guestfs_int_timeval_diff (&g->launch_t, &tv));
-
-  while (argv[i]) {
-    if (argv[i][0] == '-') /* -option starts a new line */
-      fprintf (stderr, " \\\n   ");
-
-    if (i > 0) fputc (' ', stderr);
-
-    /* Does it need shell quoting?  This only deals with simple cases. */
-    needs_quote = strcspn (argv[i], " ") != strlen (argv[i]);
-
-    if (needs_quote) fputc ('\'', stderr);
-    fprintf (stderr, "%s", argv[i]);
-    if (needs_quote) fputc ('\'', stderr);
-    i++;
-  }
-
-  fputc ('\n', stderr);
-}
-
 /* Check if a file can be opened. */
 static int
 is_openable (guestfs_h *g, const char *path, int flags)
diff --git a/lib/qemu.c b/lib/qemu.c
index 8bec6ba..41098a2 100644
--- a/lib/qemu.c
+++ b/lib/qemu.c
@@ -304,6 +304,10 @@ guestfs_int_qemu_supports_device (guestfs_h *g,
  * Escape a qemu parameter.
  *
  * Every C<,> becomes C<,,>.  The caller must free the returned string.
+ *
+ * XXX This functionality is now only used when constructing a
+ * qemu-img command in F<lib/create.c>.  We should extend the qemuopts
+ * library to cover this use case.
  */
 char *
 guestfs_int_qemu_escape_param (guestfs_h *g, const char *param)
-- 
2.9.3




More information about the Libguestfs mailing list