vmspawn: Use qemu config files
Let's avoid generating giant qemu command lines by using qemu config files instead.
This commit is contained in:
committed by
Daan De Meyer
parent
f83371324d
commit
8ff77aac13
@@ -6,6 +6,7 @@ endif
|
||||
|
||||
vmspawn_sources = files(
|
||||
'vmspawn.c',
|
||||
'vmspawn-qemu-config.c',
|
||||
'vmspawn-settings.c',
|
||||
'vmspawn-scope.c',
|
||||
'vmspawn-mount.c',
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "log.h"
|
||||
#include "vmspawn-qemu-config.h"
|
||||
|
||||
static bool qemu_config_type_valid(const char *type) {
|
||||
return !strchr(type, '\n');
|
||||
}
|
||||
|
||||
static bool qemu_config_id_valid(const char *id) {
|
||||
return !strpbrk(id, "\"\n");
|
||||
}
|
||||
|
||||
static bool qemu_config_key_name_valid(const char *key) {
|
||||
return !strpbrk(key, "=\n");
|
||||
}
|
||||
|
||||
static bool qemu_config_value_valid(const char *value) {
|
||||
return !strpbrk(value, "\"\n");
|
||||
}
|
||||
|
||||
int qemu_config_key(FILE *f, const char *key, const char *value) {
|
||||
assert(f);
|
||||
assert(key);
|
||||
assert(value);
|
||||
|
||||
if (!qemu_config_key_name_valid(key))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "QEMU config key '%s' contains '=' or newline.", key);
|
||||
if (!qemu_config_value_valid(value))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "QEMU config value '%s' contains quote or newline.", value);
|
||||
|
||||
if (fprintf(f, " %s = \"%s\"\n", key, value) < 0)
|
||||
return -errno_or_else(EIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int qemu_config_keyf(FILE *f, const char *key, const char *format, ...) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(key);
|
||||
assert(format);
|
||||
|
||||
va_start(ap, format);
|
||||
r = vasprintf(&value, format, ap);
|
||||
va_end(ap);
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return qemu_config_key(f, key, value);
|
||||
}
|
||||
|
||||
int qemu_config_section_impl(FILE *f, const char *type, const char *id, ...) {
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
assert(type);
|
||||
|
||||
if (!qemu_config_type_valid(type))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "QEMU config section type '%s' contains newline.", type);
|
||||
|
||||
if (id) {
|
||||
if (!qemu_config_id_valid(id))
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "QEMU config section id '%s' contains quote or newline.", id);
|
||||
fprintf(f, "\n[%s \"%s\"]\n", type, id);
|
||||
} else
|
||||
fprintf(f, "\n[%s]\n", type);
|
||||
|
||||
va_start(ap, id);
|
||||
for (;;) {
|
||||
const char *key = va_arg(ap, const char *);
|
||||
if (!key)
|
||||
break;
|
||||
|
||||
const char *value = ASSERT_PTR(va_arg(ap, const char *));
|
||||
|
||||
r = qemu_config_key(f, key, value);
|
||||
if (r < 0) {
|
||||
va_end(ap);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
va_end(ap);
|
||||
|
||||
if (ferror(f))
|
||||
return -errno_or_else(EIO);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
/* Helpers for writing QEMU -readconfig INI-style config files.
|
||||
*
|
||||
* QEMU config format:
|
||||
* [type "id"]
|
||||
* key = "value"
|
||||
*
|
||||
* Usage:
|
||||
* qemu_config_section(f, "device", "rng0",
|
||||
* "driver", "virtio-rng-pci",
|
||||
* "rng", "rng0");
|
||||
*/
|
||||
|
||||
/* Write a single key = "value" pair (for conditional keys added after a section header) */
|
||||
int qemu_config_key(FILE *f, const char *key, const char *value);
|
||||
|
||||
/* Write a single key with a printf-formatted value */
|
||||
int qemu_config_keyf(FILE *f, const char *key, const char *format, ...) _printf_(3, 4);
|
||||
|
||||
/* Write a section header with key-value pairs. Varargs are alternating key, value strings. */
|
||||
int qemu_config_section_impl(FILE *f, const char *type, const char *id, ...) _sentinel_;
|
||||
#define qemu_config_section(...) qemu_config_section_impl(__VA_ARGS__, NULL)
|
||||
+347
-212
@@ -37,6 +37,7 @@
|
||||
#include "event-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fork-notify.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
@@ -85,6 +86,7 @@
|
||||
#include "user-util.h"
|
||||
#include "utf8.h"
|
||||
#include "vmspawn-mount.h"
|
||||
#include "vmspawn-qemu-config.h"
|
||||
#include "vmspawn-register.h"
|
||||
#include "vmspawn-scope.h"
|
||||
#include "vmspawn-settings.h"
|
||||
@@ -2040,7 +2042,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
_cleanup_free_ int *pass_fds = NULL;
|
||||
sd_event_source **children = NULL;
|
||||
size_t n_children = 0, n_pass_fds = 0;
|
||||
const char *accel;
|
||||
int r;
|
||||
|
||||
CLEANUP_ARRAY(children, n_children, fork_notify_terminate_many);
|
||||
@@ -2119,16 +2120,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *machine = NULL;
|
||||
const char *shm = arg_directory || arg_runtime_mounts.n_mounts != 0 ? ",memory-backend=mem" : "";
|
||||
const char *hpet = ARCHITECTURE_SUPPORTS_HPET ? ",hpet=off" : "";
|
||||
if (ARCHITECTURE_SUPPORTS_SMM)
|
||||
machine = strjoin("type=" QEMU_MACHINE_TYPE ",smm=", on_off(ovmf_config->supports_sb), shm, hpet);
|
||||
else
|
||||
machine = strjoin("type=" QEMU_MACHINE_TYPE, shm, hpet);
|
||||
if (!machine)
|
||||
return log_oom();
|
||||
|
||||
if (arg_linux) {
|
||||
kernel = strdup(arg_linux);
|
||||
if (!kernel)
|
||||
@@ -2151,45 +2142,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (asprintf(&mem, "%" PRIu64 "M", DIV_ROUND_UP(arg_ram, U64_MB)) < 0)
|
||||
return log_oom();
|
||||
|
||||
cmdline = strv_new(
|
||||
qemu_binary,
|
||||
"-machine", machine,
|
||||
"-smp", arg_cpus ?: "1",
|
||||
"-m", mem,
|
||||
"-object", "rng-random,filename=/dev/urandom,id=rng0",
|
||||
"-device", "virtio-rng-pci,rng=rng0,id=rng-device0",
|
||||
"-device", "virtio-balloon,free-page-reporting=on"
|
||||
);
|
||||
if (!cmdline)
|
||||
return log_oom();
|
||||
|
||||
if (!sd_id128_is_null(arg_uuid))
|
||||
if (strv_extend_many(&cmdline, "-uuid", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_VMGENID) {
|
||||
/* Derive a vmgenid automatically from the invocation ID, in a deterministic way. */
|
||||
sd_id128_t vmgenid;
|
||||
r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(bd,84,6d,e3,e4,7d,4b,6c,a6,85,4a,87,0f,3c,a3,a0), &vmgenid);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to get invocation ID, making up randomized vmgenid: %m");
|
||||
|
||||
r = sd_id128_randomize(&vmgenid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make up randomized vmgenid: %m");
|
||||
}
|
||||
|
||||
if (strv_extend(&cmdline, "-device") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&cmdline, "vmgenid,guid=" SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(vmgenid)) < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
/* if we are going to be starting any units with state then create our runtime dir */
|
||||
/* Create runtime directory for the QEMU config file and other state */
|
||||
_cleanup_free_ char *runtime_dir = NULL;
|
||||
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir_destroy = NULL;
|
||||
if (arg_tpm != 0 || arg_directory || arg_runtime_mounts.n_mounts != 0 || arg_pass_ssh_key) {
|
||||
{
|
||||
_cleanup_free_ char *subdir = NULL;
|
||||
|
||||
if (asprintf(&subdir, "systemd/vmspawn.%" PRIx64, random_u64()) < 0)
|
||||
@@ -2214,6 +2170,88 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
log_debug("Using runtime directory: %s", runtime_dir);
|
||||
}
|
||||
|
||||
/* Build a QEMU config file for -readconfig. Items that can be expressed as QemuOpts sections go
|
||||
* here; things that require cmdline-only switches (e.g. -kernel, -smbios, -nographic, --add-fd)
|
||||
* are added to the cmdline strv below. */
|
||||
_cleanup_fclose_ FILE *config_file = NULL;
|
||||
_cleanup_(unlink_and_freep) char *config_path = NULL;
|
||||
r = fopen_temporary_child(runtime_dir, &config_file, &config_path);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create QEMU config file: %m");
|
||||
|
||||
r = qemu_config_section(config_file, "machine", /* id= */ NULL,
|
||||
"type", QEMU_MACHINE_TYPE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_SMM) {
|
||||
r = qemu_config_key(config_file, "smm", on_off(ovmf_config->supports_sb));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||
r = qemu_config_key(config_file, "memory-backend", "mem");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_HPET) {
|
||||
r = qemu_config_key(config_file, "hpet", "off");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = qemu_config_section(config_file, "object", "rng0",
|
||||
"qom-type", "rng-random",
|
||||
"filename", "/dev/urandom");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "rng-device0",
|
||||
"driver", "virtio-rng-pci",
|
||||
"rng", "rng0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "balloon0",
|
||||
"driver", "virtio-balloon",
|
||||
"free-page-reporting", "on");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_VMGENID) {
|
||||
sd_id128_t vmgenid;
|
||||
r = sd_id128_get_invocation_app_specific(SD_ID128_MAKE(bd,84,6d,e3,e4,7d,4b,6c,a6,85,4a,87,0f,3c,a3,a0), &vmgenid);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to get invocation ID, making up randomized vmgenid: %m");
|
||||
|
||||
r = sd_id128_randomize(&vmgenid);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to make up randomized vmgenid: %m");
|
||||
}
|
||||
|
||||
r = qemu_config_section(config_file, "device", "vmgenid0",
|
||||
"driver", "vmgenid");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_keyf(config_file, "guid", SD_ID128_UUID_FORMAT_STR, SD_ID128_FORMAT_VAL(vmgenid));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Start building the cmdline for items that must remain as command line arguments */
|
||||
cmdline = strv_new(qemu_binary,
|
||||
"-smp", arg_cpus ?: "1",
|
||||
"-m", mem);
|
||||
if (!cmdline)
|
||||
return log_oom();
|
||||
|
||||
if (!sd_id128_is_null(arg_uuid))
|
||||
if (strv_extend_many(&cmdline, "-uuid", SD_ID128_TO_UUID_STRING(arg_uuid)) < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_close_ int delegate_userns_fd = -EBADF, tap_fd = -EBADF;
|
||||
if (arg_network_stack == NETWORK_STACK_TAP) {
|
||||
if (have_effective_cap(CAP_NET_ADMIN) <= 0) {
|
||||
@@ -2238,11 +2276,15 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (tap_fd < 0)
|
||||
return log_error_errno(tap_fd, "Failed to allocate network tap device: %m");
|
||||
|
||||
r = strv_extend(&cmdline, "-nic");
|
||||
r = strv_extend(&cmdline, "-netdev");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "tap,fd=%i,model=virtio-net-pci", tap_fd);
|
||||
r = strv_extendf(&cmdline, "tap,id=net0,fd=%i", tap_fd);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend_many(&cmdline, "-device", "virtio-net-pci,netdev=net0");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
@@ -2267,30 +2309,46 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
} else
|
||||
mac_vm = arg_network_provided_mac;
|
||||
|
||||
r = strv_extend(&cmdline, "-nic");
|
||||
r = qemu_config_section(config_file, "netdev", "net0",
|
||||
"type", "tap",
|
||||
"ifname", tap_name,
|
||||
"script", "no",
|
||||
"downscript", "no");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
r = strv_extendf(&cmdline, "tap,ifname=%s,script=no,downscript=no,model=virtio-net-pci,mac=%s", tap_name, ETHER_ADDR_TO_STR(&mac_vm));
|
||||
r = qemu_config_section(config_file, "device", "nic0",
|
||||
"driver", "virtio-net-pci",
|
||||
"netdev", "net0",
|
||||
"mac", ETHER_ADDR_TO_STR(&mac_vm));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
}
|
||||
} else if (arg_network_stack == NETWORK_STACK_USER)
|
||||
r = strv_extend_many(&cmdline, "-nic", "user,model=virtio-net-pci");
|
||||
else
|
||||
} else if (arg_network_stack == NETWORK_STACK_USER) {
|
||||
r = qemu_config_section(config_file, "netdev", "net0",
|
||||
"type", "user");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "nic0",
|
||||
"driver", "virtio-net-pci",
|
||||
"netdev", "net0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
r = strv_extend_many(&cmdline, "-nic", "none");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
/* A shared memory backend might increase ram usage so only add one if actually necessary for virtiofsd. */
|
||||
if (arg_directory || arg_runtime_mounts.n_mounts != 0) {
|
||||
r = strv_extend(&cmdline, "-object");
|
||||
r = qemu_config_section(config_file, "object", "mem",
|
||||
"qom-type", "memory-backend-memfd",
|
||||
"size", mem,
|
||||
"share", "on");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "memory-backend-memfd,id=mem,size=%s,share=on", mem);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
}
|
||||
|
||||
bool use_vsock = arg_vsock > 0 && ARCHITECTURE_SUPPORTS_SMBIOS;
|
||||
@@ -2308,10 +2366,6 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
}
|
||||
|
||||
if (use_kvm && kvm_device_fd >= 0) {
|
||||
/* /dev/fdset/1 is magic string to tell qemu where to find the fd for /dev/kvm
|
||||
* we use this so that we can take a fd to /dev/kvm and then give qemu that fd */
|
||||
accel = "kvm,device=/dev/fdset/1";
|
||||
|
||||
r = strv_extend(&cmdline, "--add-fd");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
@@ -2324,14 +2378,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_oom();
|
||||
|
||||
pass_fds[n_pass_fds++] = kvm_device_fd;
|
||||
} else if (use_kvm)
|
||||
accel = "kvm";
|
||||
else
|
||||
accel = "tcg";
|
||||
|
||||
r = strv_extend_many(&cmdline, "-accel", accel);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "accel", /* id= */ NULL,
|
||||
"accel", "kvm",
|
||||
"device", "/dev/fdset/1");
|
||||
if (r < 0)
|
||||
return r;
|
||||
} else {
|
||||
r = qemu_config_section(config_file, "accel", /* id= */ NULL,
|
||||
"accel", use_kvm ? "kvm" : "tcg");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
_cleanup_close_ int child_vsock_fd = -EBADF;
|
||||
unsigned child_cid = arg_vsock_cid;
|
||||
@@ -2350,13 +2408,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fix CID for the guest VSOCK socket: %m");
|
||||
|
||||
r = strv_extend(&cmdline, "-device");
|
||||
r = qemu_config_section(config_file, "device", "vsock0",
|
||||
"driver", "vhost-vsock-pci");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
r = strv_extendf(&cmdline, "vhost-vsock-pci,guest-cid=%u,vhostfd=%d", child_cid, device_fd);
|
||||
r = qemu_config_keyf(config_file, "guest-cid", "%u", child_cid);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
r = qemu_config_keyf(config_file, "vhostfd", "%d", device_fd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!GREEDY_REALLOC(pass_fds, n_pass_fds + 1))
|
||||
return log_oom();
|
||||
@@ -2364,6 +2427,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
pass_fds[n_pass_fds++] = device_fd;
|
||||
}
|
||||
|
||||
/* -cpu stays on cmdline since not all flags are supported in config */
|
||||
r = strv_extend_many(&cmdline, "-cpu",
|
||||
#ifdef __x86_64__
|
||||
"max,hv_relaxed,hv-vapic,hv-time"
|
||||
@@ -2390,69 +2454,105 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
if (master < 0)
|
||||
return log_error_errno(master, "Failed to setup pty: %m");
|
||||
|
||||
if (strv_extend_many(
|
||||
&cmdline,
|
||||
"-nographic",
|
||||
"-nodefaults",
|
||||
"-device", "virtio-serial-pci,id=vmspawn-virtio-serial-pci",
|
||||
"-chardev") < 0)
|
||||
r = strv_extend_many(&cmdline, "-nographic", "-nodefaults");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend_joined(&cmdline, "serial,id=console,path=", pty_path) < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "device", "vmspawn-virtio-serial-pci",
|
||||
"driver", "virtio-serial-pci");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "chardev", "console",
|
||||
"backend", "serial",
|
||||
"path", pty_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "virtconsole0",
|
||||
"driver", "virtconsole",
|
||||
"chardev", "console");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
"-device", "virtconsole,chardev=console");
|
||||
break;
|
||||
}
|
||||
|
||||
case CONSOLE_GUI:
|
||||
/* Enable support for the qemu guest agent for clipboard sharing, resolution scaling, etc. */
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
"-vga",
|
||||
"virtio",
|
||||
"-device", "virtio-serial",
|
||||
"-chardev", "spicevmc,id=vdagent,debug=0,name=vdagent",
|
||||
"-device", "virtserialport,chardev=vdagent,name=org.qemu.guest_agent.0");
|
||||
/* -vga is a convenience option, keep on cmdline */
|
||||
r = strv_extend_many(&cmdline, "-vga", "virtio");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = qemu_config_section(config_file, "device", "virtio-serial0",
|
||||
"driver", "virtio-serial");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "chardev", "vdagent",
|
||||
"backend", "spicevmc",
|
||||
"debug", "0",
|
||||
"name", "vdagent");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "vdagent-port0",
|
||||
"driver", "virtserialport",
|
||||
"chardev", "vdagent",
|
||||
"name", "org.qemu.guest_agent.0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case CONSOLE_NATIVE:
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
"-nographic",
|
||||
"-nodefaults",
|
||||
"-chardev", "stdio,mux=on,id=console,signal=off",
|
||||
"-device", "virtio-serial-pci,id=vmspawn-virtio-serial-pci",
|
||||
"-device", "virtconsole,chardev=console",
|
||||
"-mon", "console");
|
||||
r = strv_extend_many(&cmdline, "-nographic", "-nodefaults");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = qemu_config_section(config_file, "chardev", "console",
|
||||
"backend", "stdio",
|
||||
"mux", "on",
|
||||
"signal", "off");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "vmspawn-virtio-serial-pci",
|
||||
"driver", "virtio-serial-pci");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "device", "virtconsole0",
|
||||
"driver", "virtconsole",
|
||||
"chardev", "console");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "mon", "mon0",
|
||||
"chardev", "console");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case CONSOLE_HEADLESS:
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
"-nographic",
|
||||
"-nodefaults");
|
||||
r = strv_extend_many(&cmdline, "-nographic", "-nodefaults");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-drive");
|
||||
r = qemu_config_section(config_file, "drive", "ovmf-code",
|
||||
"if", "pflash",
|
||||
"format", ovmf_config_format(ovmf_config),
|
||||
"readonly", "on",
|
||||
"file", ovmf_config->path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *escaped_ovmf_config_path = escape_qemu_value(ovmf_config->path);
|
||||
if (!escaped_ovmf_config_path)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "if=pflash,format=%s,readonly=on,file=%s", ovmf_config_format(ovmf_config), escaped_ovmf_config_path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
if (arg_efi_nvram_state_mode == STATE_AUTO && !arg_ephemeral) {
|
||||
assert(!arg_efi_nvram_state_path);
|
||||
@@ -2516,21 +2616,26 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
|
||||
destroy_path = mfree(destroy_path); /* disarm auto-destroy */
|
||||
|
||||
r = strv_extend_many(
|
||||
&cmdline,
|
||||
"-global", "ICH9-LPC.disable_s3=1",
|
||||
"-global", "driver=cfi.pflash01,property=secure,value=on",
|
||||
"-drive");
|
||||
r = qemu_config_section(config_file, "global", /* id= */ NULL,
|
||||
"driver", "ICH9-LPC",
|
||||
"property", "disable_s3",
|
||||
"value", "1");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *escaped_state = escape_qemu_value(state);
|
||||
if (!escaped_state)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_state, ovmf_config_format(ovmf_config));
|
||||
r = qemu_config_section(config_file, "global", /* id= */ NULL,
|
||||
"driver", "cfi.pflash01",
|
||||
"property", "secure",
|
||||
"value", "on");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "drive", "ovmf-vars",
|
||||
"file", state,
|
||||
"if", "pflash",
|
||||
"format", ovmf_config_format(ovmf_config));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (kernel) {
|
||||
@@ -2559,8 +2664,10 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
}
|
||||
|
||||
if (need_scsi_controller) {
|
||||
if (strv_extend_many(&cmdline, "-device", "virtio-scsi-pci,id=vmspawn_scsi") < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "device", "vmspawn_scsi",
|
||||
"driver", "virtio-scsi-pci");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (arg_image) {
|
||||
@@ -2574,54 +2681,60 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
arg_image);
|
||||
}
|
||||
|
||||
if (strv_extend(&cmdline, "-drive") < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *escaped_image = escape_qemu_value(arg_image);
|
||||
if (!escaped_image)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&cmdline, "if=none,id=vmspawn,file=%s,format=%s,discard=%s,snapshot=%s",
|
||||
escaped_image, image_format_to_string(arg_image_format), on_off(arg_discard_disk), on_off(arg_ephemeral)) < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "drive", "vmspawn",
|
||||
"if", "none",
|
||||
"file", arg_image,
|
||||
"format", image_format_to_string(arg_image_format),
|
||||
"discard", on_off(arg_discard_disk),
|
||||
"snapshot", on_off(arg_ephemeral));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *image_fn = NULL;
|
||||
r = path_extract_filename(arg_image, &image_fn);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to extract filename from path '%s': %m", image_fn);
|
||||
|
||||
_cleanup_free_ char *escaped_image_fn = escape_qemu_value(image_fn);
|
||||
if (!escaped_image_fn)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(&cmdline, "-device") < 0)
|
||||
return log_oom();
|
||||
const char *disk_driver;
|
||||
_cleanup_free_ char *serial = NULL;
|
||||
|
||||
switch (arg_image_disk_type) {
|
||||
case DISK_TYPE_VIRTIO_BLK:
|
||||
if (strv_extend_joined(&cmdline, "virtio-blk-pci,drive=vmspawn,bootindex=1,serial=", escaped_image_fn) < 0)
|
||||
disk_driver = "virtio-blk-pci";
|
||||
serial = strdup(image_fn);
|
||||
if (!serial)
|
||||
return log_oom();
|
||||
break;
|
||||
case DISK_TYPE_VIRTIO_SCSI: {
|
||||
_cleanup_free_ char *serial = NULL;
|
||||
if (disk_serial(escaped_image_fn, 30, &serial) < 0)
|
||||
return log_oom();
|
||||
if (strv_extend_joined(&cmdline, "scsi-hd,bus=vmspawn_scsi.0,drive=vmspawn,bootindex=1,serial=", serial) < 0)
|
||||
case DISK_TYPE_VIRTIO_SCSI:
|
||||
disk_driver = "scsi-hd";
|
||||
r = disk_serial(image_fn, 30, &serial);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
}
|
||||
case DISK_TYPE_NVME: {
|
||||
_cleanup_free_ char *serial = NULL;
|
||||
if (disk_serial(escaped_image_fn, 20, &serial) < 0)
|
||||
return log_oom();
|
||||
if (strv_extend_joined(&cmdline, "nvme,drive=vmspawn,bootindex=1,serial=", serial) < 0)
|
||||
case DISK_TYPE_NVME:
|
||||
disk_driver = "nvme";
|
||||
r = disk_serial(image_fn, 20, &serial);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
|
||||
r = qemu_config_section(config_file, "device", "vmspawn-disk",
|
||||
"driver", disk_driver,
|
||||
"drive", "vmspawn",
|
||||
"bootindex", "1",
|
||||
"serial", serial);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (arg_image_disk_type == DISK_TYPE_VIRTIO_SCSI) {
|
||||
r = qemu_config_key(config_file, "bus", "vmspawn_scsi.0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = grow_image(arg_image, arg_grow_image);
|
||||
if (r < 0)
|
||||
return r;
|
||||
@@ -2686,21 +2799,19 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
pidref_done(&child);
|
||||
children[n_children++] = TAKE_PTR(source);
|
||||
|
||||
_cleanup_free_ char *escaped_listen_address = escape_qemu_value(listen_address);
|
||||
if (!escaped_listen_address)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "chardev", "rootdir",
|
||||
"backend", "socket",
|
||||
"path", listen_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend(&cmdline, "-chardev") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&cmdline, "socket,id=rootdir,path=%s", escaped_listen_address) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend_many(
|
||||
&cmdline,
|
||||
"-device",
|
||||
"vhost-user-fs-pci,queue-size=1024,chardev=rootdir,tag=root") < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "device", "rootdir",
|
||||
"driver", "vhost-user-fs-pci",
|
||||
"queue-size", "1024",
|
||||
"chardev", "rootdir",
|
||||
"tag", "root");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend(&arg_kernel_cmdline_extra, "root=root rootfstype=virtiofs rw") < 0)
|
||||
return log_oom();
|
||||
@@ -2810,25 +2921,23 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
pidref_done(&child);
|
||||
children[n_children++] = TAKE_PTR(source);
|
||||
|
||||
_cleanup_free_ char *escaped_listen_address = escape_qemu_value(listen_address);
|
||||
if (!escaped_listen_address)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(&cmdline, "-chardev") < 0)
|
||||
return log_oom();
|
||||
|
||||
_cleanup_free_ char *id = NULL;
|
||||
if (asprintf(&id, "mnt%zu", j) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&cmdline, "socket,id=%s,path=%s", id, escaped_listen_address) < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "chardev", id,
|
||||
"backend", "socket",
|
||||
"path", listen_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_extend(&cmdline, "-device") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extendf(&cmdline, "vhost-user-fs-pci,queue-size=1024,chardev=%1$s,tag=%1$s", id) < 0)
|
||||
return log_oom();
|
||||
r = qemu_config_section(config_file, "device", id,
|
||||
"driver", "vhost-user-fs-pci",
|
||||
"queue-size", "1024",
|
||||
"chardev", id,
|
||||
"tag", id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_free_ char *clean_target = xescape(m->target, "\":");
|
||||
if (!clean_target)
|
||||
@@ -2911,25 +3020,33 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
}
|
||||
|
||||
if (tpm_socket_address) {
|
||||
_cleanup_free_ char *escaped_tpm_socket_address = escape_qemu_value(tpm_socket_address);
|
||||
if (!escaped_tpm_socket_address)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend(&cmdline, "-chardev") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend_joined(&cmdline, "socket,id=chrtpm,path=", tpm_socket_address) < 0)
|
||||
return log_oom();
|
||||
|
||||
if (strv_extend_many(&cmdline, "-tpmdev", "emulator,id=tpm0,chardev=chrtpm") < 0)
|
||||
return log_oom();
|
||||
|
||||
if (native_architecture() == ARCHITECTURE_X86_64)
|
||||
r = strv_extend_many(&cmdline, "-device", "tpm-tis,tpmdev=tpm0");
|
||||
else if (IN_SET(native_architecture(), ARCHITECTURE_ARM64, ARCHITECTURE_ARM64_BE))
|
||||
r = strv_extend_many(&cmdline, "-device", "tpm-tis-device,tpmdev=tpm0");
|
||||
r = qemu_config_section(config_file, "chardev", "chrtpm",
|
||||
"backend", "socket",
|
||||
"path", tpm_socket_address);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
return r;
|
||||
|
||||
r = qemu_config_section(config_file, "tpmdev", "tpm0",
|
||||
"type", "emulator",
|
||||
"chardev", "chrtpm");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
const char *tpm_driver;
|
||||
if (native_architecture() == ARCHITECTURE_X86_64)
|
||||
tpm_driver = "tpm-tis";
|
||||
else if (IN_SET(native_architecture(), ARCHITECTURE_ARM64, ARCHITECTURE_ARM64_BE))
|
||||
tpm_driver = "tpm-tis-device";
|
||||
else
|
||||
tpm_driver = NULL;
|
||||
|
||||
if (tpm_driver) {
|
||||
r = qemu_config_section(config_file, "device", "tpmdev0",
|
||||
"driver", tpm_driver,
|
||||
"tpmdev", "tpm0");
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
char *initrd = NULL;
|
||||
@@ -3083,6 +3200,16 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
return log_error_errno(r, "Failed to call getsockname on VSOCK: %m");
|
||||
}
|
||||
|
||||
/* Finalize the config file and add -readconfig to the cmdline */
|
||||
r = fflush_and_check(config_file);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to write QEMU config file: %m");
|
||||
config_file = safe_fclose(config_file);
|
||||
|
||||
r = strv_extend_many(&cmdline, "-readconfig", config_path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
const char *e = secure_getenv("SYSTEMD_VMSPAWN_QEMU_EXTRA");
|
||||
if (e) {
|
||||
r = strv_split_and_extend_full(&cmdline, e,
|
||||
@@ -3093,6 +3220,14 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *config_contents = NULL;
|
||||
|
||||
r = read_full_file(config_path, &config_contents, /* ret_size= */ NULL);
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to read back QEMU config file, ignoring: %m");
|
||||
else
|
||||
log_debug("QEMU config file %s:\n%s", config_path, config_contents);
|
||||
|
||||
_cleanup_free_ char *joined = quote_command_line(cmdline, SHELL_ESCAPE_EMPTY);
|
||||
if (!joined)
|
||||
return log_oom();
|
||||
|
||||
Reference in New Issue
Block a user