From: r-vdp Date: Wed, 1 Jul 2026 12:16:09 +0000 (+0200) Subject: bootctl: accept espPath/xbootldrPath in Varlink methods X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=ca7fd21cf34a7c697e49d78eb58393adb5282ac6;p=thirdparty%2Fsystemd.git bootctl: accept espPath/xbootldrPath in Varlink methods The CLI verbs have --esp-path/--boot-path but the Varlink methods always auto-discover the partitions, so callers that mount the ESP or XBOOTLDR at a non-standard location have to fall back to the SYSTEMD_ESP_PATH/SYSTEMD_XBOOTLDR_PATH environment variables. Allow to specify the paths when calling Install/Unlink/Link/LinkAuto so the Varlink API is on par with the CLI. --- diff --git a/src/bootctl/bootctl-install.c b/src/bootctl/bootctl-install.c index f8ba48a485b..5015f4c8072 100644 --- a/src/bootctl/bootctl-install.c +++ b/src/bootctl/bootctl-install.c @@ -2085,12 +2085,16 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT typedef struct InstallParameters { InstallContext context; unsigned root_fd_index; + char *esp_path; + char *xbootldr_path; } InstallParameters; static void install_parameters_done(InstallParameters *p) { assert(p); install_context_done(&p->context); + free(p->esp_path); + free(p->xbootldr_path); } int vl_method_install( @@ -2113,6 +2117,8 @@ int vl_method_install( { "graceful", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, context.graceful), 0 }, { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 }, { "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 }, + { "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 }, + { "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 }, { "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 }, { "touchVariables", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, context.touch_variables), 0 }, {}, @@ -2162,7 +2168,7 @@ int vl_method_install( r = find_esp_and_warn_at_full( p.context.root_fd, - /* path= */ NULL, + p.esp_path, /* unprivileged_mode= */ false, &p.context.esp_path, &p.context.esp_fd, @@ -2178,7 +2184,7 @@ int vl_method_install( r = find_xbootldr_and_warn_at( p.context.root_fd, - /* path= */ NULL, + p.xbootldr_path, /* unprivileged_mode= */ false, &p.context.xbootldr_path, &p.context.xbootldr_fd); diff --git a/src/bootctl/bootctl-link.c b/src/bootctl/bootctl-link.c index af0955b64ab..b4706670857 100644 --- a/src/bootctl/bootctl-link.c +++ b/src/bootctl/bootctl-link.c @@ -1355,6 +1355,8 @@ typedef struct LinkParameters { LinkContext context; unsigned root_fd_index; unsigned kernel_fd_index; + char *esp_path; + char *xbootldr_path; sd_varlink *link; } LinkParameters; @@ -1362,6 +1364,8 @@ static void link_parameters_done(LinkParameters *p) { assert(p); link_context_done(&p->context); + free(p->esp_path); + free(p->xbootldr_path); } typedef struct ExtraParameters { @@ -1509,7 +1513,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) { r = find_xbootldr_and_warn_at( p->context.root_fd, - /* path= */ NULL, + p->xbootldr_path, /* unprivileged_mode= */ false, &p->context.dollar_boot_path, &p->context.dollar_boot_fd); @@ -1521,7 +1525,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) { r = find_esp_and_warn_at( p->context.root_fd, - /* path= */ NULL, + p->esp_path, /* unprivileged_mode= */ false, &p->context.dollar_boot_path, &p->context.dollar_boot_fd); @@ -1566,6 +1570,8 @@ int vl_method_link( static const sd_json_dispatch_field dispatch_table[] = { { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 }, { "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 }, + { "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 }, + { "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 }, { "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 }, { "entryTitle", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_title), 0 }, { "entryVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_version), 0 }, @@ -1634,6 +1640,8 @@ int vl_method_link_auto( static const sd_json_dispatch_field dispatch_table[] = { { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 }, { "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 }, + { "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 }, + { "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 }, { "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 }, { "entryTitle", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_title), 0 }, { "entryVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_version), 0 }, diff --git a/src/bootctl/bootctl-unlink.c b/src/bootctl/bootctl-unlink.c index 44954d4fa33..dabe698bc7c 100644 --- a/src/bootctl/bootctl-unlink.c +++ b/src/bootctl/bootctl-unlink.c @@ -550,6 +550,8 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT typedef struct UnlinkParameters { UnlinkContext context; unsigned root_fd_index; + char *esp_path; + char *xbootldr_path; const char *id; bool oldest; } UnlinkParameters; @@ -558,6 +560,8 @@ static void unlink_parameters_done(UnlinkParameters *p) { assert(p); unlink_context_done(&p->context); + free(p->esp_path); + free(p->xbootldr_path); } int vl_method_unlink( @@ -578,6 +582,8 @@ int vl_method_unlink( static const sd_json_dispatch_field dispatch_table[] = { { "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 }, { "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 }, + { "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 }, + { "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 }, { "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 }, { "id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), 0 }, { "oldest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, oldest), 0 }, @@ -631,7 +637,7 @@ int vl_method_unlink( r = find_esp_and_warn_at_full( p.context.root_fd, - /* path= */ NULL, + p.esp_path, /* unprivileged_mode= */ false, &p.context.esp_path, &p.context.esp_fd, @@ -644,7 +650,7 @@ int vl_method_unlink( return r; r = find_xbootldr_and_warn_at_full( p.context.root_fd, - /* path= */ NULL, + p.xbootldr_path, /* unprivileged_mode= */ false, &p.context.xbootldr_path, &p.context.xbootldr_fd, diff --git a/src/shared/varlink-io.systemd.BootControl.c b/src/shared/varlink-io.systemd.BootControl.c index 0be93990d9a..8df9e335e5b 100644 --- a/src/shared/varlink-io.systemd.BootControl.c +++ b/src/shared/varlink-io.systemd.BootControl.c @@ -131,6 +131,10 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."), SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"), SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("If true the boot loader will be registered in an EFI boot entry via EFI variables, otherwise this is omitted"), @@ -142,6 +146,10 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."), SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"), SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("The ID of the boot loader entry to remove."), @@ -164,6 +172,10 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."), SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"), SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"), @@ -191,6 +203,10 @@ static SD_VARLINK_DEFINE_METHOD( SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Root directory to operate relative to. If both this and rootFileDescriptor is specified, this is purely informational. If only this is specified, it is what will be used."), SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the EFI System Partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the ESP is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), + SD_VARLINK_FIELD_COMMENT("Path to the XBOOTLDR partition mount point, resolved relative to rootDirectory/rootFileDescriptor if either is set. If not specified the XBOOTLDR partition is auto-discovered."), + SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"), SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE), SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"), diff --git a/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh b/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh index 0b19acd0173..99fa39da40b 100755 --- a/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh +++ b/test/units/TEST-87-AUX-UTILS-VM.bootctl.sh @@ -425,18 +425,43 @@ remove_root_dir() { rm -rf "$ROOTDIR" } +cleanup_install_varlink() { + if [[ -n "${FAKE_ESP:-}" ]]; then + rm -rf "$FAKE_ESP" + unset FAKE_ESP + fi + if [[ -n "${FAKE_BOOT:-}" ]]; then + rm -rf "$FAKE_BOOT" + unset FAKE_BOOT + fi + restore_esp +} + testcase_install_varlink() { varlinkctl introspect "$(type -p bootctl)" if [ $# -eq 0 ]; then backup_esp - trap restore_esp RETURN ERR + trap cleanup_install_varlink RETURN ERR fi (! bootctl is-installed ) SYSTEMD_LOG_TARGET=console varlinkctl call "$(type -p bootctl)" io.systemd.BootControl.Install "{\"operation\":\"new\",\"touchVariables\":false}" bootctl is-installed + + # Verify that espPath/xbootldrPath override auto-discovery: install into fresh empty + # directories (with relaxed checks so verify_esp()/verify_xbootldr() accept a non-vfat + # non-mountpoint path) and check the loader files land there. If the parameters were ignored + # the call would auto-discover the real partitions instead and the directories would stay + # empty. + FAKE_ESP="$(mktemp --directory /tmp/test-bootctl-esp.XXXXXXXXXX)" + FAKE_BOOT="$(mktemp --directory /tmp/test-bootctl-boot.XXXXXXXXXX)" + SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes SYSTEMD_LOG_TARGET=console \ + varlinkctl call --quiet "$(type -p bootctl)" io.systemd.BootControl.Install \ + "{\"operation\":\"new\",\"touchVariables\":false,\"espPath\":\"$FAKE_ESP\",\"xbootldrPath\":\"$FAKE_BOOT\"}" + test -f "$FAKE_ESP/EFI/systemd/systemd-boot$(bootctl --print-efi-architecture).efi" + test -f "$FAKE_BOOT/loader/entries.srel" } cleanup_link() {