diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml
index cd46f823ad..351b7c0772 100644
--- a/man/systemd-vmspawn.xml
+++ b/man/systemd-vmspawn.xml
@@ -135,7 +135,18 @@
-
+
+
+ PATH
+
+ Takes an absolute path, or a relative path beginning with
+ ./. Specifies a JSON firmware definition file, which allows selecting the
+ firmware to boot in the VM. If not specified a suitable firmware is automatically discovered. If the
+ special string list is specified lists all discovered firmwares.
+
+
+
+
System Identity Options
diff --git a/src/libsystemd/sd-event/event-util.c b/src/libsystemd/sd-event/event-util.c
index a3101227af..2338df1d62 100644
--- a/src/libsystemd/sd-event/event-util.c
+++ b/src/libsystemd/sd-event/event-util.c
@@ -151,3 +151,20 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
return 0;
}
+
+int event_add_child_pidref(
+ sd_event *e,
+ sd_event_source **s,
+ const PidRef *pid,
+ int options,
+ sd_event_child_handler_t callback,
+ void *userdata) {
+
+ if (!pidref_is_set(pid))
+ return -ESRCH;
+
+ if (pid->fd >= 0)
+ return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
+
+ return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
+}
diff --git a/src/libsystemd/sd-event/event-util.h b/src/libsystemd/sd-event/event-util.h
index c185584412..6259d5ae25 100644
--- a/src/libsystemd/sd-event/event-util.h
+++ b/src/libsystemd/sd-event/event-util.h
@@ -5,6 +5,8 @@
#include "sd-event.h"
+#include "pidref.h"
+
int event_reset_time(
sd_event *e,
sd_event_source **s,
@@ -32,3 +34,5 @@ static inline int event_source_disable(sd_event_source *s) {
}
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
+
+int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 445d85da6f..c48b51323b 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -5943,9 +5943,16 @@ static int run(int argc, char *argv[]) {
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
arg_quiet = true;
- if (!arg_quiet)
- log_info("Spawning container %s on %s.\nPress Ctrl-] three times within 1s to kill container.",
- arg_machine, arg_image ?: arg_directory);
+ if (!arg_quiet) {
+ const char *t = arg_image ?: arg_directory;
+ _cleanup_free_ char *u = NULL;
+ (void) terminal_urlify_path(t, t, &u);
+
+ log_info("%s %sSpawning container %s on %s.%s\n"
+ "%s %sPress %sCtrl-]%s three times within 1s to kill container.%s",
+ special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), arg_machine, u ?: t, ansi_normal(),
+ special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), ansi_highlight(), ansi_grey(), ansi_normal());
+ }
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
diff --git a/src/vmspawn/vmspawn-util.c b/src/vmspawn/vmspawn-util.c
index 16ece31809..79e1e5399a 100644
--- a/src/vmspawn/vmspawn-util.c
+++ b/src/vmspawn/vmspawn-util.c
@@ -29,7 +29,9 @@ OvmfConfig* ovmf_config_free(OvmfConfig *config) {
return NULL;
free(config->path);
+ free(config->format);
free(config->vars);
+ free(config->vars_format);
return mfree(config);
}
@@ -40,7 +42,7 @@ int qemu_check_kvm_support(void) {
log_debug_errno(errno, "/dev/kvm not found. Not using KVM acceleration.");
return false;
}
- if (errno == EPERM) {
+ if (ERRNO_IS_PRIVILEGE(errno)) {
log_debug_errno(errno, "Permission denied to access /dev/kvm. Not using KVM acceleration.");
return false;
}
@@ -62,11 +64,11 @@ int qemu_check_vsock_support(void) {
fd = open("/dev/vhost-vsock", O_RDWR|O_CLOEXEC);
if (fd >= 0)
return true;
- if (errno == ENODEV) {
+ if (ERRNO_IS_DEVICE_ABSENT(errno)) {
log_debug_errno(errno, "/dev/vhost-vsock device doesn't exist. Not adding a vsock device to the virtual machine.");
return false;
}
- if (errno == EPERM) {
+ if (ERRNO_IS_PRIVILEGE(errno)) {
log_debug_errno(errno, "Permission denied to access /dev/vhost-vsock. Not adding a vsock device to the virtual machine.");
return false;
}
@@ -78,16 +80,26 @@ int qemu_check_vsock_support(void) {
typedef struct FirmwareData {
char **features;
char *firmware;
+ char *firmware_format;
char *vars;
+ char *vars_format;
} FirmwareData;
+static bool firmware_data_supports_sb(const FirmwareData *fwd) {
+ assert(fwd);
+
+ return strv_contains(fwd->features, "secure-boot");
+}
+
static FirmwareData* firmware_data_free(FirmwareData *fwd) {
if (!fwd)
return NULL;
- fwd->features = strv_free(fwd->features);
- fwd->firmware = mfree(fwd->firmware);
- fwd->vars = mfree(fwd->vars);
+ strv_free(fwd->features);
+ free(fwd->firmware);
+ free(fwd->firmware_format);
+ free(fwd->vars);
+ free(fwd->vars_format);
return mfree(fwd);
}
@@ -95,8 +107,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FirmwareData*, firmware_data_free);
static int firmware_executable(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
static const JsonDispatch table[] = {
- { "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware), JSON_MANDATORY },
- { "format", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
+ { "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware), JSON_MANDATORY },
+ { "format", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware_format), JSON_MANDATORY },
{}
};
@@ -105,8 +117,8 @@ static int firmware_executable(const char *name, JsonVariant *v, JsonDispatchFla
static int firmware_nvram_template(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
static const JsonDispatch table[] = {
- { "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars), JSON_MANDATORY },
- { "format", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
+ { "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars), JSON_MANDATORY },
+ { "format", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars_format), JSON_MANDATORY },
{}
};
@@ -124,12 +136,136 @@ static int firmware_mapping(const char *name, JsonVariant *v, JsonDispatchFlags
return json_dispatch(v, table, flags, userdata);
}
+static int get_firmware_search_dirs(char ***ret) {
+ int r;
+
+ assert(ret);
+
+ /* Search in:
+ * - $XDG_CONFIG_HOME/qemu/firmware
+ * - /etc/qemu/firmware
+ * - /usr/share/qemu/firmware
+ *
+ * Prioritising entries in "more specific" directories */
+
+ _cleanup_free_ char *user_firmware_dir = NULL;
+ r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
+ if (r < 0)
+ return r;
+
+ _cleanup_strv_free_ char **l = NULL;
+ l = strv_new(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware");
+ if (!l)
+ return log_oom_debug();
+
+ *ret = TAKE_PTR(l);
+ return 0;
+}
+
+int list_ovmf_config(char ***ret) {
+ _cleanup_strv_free_ char **search_dirs = NULL;
+ int r;
+
+ assert(ret);
+
+ r = get_firmware_search_dirs(&search_dirs);
+ if (r < 0)
+ return r;
+
+ r = conf_files_list_strv(
+ ret,
+ ".json",
+ /* root= */ NULL,
+ CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
+ (const char *const*) search_dirs);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to list firmware files: %m");
+
+ return 0;
+}
+
+static int load_firmware_data(const char *path, FirmwareData **ret) {
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ _cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
+ r = json_parse_file(
+ /* f= */ NULL,
+ path,
+ /* flags= */ 0,
+ &json,
+ /* ret_line= */ NULL,
+ /* ret_column= */ NULL);
+ if (r < 0)
+ return r;
+
+ static const JsonDispatch table[] = {
+ { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
+ { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
+ { "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
+ { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
+ {}
+ };
+
+ _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
+ fwd = new0(FirmwareData, 1);
+ if (!fwd)
+ return -ENOMEM;
+
+ r = json_dispatch(json, table, JSON_ALLOW_EXTENSIONS, fwd);
+ if (r < 0)
+ return r;
+
+ *ret = TAKE_PTR(fwd);
+ return 0;
+}
+
+static int ovmf_config_make(FirmwareData *fwd, OvmfConfig **ret) {
+ assert(fwd);
+ assert(ret);
+
+ _cleanup_free_ OvmfConfig *config = NULL;
+ config = new(OvmfConfig, 1);
+ if (!config)
+ return -ENOMEM;
+
+ *config = (OvmfConfig) {
+ .path = TAKE_PTR(fwd->firmware),
+ .format = TAKE_PTR(fwd->firmware_format),
+ .vars = TAKE_PTR(fwd->vars),
+ .vars_format = TAKE_PTR(fwd->vars_format),
+ .supports_sb = firmware_data_supports_sb(fwd),
+ };
+
+ *ret = TAKE_PTR(config);
+ return 0;
+}
+
+int load_ovmf_config(const char *path, OvmfConfig **ret) {
+ _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
+ int r;
+
+ assert(path);
+ assert(ret);
+
+ r = load_firmware_data(path, &fwd);
+ if (r < 0)
+ return r;
+
+ return ovmf_config_make(fwd, ret);
+}
+
int find_ovmf_config(int search_sb, OvmfConfig **ret) {
_cleanup_(ovmf_config_freep) OvmfConfig *config = NULL;
- _cleanup_free_ char *user_firmware_dir = NULL;
_cleanup_strv_free_ char **conf_files = NULL;
int r;
+ assert(ret);
+
/* Search in:
* - $XDG_CONFIG_HOME/qemu/firmware
* - /etc/qemu/firmware
@@ -138,79 +274,35 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
* Prioritising entries in "more specific" directories
*/
- r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
+ r = list_ovmf_config(&conf_files);
if (r < 0)
return r;
- r = conf_files_list_strv(&conf_files, ".json", NULL, CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
- STRV_MAKE_CONST(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware"));
- if (r < 0)
- return log_debug_errno(r, "Failed to list config files: %m");
-
STRV_FOREACH(file, conf_files) {
_cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
- _cleanup_(json_variant_unrefp) JsonVariant *config_json = NULL;
- _cleanup_free_ char *contents = NULL;
- size_t contents_sz = 0;
- r = read_full_file(*file, &contents, &contents_sz);
- if (r == -ENOMEM)
- return r;
+ r = load_firmware_data(*file, &fwd);
if (r < 0) {
- log_debug_errno(r, "Failed to read contents of %s - ignoring: %m", *file);
- continue;
- }
-
- r = json_parse(contents, 0, &config_json, NULL, NULL);
- if (r == -ENOMEM)
- return r;
- if (r < 0) {
- log_debug_errno(r, "Failed to parse the JSON in %s - ignoring: %m", *file);
- continue;
- }
-
- static const JsonDispatch table[] = {
- { "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
- { "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
- { "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- { "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
- { "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
- {}
- };
-
- fwd = new0(FirmwareData, 1);
- if (!fwd)
- return -ENOMEM;
-
- r = json_dispatch(config_json, table, JSON_ALLOW_EXTENSIONS, fwd);
- if (r == -ENOMEM)
- return r;
- if (r < 0) {
- log_debug_errno(r, "Failed to extract the required fields from the JSON in %s - ignoring: %m", *file);
+ log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file);
continue;
}
if (strv_contains(fwd->features, "enrolled-keys")) {
- log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues", *file);
+ log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues.", *file);
continue;
}
- bool sb_present = strv_contains(fwd->features, "secure-boot");
-
/* exclude firmware which doesn't match our Secure Boot requirements */
- if (search_sb >= 0 && search_sb != sb_present) {
- log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration", *file);
+ if (search_sb >= 0 && !!search_sb != firmware_data_supports_sb(fwd)) {
+ log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration.", *file);
continue;
}
- config = new0(OvmfConfig, 1);
- if (!config)
- return -ENOMEM;
+ r = ovmf_config_make(fwd, &config);
+ if (r < 0)
+ return r;
- config->path = TAKE_PTR(fwd->firmware);
- config->vars = TAKE_PTR(fwd->vars);
- config->supports_sb = sb_present;
+ log_debug("Selected firmware definition %s.", *file);
break;
}
diff --git a/src/vmspawn/vmspawn-util.h b/src/vmspawn/vmspawn-util.h
index c931e66aac..f2d1b621e2 100644
--- a/src/vmspawn/vmspawn-util.h
+++ b/src/vmspawn/vmspawn-util.h
@@ -22,15 +22,27 @@
typedef struct OvmfConfig {
char *path;
+ char *format;
char *vars;
+ char *vars_format;
bool supports_sb;
} OvmfConfig;
+static inline const char *ovmf_config_format(const OvmfConfig *c) {
+ return ASSERT_PTR(c)->format ?: "raw";
+}
+
+static inline const char *ovmf_config_vars_format(const OvmfConfig *c) {
+ return ASSERT_PTR(c)->vars_format ?: "raw";
+}
+
OvmfConfig* ovmf_config_free(OvmfConfig *ovmf_config);
DEFINE_TRIVIAL_CLEANUP_FUNC(OvmfConfig*, ovmf_config_free);
int qemu_check_kvm_support(void);
int qemu_check_vsock_support(void);
-int find_ovmf_config(int search_sb, OvmfConfig **ret_ovmf_config);
+int list_ovmf_config(char ***ret);
+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(unsigned *machine_cid, const char *machine, int *ret_child_sock);
diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c
index e6ab63a6cd..a08bc4c679 100644
--- a/src/vmspawn/vmspawn.c
+++ b/src/vmspawn/vmspawn.c
@@ -13,6 +13,7 @@
#include "copy.h"
#include "creds-util.h"
#include "escape.h"
+#include "event-util.h"
#include "fileio.h"
#include "format-util.h"
#include "fs-util.h"
@@ -35,11 +36,12 @@
#include "vmspawn-settings.h"
#include "vmspawn-util.h"
+static bool arg_quiet = false;
static PagerFlags arg_pager_flags = 0;
static char *arg_image = NULL;
static char *arg_machine = NULL;
static char *arg_qemu_smp = NULL;
-static uint64_t arg_qemu_mem = 2ULL * 1024ULL * 1024ULL * 1024ULL;
+static uint64_t arg_qemu_mem = UINT64_C(2) * U64_GB;
static int arg_qemu_kvm = -1;
static int arg_qemu_vsock = -1;
static unsigned arg_vsock_cid = VMADDR_CID_ANY;
@@ -48,12 +50,14 @@ static int arg_secure_boot = -1;
static MachineCredentialContext arg_credentials = {};
static SettingsMask arg_settings_mask = 0;
static char **arg_parameters = NULL;
+static char *arg_firmware = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
+STATIC_DESTRUCTOR_REGISTER(arg_firmware, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
@@ -69,6 +73,7 @@ static int help(void) {
"%5$sSpawn a command or OS in a virtual machine.%6$s\n\n"
" -h --help Show this help\n"
" --version Print version string\n"
+ " -q --quiet Do not show status information\n"
" --no-pager Do not pipe output into a pager\n"
"\n%3$sImage:%4$s\n"
" -i --image=PATH Root file system disk image (or device node) for\n"
@@ -82,6 +87,7 @@ static int help(void) {
" --qemu-gui Start QEMU in graphical mode\n"
" --secure-boot=BOOL Configure whether to search for firmware which\n"
" supports Secure Boot\n"
+ " --firmware=PATH|list Select firmware definition file (or list available)\n"
"\n%3$sSystem Identity:%4$s\n"
" -M --machine=NAME Set the machine name for the container\n"
"\n%3$sCredentials:%4$s\n"
@@ -114,11 +120,13 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SECURE_BOOT,
ARG_SET_CREDENTIAL,
ARG_LOAD_CREDENTIAL,
+ ARG_FIRMWARE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
+ { "quiet", no_argument, NULL, 'q' },
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "image", required_argument, NULL, 'i' },
{ "machine", required_argument, NULL, 'M' },
@@ -131,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "secure-boot", required_argument, NULL, ARG_SECURE_BOOT },
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
+ { "firmware", required_argument, NULL, ARG_FIRMWARE },
{}
};
@@ -140,7 +149,7 @@ static int parse_argv(int argc, char *argv[]) {
assert(argv);
optind = 0;
- while ((c = getopt_long(argc, argv, "+hi:M", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hi:Mq", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
@@ -148,6 +157,10 @@ static int parse_argv(int argc, char *argv[]) {
case ARG_VERSION:
return version();
+ case 'q':
+ arg_quiet = true;
+ break;
+
case 'i':
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
if (r < 0)
@@ -241,6 +254,31 @@ static int parse_argv(int argc, char *argv[]) {
break;
}
+ case ARG_FIRMWARE:
+ if (streq(optarg, "list")) {
+ _cleanup_strv_free_ char **l = NULL;
+
+ r = list_ovmf_config(&l);
+ if (r < 0)
+ return log_error_errno(r, "Failed to list firmwares: %m");
+
+ bool nl = false;
+ fputstrv(stdout, l, "\n", &nl);
+ if (nl)
+ putchar('\n');
+
+ return 0;
+ }
+
+ if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./"))
+ return log_error_errno(SYNTHETIC_ERRNO(errno), "Absolute path or path starting with './' required.");
+
+ r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_firmware);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
@@ -378,25 +416,34 @@ static int vmspawn_dispatch_vsock_connections(sd_event_source *source, int fd, u
return 0;
}
-static int setup_notify_parent(sd_event *event, int fd, int *exit_status, sd_event_source **notify_event_source) {
+static int setup_notify_parent(sd_event *event, int fd, int *exit_status, sd_event_source **ret_notify_event_source) {
int r;
- r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, vmspawn_dispatch_vsock_connections, exit_status);
+ assert(event);
+ assert(fd >= 0);
+ assert(exit_status);
+ assert(ret_notify_event_source);
+
+ r = sd_event_add_io(event, ret_notify_event_source, fd, EPOLLIN, vmspawn_dispatch_vsock_connections, exit_status);
if (r < 0)
return log_error_errno(r, "Failed to allocate notify socket event source: %m");
- (void) sd_event_source_set_description(*notify_event_source, "vmspawn-notify-sock");
+ (void) sd_event_source_set_description(*ret_notify_event_source, "vmspawn-notify-sock");
return 0;
}
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
- pid_t pid;
+ PidRef *pidref = userdata;
+ int r;
- pid = PTR_TO_PID(userdata);
- if (pid > 0) {
- /* TODO: actually talk to qemu and ask the guest to shutdown here */
- if (kill(pid, SIGKILL) >= 0) {
+ /* TODO: actually talk to qemu and ask the guest to shutdown here */
+
+ if (pidref) {
+ r = pidref_kill(pidref, SIGKILL);
+ if (r < 0)
+ log_warning_errno(r, "Failed to kill qemu, terminating: %m");
+ else {
log_info("Trying to halt qemu. Send SIGTERM again to trigger vmspawn to immediately terminate.");
sd_event_source_set_userdata(s, NULL);
return 0;
@@ -450,7 +497,10 @@ static int run_virtual_machine(void) {
use_kvm = r;
}
- r = find_ovmf_config(arg_secure_boot, &ovmf_config);
+ if (arg_firmware)
+ r = load_ovmf_config(arg_firmware, &ovmf_config);
+ else
+ r = find_ovmf_config(arg_secure_boot, &ovmf_config);
if (r < 0)
return log_error_errno(r, "Failed to find OVMF config: %m");
@@ -473,7 +523,7 @@ static int run_virtual_machine(void) {
if (r < 0)
return log_error_errno(r, "Failed to find QEMU binary: %m");
- if (asprintf(&mem, "%.4fM", (double)arg_qemu_mem / (1024.0 * 1024.0)) < 0)
+ if (asprintf(&mem, "%" PRIu64, DIV_ROUND_UP(arg_qemu_mem, U64_MB)) < 0)
return log_oom();
cmdline = strv_new(
@@ -558,7 +608,7 @@ static int run_virtual_machine(void) {
if (r < 0)
return log_oom();
- r = strv_extendf(&cmdline, "if=pflash,format=raw,readonly=on,file=%s", ovmf_config->path);
+ r = strv_extendf(&cmdline, "if=pflash,format=%s,readonly=on,file=%s", ovmf_config_format(ovmf_config), ovmf_config->path);
if (r < 0)
return log_oom();
@@ -596,7 +646,7 @@ static int run_virtual_machine(void) {
if (r < 0)
return log_oom();
- r = strv_extendf(&cmdline, "file=%s,if=pflash,format=raw", ovmf_vars_to);
+ r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", ovmf_vars_to, ovmf_config_format(ovmf_config));
if (r < 0)
return log_oom();
}
@@ -663,15 +713,16 @@ static int run_virtual_machine(void) {
(void) sd_event_set_watchdog(event, true);
- pid_t child_pid;
- r = safe_fork_full(
+ _cleanup_(pidref_done) PidRef child_pidref = PIDREF_NULL;
+
+ r = pidref_safe_fork_full(
qemu_binary,
- NULL,
+ /* stdio_fds= */ NULL,
&child_vsock_fd, 1, /* pass the vsock fd to qemu */
- FORK_CLOEXEC_OFF,
- &child_pid);
+ FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE,
+ &child_pidref);
if (r < 0)
- return log_error_errno(r, "Failed to fork off %s: %m", qemu_binary);
+ return r;
if (r == 0) {
/* set TERM and LANG if they are missing */
if (setenv("TERM", "vt220", 0) < 0)
@@ -680,11 +731,14 @@ static int run_virtual_machine(void) {
if (setenv("LANG", "C.UTF-8", 0) < 0)
return log_oom();
- execve(qemu_binary, cmdline, environ);
+ execv(qemu_binary, cmdline);
log_error_errno(errno, "Failed to execve %s: %m", qemu_binary);
_exit(EXIT_FAILURE);
}
+ /* Close the vsock fd we passed to qemu in the parent. We don't need it anymore. */
+ child_vsock_fd = safe_close(child_vsock_fd);
+
int exit_status = INT_MAX;
if (use_vsock) {
r = setup_notify_parent(event, vsock_fd, &exit_status, ¬ify_event_source);
@@ -693,13 +747,13 @@ static int run_virtual_machine(void) {
}
/* shutdown qemu when we are shutdown */
- (void) sd_event_add_signal(event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
- (void) sd_event_add_signal(event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
+ (void) sd_event_add_signal(event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, &child_pidref);
+ (void) sd_event_add_signal(event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, &child_pidref);
(void) sd_event_add_signal(event, NULL, (SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
/* Exit when the child exits */
- (void) sd_event_add_child(event, NULL, child_pid, WEXITED, on_child_exit, NULL);
+ (void) event_add_child_pidref(event, NULL, &child_pidref, WEXITED, on_child_exit, NULL);
r = sd_event_loop(event);
if (r < 0)
@@ -757,6 +811,16 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
+ if (!arg_quiet) {
+ _cleanup_free_ char *u = NULL;
+ (void) terminal_urlify_path(arg_image, arg_image, &u);
+
+ log_info("%s %sSpawning VM %s on %s.%s\n"
+ "%s %sPress %sCtrl-a x%s to kill VM.%s",
+ special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), arg_machine, u ?: arg_image, ansi_normal(),
+ special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), ansi_highlight(), ansi_grey(), ansi_normal());
+ }
+
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
return run_virtual_machine();