diff --git a/man/systemd-vmspawn.xml b/man/systemd-vmspawn.xml index 72dbcb15d9d..28070cfe8f6 100644 --- a/man/systemd-vmspawn.xml +++ b/man/systemd-vmspawn.xml @@ -330,7 +330,9 @@ 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. + special string list is specified lists all discovered firmwares. If the special + string describe is specified, the firmware that would be selected (taking + into account) is printed and the program exits. diff --git a/shell-completion/bash/systemd-vmspawn b/shell-completion/bash/systemd-vmspawn index a6ce9708abe..b035a42a655 100644 --- a/shell-completion/bash/systemd-vmspawn +++ b/shell-completion/bash/systemd-vmspawn @@ -54,7 +54,7 @@ _systemd_vmspawn() { comps=$(compgen -f -- "$cur" ) elif __contains_word "$prev" ${OPTS[FIRMWARE]}; then compopt -o nospace -o filenames - comps="list $(compgen -f -- "$cur" )" + comps="list describe $(compgen -f -- "$cur" )" elif __contains_word "$prev" ${OPTS[FIRMWARE_FEATURES]}; then comps='list' elif __contains_word "$prev" ${OPTS[BIND]}; then diff --git a/src/vmspawn/vmspawn-util.c b/src/vmspawn/vmspawn-util.c index b8e2c09c830..149fb8f7c91 100644 --- a/src/vmspawn/vmspawn-util.c +++ b/src/vmspawn/vmspawn-util.c @@ -244,7 +244,7 @@ int list_ovmf_config(char ***ret) { return 0; } -static int load_firmware_data(const char *path, FirmwareData **ret) { +static int load_firmware_data(const char *path, FirmwareData **ret, sd_json_variant **ret_json) { int r; assert(path); @@ -281,6 +281,10 @@ static int load_firmware_data(const char *path, FirmwareData **ret) { return r; *ret = TAKE_PTR(fwd); + + if (ret_json) + *ret_json = TAKE_PTR(json); + return 0; } @@ -298,7 +302,7 @@ int list_ovmf_firmware_features(char ***ret) { STRV_FOREACH(file, conf_files) { _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL; - r = load_firmware_data(*file, &fwd); + r = load_firmware_data(*file, &fwd, /* ret_json= */ NULL); if (r < 0) { log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file); continue; @@ -347,14 +351,18 @@ int load_ovmf_config(const char *path, OvmfConfig **ret) { assert(path); assert(ret); - r = load_firmware_data(path, &fwd); + r = load_firmware_data(path, &fwd, /* ret_json= */ NULL); if (r < 0) return r; return ovmf_config_make(fwd, ret); } -int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret) { +int find_ovmf_config( + Set *features_include, + Set *features_exclude, + OvmfConfig **ret, + sd_json_variant **ret_firmware_json) { _cleanup_(ovmf_config_freep) OvmfConfig *config = NULL; _cleanup_strv_free_ char **conf_files = NULL; const char* native_arch_qemu; @@ -380,8 +388,9 @@ int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig ** STRV_FOREACH(file, conf_files) { _cleanup_(firmware_data_freep) FirmwareData *fwd = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; - r = load_firmware_data(*file, &fwd); + r = load_firmware_data(*file, &fwd, ret_firmware_json ? &json : NULL); if (r < 0) { log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file); continue; @@ -428,6 +437,10 @@ int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig ** return r; log_debug("Selected firmware definition %s.", *file); + + if (ret_firmware_json) + *ret_firmware_json = TAKE_PTR(json); + break; } diff --git a/src/vmspawn/vmspawn-util.h b/src/vmspawn/vmspawn-util.h index 28c2df78a70..90efd936612 100644 --- a/src/vmspawn/vmspawn-util.h +++ b/src/vmspawn/vmspawn-util.h @@ -89,7 +89,7 @@ int qemu_check_vsock_support(void); int list_ovmf_config(char ***ret); int list_ovmf_firmware_features(char ***ret); int load_ovmf_config(const char *path, OvmfConfig **ret); -int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret); +int find_ovmf_config(Set *features_include, Set *features_exclude, OvmfConfig **ret, sd_json_variant **ret_firmware_json); int find_qemu_binary(char **ret_qemu_binary); int vsock_fix_child_cid(int vhost_device_fd, unsigned *machine_cid, const char *machine); diff --git a/src/vmspawn/vmspawn.c b/src/vmspawn/vmspawn.c index fbe4d2145bf..02f2b0df2e0 100644 --- a/src/vmspawn/vmspawn.c +++ b/src/vmspawn/vmspawn.c @@ -138,6 +138,7 @@ static MachineCredentialContext arg_credentials = {}; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; static RuntimeMountContext arg_runtime_mounts = {}; static char *arg_firmware = NULL; +static bool arg_firmware_describe = false; static Set *arg_firmware_features_include = NULL; static Set *arg_firmware_features_exclude = NULL; static char *arg_forward_journal = NULL; @@ -239,7 +240,9 @@ static int help(void) { " --network-user-mode Use user mode networking\n" " --secure-boot=BOOL|auto\n" " Enable searching for firmware supporting SecureBoot\n" - " --firmware=PATH|list Select firmware definition file (or list available)\n" + " --firmware=PATH|list|describe\n" + " Select firmware definition file (or list/describe\n" + " available)\n" " --firmware-features=FEATURE[,FEATURE...]|list\n" " Require/exclude specific firmware features\n" " --discard-disk=BOOL Control processing of discard requests\n" @@ -737,6 +740,16 @@ static int parse_argv(int argc, char *argv[]) { return 0; } + if (streq(optarg, "describe")) { + /* Handled after argument parsing so that --firmware-features= is + * taken into account. */ + arg_firmware = mfree(arg_firmware); + arg_firmware_describe = true; + break; + } + + arg_firmware_describe = false; + if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./")) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Absolute path or path starting with './' required."); @@ -2171,7 +2184,7 @@ static int run_virtual_machine(int kvm_device_fd, int vhost_device_fd) { if (arg_firmware) r = load_ovmf_config(arg_firmware, &ovmf_config); else - r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config); + r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config, /* ret_firmware_json= */ NULL); if (r < 0) return log_error_errno(r, "Failed to find OVMF config: %m"); @@ -3637,6 +3650,21 @@ static int run(int argc, char *argv[]) { if (r <= 0) return r; + if (arg_firmware_describe) { + _cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL; + _cleanup_(sd_json_variant_unrefp) sd_json_variant *json = NULL; + + r = find_ovmf_config(arg_firmware_features_include, arg_firmware_features_exclude, &ovmf_config, &json); + if (r < 0) + return log_error_errno(r, "Failed to find OVMF config: %m"); + + r = sd_json_variant_dump(json, SD_JSON_FORMAT_PRETTY|SD_JSON_FORMAT_COLOR_AUTO, stdout, /* prefix= */ NULL); + if (r < 0) + return log_error_errno(r, "Failed to output JSON: %m"); + + return 0; + } + r = determine_names(); if (r < 0) return r;