Skip to content

Commit b19ef18

Browse files
authored
bootctl: expose --esp-path/--boot-path/--make-entry-directory via Varlink (systemd#42838)
The Install/Unlink/Link/LinkAuto Varlink methods always auto-discover the ESP and XBOOTLDR partitions and Install always runs make-entry-directory in auto mode, so IPC callers cannot match what the CLI verbs do with `--esp-path`, `--boot-path` and `--make-entry-directory`. Add optional `espPath`, `xbootldrPath` and `makeEntryDirectory` parameters that feed into the same code paths the CLI already uses.
2 parents 805742a + e2cda71 commit b19ef18

5 files changed

Lines changed: 93 additions & 13 deletions

File tree

src/bootctl/bootctl-install.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2085,12 +2085,16 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT
20852085
typedef struct InstallParameters {
20862086
InstallContext context;
20872087
unsigned root_fd_index;
2088+
char *esp_path;
2089+
char *xbootldr_path;
20882090
} InstallParameters;
20892091

20902092
static void install_parameters_done(InstallParameters *p) {
20912093
assert(p);
20922094

20932095
install_context_done(&p->context);
2096+
free(p->esp_path);
2097+
free(p->xbootldr_path);
20942098
}
20952099

20962100
int vl_method_install(
@@ -2109,12 +2113,15 @@ int vl_method_install(
21092113
};
21102114

21112115
static const sd_json_dispatch_field dispatch_table[] = {
2112-
{ "operation", SD_JSON_VARIANT_STRING, json_dispatch_install_operation, voffsetof(p, context.operation), SD_JSON_MANDATORY },
2113-
{ "graceful", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, context.graceful), 0 },
2114-
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
2115-
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
2116-
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
2117-
{ "touchVariables", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, context.touch_variables), 0 },
2116+
{ "operation", SD_JSON_VARIANT_STRING, json_dispatch_install_operation, voffsetof(p, context.operation), SD_JSON_MANDATORY },
2117+
{ "graceful", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, context.graceful), 0 },
2118+
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
2119+
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
2120+
{ "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 },
2121+
{ "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 },
2122+
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
2123+
{ "makeEntryDirectory", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, context.make_entry_directory), 0 },
2124+
{ "touchVariables", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, voffsetof(p, context.touch_variables), 0 },
21182125
{},
21192126
};
21202127

