mirror of
https://github.com/systemd/systemd
synced 2024-07-21 18:24:38 +00:00
Merge pull request #31326 from CodethinkLabs/vmspawn/extra_drives
vmspawn: add --extra-drive= and correctly escape ',' in certain qemu arguments
This commit is contained in:
commit
14975aaef8
|
@ -328,6 +328,15 @@
|
|||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--extra-drive=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Takes a disk image or block device on the host and supplies it to the virtual machine as another drive.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
|
|
|
@ -18,6 +18,10 @@ vmspawn_libs = [
|
|||
libshared,
|
||||
]
|
||||
|
||||
vmspawn_test_template = test_template + {
|
||||
'link_with' : [vmspawn_libs],
|
||||
}
|
||||
|
||||
executables += [
|
||||
executable_template + {
|
||||
'name' : 'systemd-vmspawn',
|
||||
|
@ -26,5 +30,9 @@ executables += [
|
|||
'sources' : files('vmspawn.c'),
|
||||
'link_with' : vmspawn_libs,
|
||||
'dependencies' : [libblkid]
|
||||
}
|
||||
},
|
||||
vmspawn_test_template + {
|
||||
'conditions': ['ENABLE_VMSPAWN'],
|
||||
'sources' : files('test-vmspawn-util.c'),
|
||||
},
|
||||
]
|
||||
|
|
28
src/vmspawn/test-vmspawn-util.c
Normal file
28
src/vmspawn/test-vmspawn-util.c
Normal file
|
@ -0,0 +1,28 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "string-util.h"
|
||||
#include "vmspawn-util.h"
|
||||
#include "tests.h"
|
||||
|
||||
#define _ESCAPE_QEMU_VALUE_CHECK(str, correct, varname) \
|
||||
do { \
|
||||
_cleanup_free_ char* varname = NULL; \
|
||||
varname = escape_qemu_value(str); \
|
||||
assert(varname); \
|
||||
assert_se(streq(varname, correct)); \
|
||||
} while (0)
|
||||
|
||||
#define ESCAPE_QEMU_VALUE_CHECK(str, correct) \
|
||||
_ESCAPE_QEMU_VALUE_CHECK(str, correct, conf##__COUNTER__)
|
||||
|
||||
TEST(escape_qemu_value) {
|
||||
ESCAPE_QEMU_VALUE_CHECK("abcde", "abcde");
|
||||
ESCAPE_QEMU_VALUE_CHECK("a,bcde", "a,,bcde");
|
||||
ESCAPE_QEMU_VALUE_CHECK(",,,", ",,,,,,");
|
||||
ESCAPE_QEMU_VALUE_CHECK("", "");
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
|
@ -7,6 +7,7 @@
|
|||
#include "architecture.h"
|
||||
#include "conf-files.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "json.h"
|
||||
|
@ -433,3 +434,36 @@ int vsock_fix_child_cid(int vhost_device_fd, unsigned *machine_cid, const char *
|
|||
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EADDRNOTAVAIL), "Failed to assign a CID to the guest vsock");
|
||||
}
|
||||
|
||||
char* escape_qemu_value(const char *s) {
|
||||
const char *f;
|
||||
char *e, *t;
|
||||
size_t n;
|
||||
|
||||
assert(s);
|
||||
|
||||
/* QEMU requires that commas in arguments be escaped by doubling up the commas.
|
||||
* See https://www.qemu.org/docs/master/system/qemu-manpage.html#options
|
||||
* for more information.
|
||||
*
|
||||
* This function performs this escaping, returning an allocated string with the escaped value, or NULL if allocation failed. */
|
||||
|
||||
n = strlen(s);
|
||||
|
||||
if (n > (SIZE_MAX - 1) / 2)
|
||||
return NULL;
|
||||
|
||||
e = new(char, n*2 + 1);
|
||||
if (!e)
|
||||
return NULL;
|
||||
|
||||
for (f = s, t = e; f < s + n; f++) {
|
||||
*t++ = *f;
|
||||
if (*f == ',')
|
||||
*t++ = ',';
|
||||
}
|
||||
|
||||
*t = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
|
|
@ -87,3 +87,5 @@ int load_ovmf_config(const char *path, OvmfConfig **ret);
|
|||
int find_ovmf_config(int search_sb, OvmfConfig **ret);
|
||||
int find_qemu_binary(char **ret_qemu_binary);
|
||||
int vsock_fix_child_cid(int vsock_fd, unsigned *machine_cid, const char *machine);
|
||||
|
||||
char* escape_qemu_value(const char *s);
|
||||
|
|
|
@ -86,6 +86,7 @@ static char *arg_forward_journal = NULL;
|
|||
static bool arg_runtime_directory_created = false;
|
||||
static bool arg_privileged = false;
|
||||
static char **arg_kernel_cmdline_extra = NULL;
|
||||
static char **arg_extra_drives = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
|
@ -99,6 +100,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_initrds, strv_freep);
|
|||
STATIC_DESTRUCTOR_REGISTER(arg_runtime_mounts, runtime_mount_context_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_forward_journal, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_kernel_cmdline_extra, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_extra_drives, strv_freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
|
@ -144,6 +146,7 @@ static int help(void) {
|
|||
" Mount a file or directory from the host into the VM\n"
|
||||
" --bind-ro=SOURCE[:TARGET]\n"
|
||||
" Mount a file or directory, but read-only\n"
|
||||
" --extra-drive=PATH Adds an additional disk to the virtual machine\n"
|
||||
"\n%3$sIntegration:%4$s\n"
|
||||
" --forward-journal=FILE|DIR\n"
|
||||
" Forward the VM's journal to the host\n"
|
||||
|
@ -180,6 +183,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
ARG_NETWORK_USER_MODE,
|
||||
ARG_BIND,
|
||||
ARG_BIND_RO,
|
||||
ARG_EXTRA_DRIVE,
|
||||
ARG_SECURE_BOOT,
|
||||
ARG_PRIVATE_USERS,
|
||||
ARG_FORWARD_JOURNAL,
|
||||
|
@ -209,6 +213,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
{ "network-user-mode", no_argument, NULL, ARG_NETWORK_USER_MODE },
|
||||
{ "bind", required_argument, NULL, ARG_BIND },
|
||||
{ "bind-ro", required_argument, NULL, ARG_BIND_RO },
|
||||
{ "extra-drive", required_argument, NULL, ARG_EXTRA_DRIVE },
|
||||
{ "secure-boot", required_argument, NULL, ARG_SECURE_BOOT },
|
||||
{ "private-users", required_argument, NULL, ARG_PRIVATE_USERS },
|
||||
{ "forward-journal", required_argument, NULL, ARG_FORWARD_JOURNAL },
|
||||
|
@ -356,6 +361,19 @@ static int parse_argv(int argc, char *argv[]) {
|
|||
arg_settings_mask |= SETTING_BIND_MOUNTS;
|
||||
break;
|
||||
|
||||
case ARG_EXTRA_DRIVE: {
|
||||
_cleanup_free_ char *drive_path = NULL;
|
||||
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &drive_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = strv_consume(&arg_extra_drives, TAKE_PTR(drive_path));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_SECURE_BOOT:
|
||||
r = parse_tristate(optarg, &arg_secure_boot);
|
||||
if (r < 0)
|
||||
|
@ -1207,13 +1225,18 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "if=pflash,format=%s,readonly=on,file=%s", ovmf_config_format(ovmf_config), ovmf_config->path);
|
||||
_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();
|
||||
|
||||
_cleanup_(unlink_and_freep) char *ovmf_vars_to = NULL;
|
||||
if (ovmf_config->supports_sb) {
|
||||
const char *ovmf_vars_from = ovmf_config->vars;
|
||||
_cleanup_free_ char *escaped_ovmf_vars_to = NULL;
|
||||
_cleanup_close_ int source_fd = -EBADF, target_fd = -EBADF;
|
||||
|
||||
r = tempfn_random_child(NULL, "vmspawn-", &ovmf_vars_to);
|
||||
|
@ -1245,7 +1268,27 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", ovmf_vars_to, ovmf_config_format(ovmf_config));
|
||||
escaped_ovmf_vars_to = escape_qemu_value(ovmf_vars_to);
|
||||
if (!escaped_ovmf_vars_to)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", escaped_ovmf_vars_to, ovmf_config_format(ovmf_config));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
STRV_FOREACH(drive, arg_extra_drives) {
|
||||
_cleanup_free_ char *escaped_drive = NULL;
|
||||
|
||||
r = strv_extend(&cmdline, "-drive");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
escaped_drive = escape_qemu_value(*drive);
|
||||
if (!escaped_drive)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "format=raw,cache=unsafe,file=%s", escaped_drive);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
|
@ -1265,13 +1308,19 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
}
|
||||
|
||||
if (arg_image) {
|
||||
_cleanup_free_ char *escaped_image = NULL;
|
||||
|
||||
assert(!arg_directory);
|
||||
|
||||
r = strv_extend(&cmdline, "-drive");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw", arg_image);
|
||||
escaped_image = escape_qemu_value(arg_image);
|
||||
if (!escaped_image)
|
||||
log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "if=none,id=mkosi,file=%s,format=raw", escaped_image);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
|
@ -1283,16 +1332,21 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
}
|
||||
|
||||
if (arg_directory) {
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL;
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL, *escaped_sock_path = NULL;
|
||||
|
||||
r = start_virtiofsd(bus, trans_scope, arg_directory, /* uidmap= */ true, &sock_path, &sock_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
escaped_sock_path = escape_qemu_value(sock_path);
|
||||
if (!escaped_sock_path)
|
||||
log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-chardev");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "socket,id=%1$s,path=%2$s/%1$s", sock_name, sock_path);
|
||||
r = strv_extendf(&cmdline, "socket,id=%1$s,path=%2$s/%1$s", sock_name, escaped_sock_path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
|
@ -1314,16 +1368,20 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
return log_oom();
|
||||
|
||||
FOREACH_ARRAY(mount, arg_runtime_mounts.mounts, arg_runtime_mounts.n_mounts) {
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL, *clean_target = NULL;
|
||||
_cleanup_free_ char *sock_path = NULL, *sock_name = NULL, *clean_target = NULL, *escaped_sock_path = NULL;
|
||||
r = start_virtiofsd(bus, trans_scope, mount->source, /* uidmap= */ false, &sock_path, &sock_name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
escaped_sock_path = escape_qemu_value(sock_path);
|
||||
if (!escaped_sock_path)
|
||||
log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-chardev");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "socket,id=%1$s,path=%2$s/%1$s", sock_name, sock_path);
|
||||
r = strv_extendf(&cmdline, "socket,id=%1$s,path=%2$s/%1$s", sock_name, escaped_sock_path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
|
@ -1346,7 +1404,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
}
|
||||
|
||||
if (ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
_cleanup_free_ char *kcl = strv_join(arg_kernel_cmdline_extra, " ");
|
||||
_cleanup_free_ char *kcl = strv_join(arg_kernel_cmdline_extra, " "), *escaped_kcl = NULL;
|
||||
if (!kcl)
|
||||
return log_oom();
|
||||
|
||||
|
@ -1356,11 +1414,15 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
return log_oom();
|
||||
} else {
|
||||
if (ARCHITECTURE_SUPPORTS_SMBIOS) {
|
||||
escaped_kcl = escape_qemu_value(kcl);
|
||||
if (!escaped_kcl)
|
||||
log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-smbios");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", kcl);
|
||||
r = strv_extendf(&cmdline, "type=11,value=io.systemd.stub.kernel-cmdline-extra=%s", escaped_kcl);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
} else
|
||||
|
@ -1393,6 +1455,8 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
|
||||
_cleanup_free_ const char *tpm_state_tempdir = NULL;
|
||||
if (swtpm) {
|
||||
_cleanup_free_ char *escaped_state_dir = NULL;
|
||||
|
||||
r = start_tpm(bus, trans_scope, swtpm, &tpm_state_tempdir);
|
||||
if (r < 0) {
|
||||
/* only bail if the user asked for a tpm */
|
||||
|
@ -1401,11 +1465,15 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) {
|
|||
log_debug_errno(r, "Failed to start tpm, ignoring: %m");
|
||||
}
|
||||
|
||||
escaped_state_dir = escape_qemu_value(tpm_state_tempdir);
|
||||
if (!escaped_state_dir)
|
||||
log_oom();
|
||||
|
||||
r = strv_extend(&cmdline, "-chardev");
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "socket,id=chrtpm,path=%s/sock", tpm_state_tempdir);
|
||||
r = strv_extendf(&cmdline, "socket,id=chrtpm,path=%s/sock", escaped_state_dir);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
|
|
Loading…
Reference in a new issue