mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
launch: direct: Reimplement command line handling using qemuopts library.
This commit is contained in:
@@ -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) \
|
||||
|
||||
@@ -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");
|
||||
start_list ("-device") {
|
||||
append_list (VIRTIO_SCSI);
|
||||
append_list ("id=scsi");
|
||||
} end_list ();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
/* 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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user