@@ -2162,7 +2169,7 @@ int vl_method_install(
21622169

21632170
r = find_esp_and_warn_at_full(
21642171
p.context.root_fd,
2165-
/* path= */ NULL,
2172+
p.esp_path,
21662173
/* unprivileged_mode= */ false,
21672174
&p.context.esp_path,
21682175
&p.context.esp_fd,
@@ -2178,7 +2185,7 @@ int vl_method_install(
21782185

21792186
r = find_xbootldr_and_warn_at(
21802187
p.context.root_fd,
2181-
/* path= */ NULL,
2188+
p.xbootldr_path,
21822189
/* unprivileged_mode= */ false,
21832190
&p.context.xbootldr_path,
21842191
&p.context.xbootldr_fd);

src/bootctl/bootctl-link.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1355,13 +1355,17 @@ typedef struct LinkParameters {
13551355
LinkContext context;
13561356
unsigned root_fd_index;
13571357
unsigned kernel_fd_index;
1358+
char *esp_path;
1359+
char *xbootldr_path;
13581360
sd_varlink *link;
13591361
} LinkParameters;
13601362

13611363
static void link_parameters_done(LinkParameters *p) {
13621364
assert(p);
13631365

13641366
link_context_done(&p->context);
1367+
free(p->esp_path);
1368+
free(p->xbootldr_path);
13651369
}
13661370

13671371
typedef struct ExtraParameters {
@@ -1509,7 +1513,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) {
15091513

15101514
r = find_xbootldr_and_warn_at(
15111515
p->context.root_fd,
1512-
/* path= */ NULL,
1516+
p->xbootldr_path,
15131517
/* unprivileged_mode= */ false,
15141518
&p->context.dollar_boot_path,
15151519
&p->context.dollar_boot_fd);
@@ -1521,7 +1525,7 @@ static int vl_link_finish(sd_varlink *link, LinkParameters *p, bool with_ids) {
15211525

15221526
r = find_esp_and_warn_at(
15231527
p->context.root_fd,
1524-
/* path= */ NULL,
1528+
p->esp_path,
15251529
/* unprivileged_mode= */ false,
15261530
&p->context.dollar_boot_path,
15271531
&p->context.dollar_boot_fd);
@@ -1566,6 +1570,8 @@ int vl_method_link(
15661570
static const sd_json_dispatch_field dispatch_table[] = {
15671571
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
15681572
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
1573+
{ "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 },
1574+
{ "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 },
15691575
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
15701576
{ "entryTitle", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_title), 0 },
15711577
{ "entryVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_version), 0 },
@@ -1634,6 +1640,8 @@ int vl_method_link_auto(
16341640
static const sd_json_dispatch_field dispatch_table[] = {
16351641
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
16361642
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
1643+
{ "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 },
1644+
{ "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 },
16371645
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
16381646
{ "entryTitle", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_title), 0 },
16391647
{ "entryVersion", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, voffsetof(p, context.entry_version), 0 },

src/bootctl/bootctl-unlink.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,8 @@ static JSON_DISPATCH_ENUM_DEFINE(json_dispatch_boot_entry_token_type, BootEntryT
550550
typedef struct UnlinkParameters {
551551
UnlinkContext context;
552552
unsigned root_fd_index;
553+
char *esp_path;
554+
char *xbootldr_path;
553555
const char *id;
554556
bool oldest;
555557
} UnlinkParameters;
@@ -558,6 +560,8 @@ static void unlink_parameters_done(UnlinkParameters *p) {
558560
assert(p);
559561

560562
unlink_context_done(&p->context);
563+
free(p->esp_path);
564+
free(p->xbootldr_path);
561565
}
562566

563567
int vl_method_unlink(
@@ -578,6 +582,8 @@ int vl_method_unlink(
578582
static const sd_json_dispatch_field dispatch_table[] = {
579583
{ "rootFileDescriptor", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint, voffsetof(p, root_fd_index), 0 },
580584
{ "rootDirectory", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, context.root), 0 },
585+
{ "espPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, esp_path), 0 },
586+
{ "xbootldrPath", SD_JSON_VARIANT_STRING, json_dispatch_path, voffsetof(p, xbootldr_path), 0 },
581587
{ "bootEntryTokenType", SD_JSON_VARIANT_STRING, json_dispatch_boot_entry_token_type, voffsetof(p, context.entry_token_type), 0 },
582588
{ "id", SD_JSON_VARIANT_STRING, sd_json_dispatch_const_string, voffsetof(p, id), 0 },
583589
{ "oldest", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, voffsetof(p, oldest), 0 },
@@ -631,7 +637,7 @@ int vl_method_unlink(
631637

632638
r = find_esp_and_warn_at_full(
633639
p.context.root_fd,
634-
/* path= */ NULL,
640+
p.esp_path,
635641
/* unprivileged_mode= */ false,
636642
&p.context.esp_path,
637643
&p.context.esp_fd,
@@ -644,7 +650,7 @@ int vl_method_unlink(
644650
return r;
645651
r = find_xbootldr_and_warn_at_full(
646652
p.context.root_fd,
647-
/* path= */ NULL,
653+
p.xbootldr_path,
648654
/* unprivileged_mode= */ false,
649655
&p.context.xbootldr_path,
650656
&p.context.xbootldr_fd,

src/shared/varlink-io.systemd.BootControl.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,14 @@ static SD_VARLINK_DEFINE_METHOD(
131131
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
132132
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."),
133133
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
134+
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."),
135+
SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
136+
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."),
137+
SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
134138
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
135139
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
140+
SD_VARLINK_FIELD_COMMENT("Whether to create the '$BOOT/ENTRY-TOKEN/' directory for Boot Loader Specification Type #1 snippets. If not specified this is decided automatically."),
141+
SD_VARLINK_DEFINE_INPUT(makeEntryDirectory, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE),
136142
SD_VARLINK_FIELD_COMMENT("If true the boot loader will be registered in an EFI boot entry via EFI variables, otherwise this is omitted"),
137143
SD_VARLINK_DEFINE_INPUT(touchVariables, SD_VARLINK_BOOL, SD_VARLINK_NULLABLE));
138144

@@ -142,6 +148,10 @@ static SD_VARLINK_DEFINE_METHOD(
142148
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
143149
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."),
144150
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
151+
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."),
152+
SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
153+
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."),
154+
SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
145155
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
146156
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
147157
SD_VARLINK_FIELD_COMMENT("The ID of the boot loader entry to remove."),
@@ -164,6 +174,10 @@ static SD_VARLINK_DEFINE_METHOD(
164174
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
165175
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."),
166176
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
177+
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."),
178+
SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
179+
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."),
180+
SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
167181
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
168182
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
169183
SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"),
@@ -191,6 +205,10 @@ static SD_VARLINK_DEFINE_METHOD(
191205
SD_VARLINK_DEFINE_INPUT(rootFileDescriptor, SD_VARLINK_INT, SD_VARLINK_NULLABLE),
192206
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."),
193207
SD_VARLINK_DEFINE_INPUT(rootDirectory, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
208+
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."),
209+
SD_VARLINK_DEFINE_INPUT(espPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
210+
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."),
211+
SD_VARLINK_DEFINE_INPUT(xbootldrPath, SD_VARLINK_STRING, SD_VARLINK_NULLABLE),
194212
SD_VARLINK_FIELD_COMMENT("Selects how to identify boot entries"),
195213
SD_VARLINK_DEFINE_INPUT_BY_TYPE(bootEntryTokenType, BootEntryTokenType, SD_VARLINK_NULLABLE),
196214
SD_VARLINK_FIELD_COMMENT("The entry title for the newly created boot menu entry"),

test/units/TEST-87-AUX-UTILS-VM.bootctl.sh

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,18 +425,59 @@ remove_root_dir() {
425425
rm -rf "$ROOTDIR"
426426
}
427427

428+
cleanup_install_varlink() {
429+
if [[ -n "${FAKE_ESP:-}" ]]; then
430+
rm -rf "$FAKE_ESP"
431+
unset FAKE_ESP
432+
fi
433+
if [[ -n "${FAKE_BOOT:-}" ]]; then
434+
rm -rf "$FAKE_BOOT"
435+
unset FAKE_BOOT
436+
fi
437+
restore_esp
438+
}
439+
428440
testcase_install_varlink() {
429441

430442
varlinkctl introspect "$(type -p bootctl)"
431443

432444
if [ $# -eq 0 ]; then
433445
backup_esp
434-
trap restore_esp RETURN ERR
446+
trap cleanup_install_varlink RETURN ERR
435447
fi
436448

437449
(! bootctl is-installed )
438450
SYSTEMD_LOG_TARGET=console varlinkctl call "$(type -p bootctl)" io.systemd.BootControl.Install "{\"operation\":\"new\",\"touchVariables\":false}"
439451
bootctl is-installed
452+
453+
# Verify that espPath/xbootldrPath override auto-discovery: install into fresh empty
454+
# directories (with relaxed checks so verify_esp()/verify_xbootldr() accept a non-vfat
455+
# non-mountpoint path) and check the loader files land there. If the parameters were ignored
456+
# the call would auto-discover the real partitions instead and the directories would stay
457+
# empty.
458+
FAKE_ESP="$(mktemp --directory /tmp/test-bootctl-esp.XXXXXXXXXX)"
459+
FAKE_BOOT="$(mktemp --directory /tmp/test-bootctl-boot.XXXXXXXXXX)"
460+
SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes SYSTEMD_LOG_TARGET=console \
461+
varlinkctl call --quiet "$(type -p bootctl)" io.systemd.BootControl.Install \
462+
"{\"operation\":\"new\",\"touchVariables\":false,\"espPath\":\"$FAKE_ESP\",\"xbootldrPath\":\"$FAKE_BOOT\",\"makeEntryDirectory\":false}"
463+
test -f "$FAKE_ESP/EFI/systemd/systemd-boot$(bootctl --print-efi-architecture).efi"
464+
test -f "$FAKE_BOOT/loader/entries.srel"
465+
# makeEntryDirectory:false means loader.conf must not gain a "default" line and no entry
466+
# token directory is created under $BOOT.
467+
(! grep '^default ' "$FAKE_ESP/loader/loader.conf" >/dev/null)
468+
469+
# Same again into fresh directories with makeEntryDirectory:true, and check loader.conf now
470+
# gets a "default <entry-token>-*" line and the entry token directory shows up under $BOOT.
471+
rm -rf "$FAKE_ESP" "$FAKE_BOOT"
472+
FAKE_ESP="$(mktemp --directory /tmp/test-bootctl-esp.XXXXXXXXXX)"
473+
FAKE_BOOT="$(mktemp --directory /tmp/test-bootctl-boot.XXXXXXXXXX)"
474+
SYSTEMD_RELAX_ESP_CHECKS=yes SYSTEMD_RELAX_XBOOTLDR_CHECKS=yes SYSTEMD_LOG_TARGET=console \
475+
varlinkctl call --quiet "$(type -p bootctl)" io.systemd.BootControl.Install \
476+
"{\"operation\":\"new\",\"touchVariables\":false,\"espPath\":\"$FAKE_ESP\",\"xbootldrPath\":\"$FAKE_BOOT\",\"makeEntryDirectory\":true}"
477+
local TOKEN
478+
TOKEN="$(sed -n 's/^default \(.*\)-\*$/\1/p' "$FAKE_ESP/loader/loader.conf")"
479+
test -n "$TOKEN"
480+
test -d "$FAKE_BOOT/$TOKEN"
440481
}
441482

442483
cleanup_link() {

0 commit comments

Comments
 (0)