1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
4 #include "bootctl-install.h"
5 #include "bootctl-random-seed.h"
6 #include "bootctl-util.h"
7 #include "chase-symlinks.h"
13 #include "glyph-util.h"
15 #include "path-util.h"
16 #include "stat-util.h"
17 #include "sync-util.h"
18 #include "tmpfile-util.h"
19 #include "umask-util.h"
21 #include "dirent-util.h"
25 static int load_etc_machine_id(void) {
28 r
= sd_id128_get_machine(&arg_machine_id
);
29 if (IN_SET(r
, -ENOENT
, -ENOMEDIUM
, -ENOPKG
)) /* Not set or empty */
32 return log_error_errno(r
, "Failed to get machine-id: %m");
34 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id
));
38 static int load_etc_machine_info(void) {
39 /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
40 * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
41 * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
42 * has been deprecated and is only returned for compatibility. */
43 _cleanup_free_
char *s
= NULL
, *layout
= NULL
;
46 r
= parse_env_file(NULL
, "/etc/machine-info",
47 "KERNEL_INSTALL_LAYOUT", &layout
,
48 "KERNEL_INSTALL_MACHINE_ID", &s
);
52 return log_error_errno(r
, "Failed to parse /etc/machine-info: %m");
56 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
57 "Please move it to /etc/kernel/entry-token.");
59 r
= sd_id128_from_string(s
, &arg_machine_id
);
61 return log_error_errno(r
, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s
);
63 log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.",
64 SD_ID128_TO_STRING(arg_machine_id
));
67 if (!isempty(layout
)) {
69 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
70 "Please move it to the layout= setting of /etc/kernel/install.conf.");
72 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout
);
73 free_and_replace(arg_install_layout
, layout
);
79 static int load_etc_kernel_install_conf(void) {
80 _cleanup_free_
char *layout
= NULL
;
83 r
= parse_env_file(NULL
, "/etc/kernel/install.conf",
88 return log_error_errno(r
, "Failed to parse /etc/kernel/install.conf: %m");
90 if (!isempty(layout
)) {
91 log_debug("layout=%s is specified in /etc/machine-info.", layout
);
92 free_and_replace(arg_install_layout
, layout
);
98 static int settle_entry_token(void) {
101 switch (arg_entry_token_type
) {
103 case ARG_ENTRY_TOKEN_AUTO
: {
104 _cleanup_free_
char *buf
= NULL
;
105 r
= read_one_line_file("/etc/kernel/entry-token", &buf
);
106 if (r
< 0 && r
!= -ENOENT
)
107 return log_error_errno(r
, "Failed to read /etc/kernel/entry-token: %m");
110 free_and_replace(arg_entry_token
, buf
);
111 arg_entry_token_type
= ARG_ENTRY_TOKEN_LITERAL
;
112 } else if (sd_id128_is_null(arg_machine_id
)) {
113 _cleanup_free_
char *id
= NULL
, *image_id
= NULL
;
115 r
= parse_os_release(NULL
,
116 "IMAGE_ID", &image_id
,
119 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
121 if (!isempty(image_id
)) {
122 free_and_replace(arg_entry_token
, image_id
);
123 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_IMAGE_ID
;
124 } else if (!isempty(id
)) {
125 free_and_replace(arg_entry_token
, id
);
126 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_ID
;
128 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
130 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
134 arg_entry_token_type
= ARG_ENTRY_TOKEN_MACHINE_ID
;
140 case ARG_ENTRY_TOKEN_MACHINE_ID
:
141 if (sd_id128_is_null(arg_machine_id
))
142 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set.");
144 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
150 case ARG_ENTRY_TOKEN_OS_IMAGE_ID
: {
151 _cleanup_free_
char *buf
= NULL
;
153 r
= parse_os_release(NULL
, "IMAGE_ID", &buf
);
155 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
158 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "IMAGE_ID= field not set in /etc/os-release.");
160 free_and_replace(arg_entry_token
, buf
);
164 case ARG_ENTRY_TOKEN_OS_ID
: {
165 _cleanup_free_
char *buf
= NULL
;
167 r
= parse_os_release(NULL
, "ID", &buf
);
169 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
172 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "ID= field not set in /etc/os-release.");
174 free_and_replace(arg_entry_token
, buf
);
178 case ARG_ENTRY_TOKEN_LITERAL
:
179 assert(!isempty(arg_entry_token
)); /* already filled in by command line parser */
183 if (isempty(arg_entry_token
) || !(utf8_is_valid(arg_entry_token
) && string_is_safe(arg_entry_token
)))
184 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Selected entry token not valid: %s", arg_entry_token
);
186 log_debug("Using entry token: %s", arg_entry_token
);
190 static bool use_boot_loader_spec_type1(void) {
191 /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
192 * Specification Type #1 is the chosen format for our boot loader entries */
193 return !arg_install_layout
|| streq(arg_install_layout
, "bls");
196 static int settle_make_entry_directory(void) {
199 r
= load_etc_machine_id();
203 r
= load_etc_machine_info();
207 r
= load_etc_kernel_install_conf();
211 r
= settle_entry_token();
215 bool layout_type1
= use_boot_loader_spec_type1();
216 if (arg_make_entry_directory
< 0) { /* Automatic mode */
218 if (arg_entry_token
== ARG_ENTRY_TOKEN_MACHINE_ID
) {
219 r
= path_is_temporary_fs("/etc/machine-id");
221 return log_debug_errno(r
, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
223 arg_make_entry_directory
= r
== 0;
225 arg_make_entry_directory
= true;
227 arg_make_entry_directory
= false;
230 if (arg_make_entry_directory
> 0 && !layout_type1
)
231 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
232 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
238 static int compare_product(const char *a
, const char *b
) {
247 return x
< y
? -1 : x
> y
? 1 : 0;
249 return strncmp(a
, b
, x
);
252 static int compare_version(const char *a
, const char *b
) {
256 a
+= strcspn(a
, " ");
258 b
+= strcspn(b
, " ");
261 return strverscmp_improved(a
, b
);
264 static int version_check(int fd_from
, const char *from
, int fd_to
, const char *to
) {
265 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
268 assert(fd_from
>= 0);
273 r
= get_file_version(fd_from
, &a
);
277 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
278 "Source file \"%s\" does not carry version information!",
281 r
= get_file_version(fd_to
, &b
);
284 if (r
== 0 || compare_product(a
, b
) != 0)
285 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
286 "Skipping \"%s\", since it's owned by another boot loader.",
289 r
= compare_version(a
, b
);
290 log_debug("Comparing versions: \"%s\" %s \"%s", a
, comparison_operator(r
), b
);
292 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE
),
293 "Skipping \"%s\", since newer boot loader version in place already.", to
);
295 return log_info_errno(SYNTHETIC_ERRNO(ESTALE
),
296 "Skipping \"%s\", since same boot loader version in place already.", to
);
301 static int copy_file_with_version_check(const char *from
, const char *to
, bool force
) {
302 _cleanup_close_
int fd_from
= -1, fd_to
= -1;
303 _cleanup_free_
char *t
= NULL
;
306 fd_from
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
308 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", from
);
311 fd_to
= open(to
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
314 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", to
);
316 r
= version_check(fd_from
, from
, fd_to
, to
);
320 if (lseek(fd_from
, 0, SEEK_SET
) == (off_t
) -1)
321 return log_error_errno(errno
, "Failed to seek in \"%s\": %m", from
);
323 fd_to
= safe_close(fd_to
);
327 r
= tempfn_random(to
, NULL
, &t
);
332 fd_to
= open(t
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_EXCL
|O_NOFOLLOW
, 0644);
334 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", t
);
337 r
= copy_bytes(fd_from
, fd_to
, UINT64_MAX
, COPY_REFLINK
);
340 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
343 (void) copy_times(fd_from
, fd_to
, 0);
345 r
= fsync_full(fd_to
);
347 (void) unlink_noerrno(t
);
348 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
351 if (renameat(AT_FDCWD
, t
, AT_FDCWD
, to
) < 0) {
352 (void) unlink_noerrno(t
);
353 return log_error_errno(errno
, "Failed to rename \"%s\" to \"%s\": %m", t
, to
);
356 log_info("Copied \"%s\" to \"%s\".", from
, to
);
361 static int mkdir_one(const char *prefix
, const char *suffix
) {
362 _cleanup_free_
char *p
= NULL
;
364 p
= path_join(prefix
, suffix
);
365 if (mkdir(p
, 0700) < 0) {
367 return log_error_errno(errno
, "Failed to create \"%s\": %m", p
);
369 log_info("Created \"%s\".", p
);
374 static const char *const esp_subdirs
[] = {
375 /* The directories to place in the ESP */
383 static const char *const dollar_boot_subdirs
[] = {
384 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
386 "loader/entries", /* Type #1 entries */
388 "EFI/Linux", /* Type #2 entries */
392 static int create_subdirs(const char *root
, const char * const *subdirs
) {
395 STRV_FOREACH(i
, subdirs
) {
396 r
= mkdir_one(root
, *i
);
405 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
406 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
407 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
412 dest_name
= strdupa_safe(name
);
413 s
= endswith_no_case(dest_name
, ".signed");
417 p
= path_join(BOOTLIBDIR
, name
);
421 r
= chase_symlinks(p
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
422 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
423 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
424 r
= chase_symlinks(p
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &source_path
, NULL
);
426 return log_error_errno(r
,
427 "Failed to resolve path %s%s%s: %m",
429 root
? " under directory " : "",
432 q
= path_join("/EFI/systemd/", dest_name
);
436 r
= chase_symlinks(q
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &dest_path
, NULL
);
438 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
440 /* Note that if this fails we do the second copy anyway, but return this error code,
441 * so we stash it away in a separate variable. */
442 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
444 e
= startswith(dest_name
, "systemd-boot");
446 _cleanup_free_
char *default_dest_path
= NULL
;
449 /* Create the EFI default boot loader name (specified for removable devices) */
450 v
= strjoina("/EFI/BOOT/BOOT", e
);
451 ascii_strupper(strrchr(v
, '/') + 1);
453 r
= chase_symlinks(v
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
|CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
455 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
457 r
= copy_file_with_version_check(source_path
, default_dest_path
, force
);
458 if (r
< 0 && ret
== 0)
465 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
466 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
467 _cleanup_closedir_
DIR *d
= NULL
;
468 _cleanup_free_
char *path
= NULL
;
471 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
472 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
473 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
474 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &path
, &d
);
476 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
478 const char *suffix
= strjoina(arch
, ".efi");
479 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
481 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
484 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
487 /* skip the .efi file, if there's a .signed version of it */
488 if (endswith_no_case(de
->d_name
, ".efi")) {
489 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
492 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
496 k
= copy_one_file(esp_path
, de
->d_name
, force
);
497 /* Don't propagate an error code if no update necessary, installed version already equal or
498 * newer version, or other boot loader in place. */
499 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -EREMOTE
))
508 static int install_loader_config(const char *esp_path
) {
509 _cleanup_(unlink_and_freep
) char *t
= NULL
;
510 _cleanup_fclose_
FILE *f
= NULL
;
514 assert(arg_make_entry_directory
>= 0);
516 p
= prefix_roota(esp_path
, "/loader/loader.conf");
517 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
520 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
522 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
524 fprintf(f
, "#timeout 3\n"
525 "#console-mode keep\n");
527 if (arg_make_entry_directory
) {
528 assert(arg_entry_token
);
529 fprintf(f
, "default %s-*\n", arg_entry_token
);
532 r
= flink_tmpfile(f
, t
, p
);
534 return 0; /* Silently skip creation if the file exists now (recheck) */
536 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
542 static int install_loader_specification(const char *root
) {
543 _cleanup_(unlink_and_freep
) char *t
= NULL
;
544 _cleanup_fclose_
FILE *f
= NULL
;
545 _cleanup_free_
char *p
= NULL
;
548 p
= path_join(root
, "/loader/entries.srel");
552 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
555 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
557 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
559 fprintf(f
, "type1\n");
561 r
= flink_tmpfile(f
, t
, p
);
563 return 0; /* Silently skip creation if the file exists now (recheck) */
565 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
571 static int install_entry_directory(const char *root
) {
573 assert(arg_make_entry_directory
>= 0);
575 if (!arg_make_entry_directory
)
578 assert(arg_entry_token
);
579 return mkdir_one(root
, arg_entry_token
);
582 static int install_entry_token(void) {
585 assert(arg_make_entry_directory
>= 0);
586 assert(arg_entry_token
);
588 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
589 * directory, or if anything else but the machine ID */
591 if (!arg_make_entry_directory
&& arg_entry_token_type
== ARG_ENTRY_TOKEN_MACHINE_ID
)
594 r
= write_string_file("/etc/kernel/entry-token", arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
596 return log_error_errno(r
, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token
);
601 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
602 _cleanup_free_
char *opath
= NULL
;
606 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
609 if (!sd_id128_equal(uuid
, ouuid
))
612 /* Some motherboards convert the path to uppercase under certain circumstances
613 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
614 * so use case-insensitive checking */
615 if (!strcaseeq_ptr(path
, opath
))
621 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
622 _cleanup_free_
uint16_t *options
= NULL
;
624 int n
= efi_get_boot_options(&options
);
628 /* find already existing systemd-boot entry */
629 for (int i
= 0; i
< n
; i
++)
630 if (same_entry(options
[i
], uuid
, path
)) {
635 /* find free slot in the sorted BootXXXX variable list */
636 for (int i
= 0; i
< n
; i
++)
637 if (i
!= options
[i
]) {
642 /* use the next one */
649 static int insert_into_order(uint16_t slot
, bool first
) {
650 _cleanup_free_
uint16_t *order
= NULL
;
654 n
= efi_get_boot_order(&order
);
656 /* no entry, add us */
657 return efi_set_boot_order(&slot
, 1);
659 /* are we the first and only one? */
660 if (n
== 1 && order
[0] == slot
)
663 /* are we already in the boot order? */
664 for (int i
= 0; i
< n
; i
++) {
665 if (order
[i
] != slot
)
668 /* we do not require to be the first one, all is fine */
672 /* move us to the first slot */
673 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
675 return efi_set_boot_order(order
, n
);
679 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
684 /* add us to the top or end of the list */
686 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
691 return efi_set_boot_order(order
, n
+ 1);
694 static int remove_from_order(uint16_t slot
) {
695 _cleanup_free_
uint16_t *order
= NULL
;
698 n
= efi_get_boot_order(&order
);
702 for (int i
= 0; i
< n
; i
++) {
703 if (order
[i
] != slot
)
707 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
708 return efi_set_boot_order(order
, n
- 1);
714 static const char *pick_efi_boot_option_description(void) {
715 return arg_efi_boot_option_description
?: "Linux Boot Manager";
718 static int install_variables(
719 const char *esp_path
,
731 log_info("Acting on %s, skipping EFI variable setup.",
732 arg_image
? "image" : "root directory");
736 if (!is_efi_boot()) {
737 log_warning("Not booted with EFI, skipping EFI variable setup.");
741 r
= chase_symlinks_and_access(path
, esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, F_OK
, NULL
, NULL
);
745 return log_error_errno(r
, "Cannot access \"%s/%s\": %m", esp_path
, path
);
747 r
= find_slot(uuid
, path
, &slot
);
749 return log_error_errno(r
,
751 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
752 "Failed to determine current boot order: %m");
754 if (first
|| r
== 0) {
755 r
= efi_add_boot_option(slot
, pick_efi_boot_option_description(),
759 return log_error_errno(r
, "Failed to create EFI Boot variable entry: %m");
761 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
764 return insert_into_order(slot
, first
);
767 static int are_we_installed(const char *esp_path
) {
770 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
771 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
772 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
773 * should be a suitable and very minimal check for a number of reasons:
775 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
776 * not a specific one.)
778 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
779 * /EFI/BOOT/BOOT*.EFI fallback binary.
781 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
782 * /boot/loader/entries would do). */
784 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
788 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
789 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
790 if (r
< 0 && r
!= -ENOENT
)
791 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
796 int verb_install(int argc
, char *argv
[], void *userdata
) {
797 sd_id128_t uuid
= SD_ID128_NULL
;
798 uint64_t pstart
= 0, psize
= 0;
800 bool install
, graceful
;
803 /* Invoked for both "update" and "install" */
805 install
= streq(argv
[0], "install");
806 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
808 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
809 if (graceful
&& r
== -ENOKEY
)
810 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
815 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
816 r
= are_we_installed(arg_esp_path
);
820 log_debug("Skipping update because sd-boot is not installed in the ESP.");
825 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
829 r
= settle_make_entry_directory();
833 const char *arch
= arg_arch_all
? "" : get_efi_arch();
837 /* Don't create any of these directories when we are just updating. When we update
838 * we'll drop-in our files (unless there are newer ones already), but we won't create
839 * the directories for them in the first place. */
840 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
844 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
849 r
= install_binaries(arg_esp_path
, arch
, install
);
854 r
= install_loader_config(arg_esp_path
);
858 r
= install_entry_directory(arg_dollar_boot_path());
862 r
= install_entry_token();
866 r
= install_random_seed(arg_esp_path
);
871 r
= install_loader_specification(arg_dollar_boot_path());
876 (void) sync_everything();
878 if (!arg_touch_variables
)
882 log_info("Not changing EFI variables with --all-architectures.");
886 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
887 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
);
890 static int remove_boot_efi(const char *esp_path
) {
891 _cleanup_closedir_
DIR *d
= NULL
;
892 _cleanup_free_
char *p
= NULL
;
895 r
= chase_symlinks_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
|CHASE_PROHIBIT_SYMLINKS
, &p
, &d
);
899 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
901 FOREACH_DIRENT(de
, d
, break) {
902 _cleanup_close_
int fd
= -1;
903 _cleanup_free_
char *v
= NULL
;
905 if (!endswith_no_case(de
->d_name
, ".efi"))
908 if (!startswith_no_case(de
->d_name
, "boot"))
911 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
913 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
915 r
= get_file_version(fd
, &v
);
918 if (r
> 0 && startswith(v
, "systemd-boot ")) {
919 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
921 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
923 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
932 static int rmdir_one(const char *prefix
, const char *suffix
) {
935 p
= prefix_roota(prefix
, suffix
);
937 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
939 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
940 "Failed to remove directory \"%s\": %m", p
);
944 log_info("Removed \"%s\".", p
);
949 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
952 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
953 * short the array is. */
955 if (!subdirs
[0]) /* A the end of the list */
958 r
= remove_subdirs(root
, subdirs
+ 1);
959 q
= rmdir_one(root
, subdirs
[0]);
961 return r
< 0 ? r
: q
;
964 static int remove_entry_directory(const char *root
) {
966 assert(arg_make_entry_directory
>= 0);
968 if (!arg_make_entry_directory
|| !arg_entry_token
)
971 return rmdir_one(root
, arg_entry_token
);
974 static int remove_binaries(const char *esp_path
) {
978 p
= prefix_roota(esp_path
, "/EFI/systemd");
979 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
981 q
= remove_boot_efi(esp_path
);
988 static int remove_file(const char *root
, const char *file
) {
994 p
= prefix_roota(root
, file
);
996 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
997 "Failed to unlink file \"%s\": %m", p
);
999 return errno
== ENOENT
? 0 : -errno
;
1002 log_info("Removed \"%s\".", p
);
1006 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
1010 if (arg_root
|| !is_efi_boot())
1013 r
= find_slot(uuid
, path
, &slot
);
1017 r
= efi_remove_boot_option(slot
);
1022 return remove_from_order(slot
);
1027 static int remove_loader_variables(void) {
1030 /* Remove all persistent loader variables we define */
1033 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
1034 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
1035 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
1036 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
1037 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
1041 q
= efi_set_variable(var
, NULL
, 0);
1045 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
1049 log_info("Removed EFI variable %s.", var
);
1055 int verb_remove(int argc
, char *argv
[], void *userdata
) {
1056 sd_id128_t uuid
= SD_ID128_NULL
;
1059 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
1063 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
1067 r
= settle_make_entry_directory();
1071 r
= remove_binaries(arg_esp_path
);
1073 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
1074 if (q
< 0 && r
>= 0)
1077 q
= remove_file(arg_esp_path
, "/loader/random-seed");
1078 if (q
< 0 && r
>= 0)
1081 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
1082 if (q
< 0 && r
>= 0)
1085 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
1086 if (q
< 0 && r
>= 0)
1089 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
1090 if (q
< 0 && r
>= 0)
1093 q
= remove_entry_directory(arg_esp_path
);
1094 if (q
< 0 && r
>= 0)
1097 if (arg_xbootldr_path
) {
1098 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
1100 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
1101 if (q
< 0 && r
>= 0)
1104 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
1105 if (q
< 0 && r
>= 0)
1108 q
= remove_entry_directory(arg_xbootldr_path
);
1109 if (q
< 0 && r
>= 0)
1113 (void) sync_everything();
1115 if (!arg_touch_variables
)
1119 log_info("Not changing EFI variables with --all-architectures.");
1123 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
1124 q
= remove_variables(uuid
, path
, true);
1125 if (q
< 0 && r
>= 0)
1128 q
= remove_loader_variables();
1129 if (q
< 0 && r
>= 0)
1135 int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
1138 r
= acquire_esp(/* privileged_mode= */ false,
1139 /* graceful= */ arg_graceful
,
1140 NULL
, NULL
, NULL
, NULL
, NULL
);
1144 r
= are_we_installed(arg_esp_path
);
1151 return EXIT_SUCCESS
;
1155 return EXIT_FAILURE
;