1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include <linux/magic.h>
15 #include "alloc-util.h"
16 #include "blkid-util.h"
19 #include "chase-symlinks.h"
21 #include "devnum-util.h"
22 #include "dirent-util.h"
23 #include "dissect-image.h"
25 #include "efi-loader.h"
34 #include "glyph-util.h"
35 #include "main-func.h"
37 #include "mount-util.h"
40 #include "parse-argument.h"
41 #include "parse-util.h"
42 #include "pretty-print.h"
43 #include "random-util.h"
45 #include "stat-util.h"
46 #include "stdio-util.h"
47 #include "string-table.h"
48 #include "string-util.h"
50 #include "sync-util.h"
51 #include "terminal-util.h"
52 #include "tmpfile-util.h"
53 #include "tmpfile-util-label.h"
54 #include "tpm2-util.h"
55 #include "umask-util.h"
60 /* EFI_BOOT_OPTION_DESCRIPTION_MAX sets the maximum length for the boot option description
61 * stored in NVRAM. The UEFI spec does not specify a minimum or maximum length for this
62 * string, but we limit the length to something reasonable to prevent from the firmware
63 * having to deal with a potentially too long string. */
64 #define EFI_BOOT_OPTION_DESCRIPTION_MAX ((size_t) 255)
66 static char *arg_esp_path
= NULL
;
67 static char *arg_xbootldr_path
= NULL
;
68 static bool arg_print_esp_path
= false;
69 static bool arg_print_dollar_boot_path
= false;
70 static bool arg_touch_variables
= true;
71 static PagerFlags arg_pager_flags
= 0;
72 static bool arg_graceful
= false;
73 static bool arg_quiet
= false;
74 static int arg_make_entry_directory
= false; /* tri-state: < 0 for automatic logic */
75 static sd_id128_t arg_machine_id
= SD_ID128_NULL
;
76 static char *arg_install_layout
= NULL
;
78 ARG_ENTRY_TOKEN_MACHINE_ID
,
79 ARG_ENTRY_TOKEN_OS_IMAGE_ID
,
80 ARG_ENTRY_TOKEN_OS_ID
,
81 ARG_ENTRY_TOKEN_LITERAL
,
83 } arg_entry_token_type
= ARG_ENTRY_TOKEN_AUTO
;
84 static char *arg_entry_token
= NULL
;
85 static JsonFormatFlags arg_json_format_flags
= JSON_FORMAT_OFF
;
86 static bool arg_arch_all
= false;
87 static char *arg_root
= NULL
;
88 static char *arg_image
= NULL
;
90 ARG_INSTALL_SOURCE_IMAGE
,
91 ARG_INSTALL_SOURCE_HOST
,
92 ARG_INSTALL_SOURCE_AUTO
,
93 } arg_install_source
= ARG_INSTALL_SOURCE_AUTO
;
94 static char *arg_efi_boot_option_description
= NULL
;
96 STATIC_DESTRUCTOR_REGISTER(arg_esp_path
, freep
);
97 STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path
, freep
);
98 STATIC_DESTRUCTOR_REGISTER(arg_install_layout
, freep
);
99 STATIC_DESTRUCTOR_REGISTER(arg_entry_token
, freep
);
100 STATIC_DESTRUCTOR_REGISTER(arg_root
, freep
);
101 STATIC_DESTRUCTOR_REGISTER(arg_image
, freep
);
102 STATIC_DESTRUCTOR_REGISTER(arg_efi_boot_option_description
, freep
);
104 static const char *arg_dollar_boot_path(void) {
105 /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */
106 return arg_xbootldr_path
?: arg_esp_path
;
109 static const char *pick_efi_boot_option_description(void) {
110 return arg_efi_boot_option_description
?: "Linux Boot Manager";
113 static int acquire_esp(
114 bool unprivileged_mode
,
117 uint64_t *ret_pstart
,
119 sd_id128_t
*ret_uuid
,
125 /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on
126 * its own, except for ENOKEY (which is good, we want to show our own message in that case,
127 * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case
128 * we simply eat up the error here, so that --list and --status work too, without noise about
131 r
= find_esp_and_warn(arg_root
, arg_esp_path
, unprivileged_mode
, &np
, ret_part
, ret_pstart
, ret_psize
, ret_uuid
, ret_devid
);
134 return log_full_errno(arg_quiet
? LOG_DEBUG
: LOG_INFO
, r
,
135 "Couldn't find EFI system partition, skipping.");
137 return log_error_errno(r
,
138 "Couldn't find EFI system partition. It is recommended to mount it to /boot or /efi.\n"
139 "Alternatively, use --esp-path= to specify path to mount point.");
144 free_and_replace(arg_esp_path
, np
);
145 log_debug("Using EFI System Partition at %s.", arg_esp_path
);
150 static int acquire_xbootldr(
151 bool unprivileged_mode
,
152 sd_id128_t
*ret_uuid
,
158 r
= find_xbootldr_and_warn(arg_root
, arg_xbootldr_path
, unprivileged_mode
, &np
, ret_uuid
, ret_devid
);
160 log_debug_errno(r
, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT.");
161 arg_xbootldr_path
= mfree(arg_xbootldr_path
);
164 *ret_uuid
= SD_ID128_NULL
;
172 free_and_replace(arg_xbootldr_path
, np
);
173 log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path
);
178 static int load_etc_machine_id(void) {
181 r
= sd_id128_get_machine(&arg_machine_id
);
182 if (IN_SET(r
, -ENOENT
, -ENOMEDIUM
)) /* Not set or empty */
185 return log_error_errno(r
, "Failed to get machine-id: %m");
187 log_debug("Loaded machine ID %s from /etc/machine-id.", SD_ID128_TO_STRING(arg_machine_id
));
191 static int load_etc_machine_info(void) {
192 /* systemd v250 added support to store the kernel-install layout setting and the machine ID to use
193 * for setting up the ESP in /etc/machine-info. The newer /etc/kernel/entry-token file, as well as
194 * the $layout field in /etc/kernel/install.conf are better replacements for this though, hence this
195 * has been deprecated and is only returned for compatibility. */
196 _cleanup_free_
char *s
= NULL
, *layout
= NULL
;
199 r
= parse_env_file(NULL
, "/etc/machine-info",
200 "KERNEL_INSTALL_LAYOUT", &layout
,
201 "KERNEL_INSTALL_MACHINE_ID", &s
);
205 return log_error_errno(r
, "Failed to parse /etc/machine-info: %m");
209 log_notice("Read $KERNEL_INSTALL_MACHINE_ID from /etc/machine-info. "
210 "Please move it to /etc/kernel/entry-token.");
212 r
= sd_id128_from_string(s
, &arg_machine_id
);
214 return log_error_errno(r
, "Failed to parse KERNEL_INSTALL_MACHINE_ID=%s in /etc/machine-info: %m", s
);
216 log_debug("Loaded KERNEL_INSTALL_MACHINE_ID=%s from KERNEL_INSTALL_MACHINE_ID in /etc/machine-info.",
217 SD_ID128_TO_STRING(arg_machine_id
));
220 if (!isempty(layout
)) {
222 log_notice("Read $KERNEL_INSTALL_LAYOUT from /etc/machine-info. "
223 "Please move it to the layout= setting of /etc/kernel/install.conf.");
225 log_debug("KERNEL_INSTALL_LAYOUT=%s is specified in /etc/machine-info.", layout
);
226 free_and_replace(arg_install_layout
, layout
);
232 static int load_etc_kernel_install_conf(void) {
233 _cleanup_free_
char *layout
= NULL
;
236 r
= parse_env_file(NULL
, "/etc/kernel/install.conf",
241 return log_error_errno(r
, "Failed to parse /etc/kernel/install.conf: %m");
243 if (!isempty(layout
)) {
244 log_debug("layout=%s is specified in /etc/machine-info.", layout
);
245 free_and_replace(arg_install_layout
, layout
);
251 static int settle_entry_token(void) {
254 switch (arg_entry_token_type
) {
256 case ARG_ENTRY_TOKEN_AUTO
: {
257 _cleanup_free_
char *buf
= NULL
;
258 r
= read_one_line_file("/etc/kernel/entry-token", &buf
);
259 if (r
< 0 && r
!= -ENOENT
)
260 return log_error_errno(r
, "Failed to read /etc/kernel/entry-token: %m");
263 free_and_replace(arg_entry_token
, buf
);
264 arg_entry_token_type
= ARG_ENTRY_TOKEN_LITERAL
;
265 } else if (sd_id128_is_null(arg_machine_id
)) {
266 _cleanup_free_
char *id
= NULL
, *image_id
= NULL
;
268 r
= parse_os_release(NULL
,
269 "IMAGE_ID", &image_id
,
272 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
274 if (!isempty(image_id
)) {
275 free_and_replace(arg_entry_token
, image_id
);
276 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_IMAGE_ID
;
277 } else if (!isempty(id
)) {
278 free_and_replace(arg_entry_token
, id
);
279 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_ID
;
281 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set, and /etc/os-release carries no ID=/IMAGE_ID= fields.");
283 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
287 arg_entry_token_type
= ARG_ENTRY_TOKEN_MACHINE_ID
;
293 case ARG_ENTRY_TOKEN_MACHINE_ID
:
294 if (sd_id128_is_null(arg_machine_id
))
295 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "No machine ID set.");
297 r
= free_and_strdup_warn(&arg_entry_token
, SD_ID128_TO_STRING(arg_machine_id
));
303 case ARG_ENTRY_TOKEN_OS_IMAGE_ID
: {
304 _cleanup_free_
char *buf
= NULL
;
306 r
= parse_os_release(NULL
, "IMAGE_ID", &buf
);
308 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
311 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "IMAGE_ID= field not set in /etc/os-release.");
313 free_and_replace(arg_entry_token
, buf
);
317 case ARG_ENTRY_TOKEN_OS_ID
: {
318 _cleanup_free_
char *buf
= NULL
;
320 r
= parse_os_release(NULL
, "ID", &buf
);
322 return log_error_errno(r
, "Failed to load /etc/os-release: %m");
325 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "ID= field not set in /etc/os-release.");
327 free_and_replace(arg_entry_token
, buf
);
331 case ARG_ENTRY_TOKEN_LITERAL
:
332 assert(!isempty(arg_entry_token
)); /* already filled in by command line parser */
336 if (isempty(arg_entry_token
) || !(utf8_is_valid(arg_entry_token
) && string_is_safe(arg_entry_token
)))
337 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Selected entry token not valid: %s", arg_entry_token
);
339 log_debug("Using entry token: %s", arg_entry_token
);
343 static bool use_boot_loader_spec_type1(void) {
344 /* If the layout is not specified, or if it is set explicitly to "bls" we assume Boot Loader
345 * Specification Type #1 is the chosen format for our boot loader entries */
346 return !arg_install_layout
|| streq(arg_install_layout
, "bls");
349 static int settle_make_entry_directory(void) {
352 r
= load_etc_machine_id();
356 r
= load_etc_machine_info();
360 r
= load_etc_kernel_install_conf();
364 r
= settle_entry_token();
368 bool layout_type1
= use_boot_loader_spec_type1();
369 if (arg_make_entry_directory
< 0) { /* Automatic mode */
371 if (arg_entry_token
== ARG_ENTRY_TOKEN_MACHINE_ID
) {
372 r
= path_is_temporary_fs("/etc/machine-id");
374 return log_debug_errno(r
, "Couldn't determine whether /etc/machine-id is on a temporary file system: %m");
376 arg_make_entry_directory
= r
== 0;
378 arg_make_entry_directory
= true;
380 arg_make_entry_directory
= false;
383 if (arg_make_entry_directory
> 0 && !layout_type1
)
384 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
385 "KERNEL_INSTALL_LAYOUT=%s is configured, but Boot Loader Specification Type #1 entry directory creation was requested.",
391 /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */
392 static int get_file_version(int fd
, char **v
) {
402 if (fstat(fd
, &st
) < 0)
403 return log_error_errno(errno
, "Failed to stat EFI binary: %m");
405 r
= stat_verify_regular(&st
);
407 return log_error_errno(r
, "EFI binary is not a regular file: %m");
409 if (st
.st_size
< 27 || file_offset_beyond_memory_size(st
.st_size
)) {
414 buf
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
415 if (buf
== MAP_FAILED
)
416 return log_error_errno(errno
, "Failed to memory map EFI binary: %m");
418 s
= mempmem_safe(buf
, st
.st_size
- 8, "#### LoaderInfo: ", 17);
422 e
= memmem_safe(s
, st
.st_size
- (s
- buf
), " ####", 5);
423 if (!e
|| e
- s
< 3) {
424 r
= log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Malformed version string.");
428 x
= strndup(s
, e
- s
);
436 (void) munmap(buf
, st
.st_size
);
441 static const char *get_efi_arch(void) {
442 /* Detect EFI firmware architecture of the running system. On mixed mode systems, it could be 32bit
443 * while the kernel is running in 64bit. */
446 _cleanup_free_
char *platform_size
= NULL
;
449 r
= read_one_line_file("/sys/firmware/efi/fw_platform_size", &platform_size
);
451 return EFI_MACHINE_TYPE_NAME
;
453 log_warning_errno(r
, "Error reading EFI firmware word size, assuming '%i': %m", __WORDSIZE
);
454 return EFI_MACHINE_TYPE_NAME
;
457 if (streq(platform_size
, "64"))
458 return EFI_MACHINE_TYPE_NAME
;
459 if (streq(platform_size
, "32"))
463 "Unknown EFI firmware word size '%s', using default word size '%i' instead.",
468 return EFI_MACHINE_TYPE_NAME
;
471 static int enumerate_binaries(
472 const char *esp_path
,
478 _cleanup_closedir_
DIR *d
= NULL
;
479 _cleanup_free_
char *p
= NULL
;
487 r
= chase_symlinks_and_opendir(path
, esp_path
, CHASE_PREFIX_ROOT
, &p
, &d
);
491 return log_error_errno(r
, "Failed to read \"%s/%s\": %m", esp_path
, path
);
493 FOREACH_DIRENT(de
, d
, break) {
494 _cleanup_free_
char *v
= NULL
;
495 _cleanup_close_
int fd
= -1;
497 if (!endswith_no_case(de
->d_name
, ".efi"))
500 if (prefix
&& !startswith_no_case(de
->d_name
, prefix
))
503 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
505 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
507 r
= get_file_version(fd
, &v
);
511 if (*previous
) { /* let's output the previous entry now, since now we know that there will be one more, and can draw the tree glyph properly */
513 *is_first
? "File:" : " ",
514 special_glyph(SPECIAL_GLYPH_TREE_BRANCH
), *previous
);
516 *previous
= mfree(*previous
);
519 /* Do not output this entry immediately, but store what should be printed in a state
520 * variable, because we only will know the tree glyph to print (branch or final edge) once we
521 * read one more entry */
523 r
= asprintf(previous
, "/%s/%s (%s%s%s)", path
, de
->d_name
, ansi_highlight(), v
, ansi_normal());
525 r
= asprintf(previous
, "/%s/%s", path
, de
->d_name
);
535 static int status_binaries(const char *esp_path
, sd_id128_t partition
) {
536 _cleanup_free_
char *last
= NULL
;
537 bool is_first
= true;
540 printf("%sAvailable Boot Loaders on ESP:%s\n", ansi_underline(), ansi_normal());
543 printf(" ESP: Cannot find or access mount point of ESP.\n\n");
547 printf(" ESP: %s", esp_path
);
548 if (!sd_id128_is_null(partition
))
549 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")", SD_ID128_FORMAT_VAL(partition
));
552 r
= enumerate_binaries(esp_path
, "EFI/systemd", NULL
, &last
, &is_first
);
558 k
= enumerate_binaries(esp_path
, "EFI/BOOT", "boot", &last
, &is_first
);
564 if (last
) /* let's output the last entry now, since now we know that there will be no more, and can draw the tree glyph properly */
566 is_first
? "File:" : " ",
567 special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), last
);
569 if (r
== 0 && !arg_quiet
)
570 log_info("systemd-boot not installed in ESP.");
571 if (k
== 0 && !arg_quiet
)
572 log_info("No default/fallback boot loader installed in ESP.");
578 static int print_efi_option(uint16_t id
, int *n_printed
, bool in_order
) {
579 _cleanup_free_
char *title
= NULL
;
580 _cleanup_free_
char *path
= NULL
;
581 sd_id128_t partition
;
587 r
= efi_get_boot_option(id
, &title
, &partition
, &path
, &active
);
589 return log_error_errno(r
, "Failed to read boot option %u: %m", id
);
591 /* print only configured entries with partition information */
592 if (!path
|| sd_id128_is_null(partition
)) {
593 log_debug("Ignoring boot entry %u without partition information.", id
);
597 efi_tilt_backslashes(path
);
599 if (*n_printed
== 0) /* Print section title before first entry */
600 printf("%sBoot Loaders Listed in EFI Variables:%s\n", ansi_underline(), ansi_normal());
602 printf(" Title: %s%s%s\n", ansi_highlight(), strna(title
), ansi_normal());
603 printf(" ID: 0x%04X\n", id
);
604 printf(" Status: %sactive%s\n", active
? "" : "in", in_order
? ", boot-order" : "");
605 printf(" Partition: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
606 SD_ID128_FORMAT_VAL(partition
));
607 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), path
);
614 static int status_variables(void) {
615 _cleanup_free_
uint16_t *options
= NULL
, *order
= NULL
;
616 int n_options
, n_order
, n_printed
= 0;
618 n_options
= efi_get_boot_options(&options
);
619 if (n_options
== -ENOENT
)
620 return log_error_errno(n_options
,
621 "Failed to access EFI variables, efivarfs"
622 " needs to be available at /sys/firmware/efi/efivars/.");
624 return log_error_errno(n_options
, "Failed to read EFI boot entries: %m");
626 n_order
= efi_get_boot_order(&order
);
627 if (n_order
== -ENOENT
)
629 else if (n_order
< 0)
630 return log_error_errno(n_order
, "Failed to read EFI boot order: %m");
632 /* print entries in BootOrder first */
633 for (int i
= 0; i
< n_order
; i
++)
634 (void) print_efi_option(order
[i
], &n_printed
, /* in_order= */ true);
636 /* print remaining entries */
637 for (int i
= 0; i
< n_options
; i
++) {
638 for (int j
= 0; j
< n_order
; j
++)
639 if (options
[i
] == order
[j
])
642 (void) print_efi_option(options
[i
], &n_printed
, /* in_order= */ false);
649 printf("No boot loaders listed in EFI Variables.\n\n");
654 static int boot_config_load_and_select(
656 const char *esp_path
,
658 const char *xbootldr_path
,
659 dev_t xbootldr_devid
) {
663 /* If XBOOTLDR and ESP actually refer to the same block device, suppress XBOOTLDR, since it would
664 * find the same entries twice. */
665 bool same
= esp_path
&& xbootldr_path
&& devnum_set_and_equal(esp_devid
, xbootldr_devid
);
667 r
= boot_config_load(config
, esp_path
, same
? NULL
: xbootldr_path
);
672 _cleanup_strv_free_
char **efi_entries
= NULL
;
674 r
= efi_loader_get_entries(&efi_entries
);
675 if (r
== -ENOENT
|| ERRNO_IS_NOT_SUPPORTED(r
))
676 log_debug_errno(r
, "Boot loader reported no entries.");
678 log_warning_errno(r
, "Failed to determine entries reported by boot loader, ignoring: %m");
680 (void) boot_config_augment_from_loader(config
, efi_entries
, /* only_auto= */ false);
683 return boot_config_select_special_entries(config
, /* skip_efivars= */ !!arg_root
);
686 static int status_entries(
687 const BootConfig
*config
,
688 const char *esp_path
,
689 sd_id128_t esp_partition_uuid
,
690 const char *xbootldr_path
,
691 sd_id128_t xbootldr_partition_uuid
) {
693 sd_id128_t dollar_boot_partition_uuid
;
694 const char *dollar_boot_path
;
698 assert(esp_path
|| xbootldr_path
);
701 dollar_boot_path
= xbootldr_path
;
702 dollar_boot_partition_uuid
= xbootldr_partition_uuid
;
704 dollar_boot_path
= esp_path
;
705 dollar_boot_partition_uuid
= esp_partition_uuid
;
708 printf("%sBoot Loader Entries:%s\n"
709 " $BOOT: %s", ansi_underline(), ansi_normal(), dollar_boot_path
);
710 if (!sd_id128_is_null(dollar_boot_partition_uuid
))
711 printf(" (/dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
")",
712 SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid
));
715 if (config
->default_entry
< 0)
716 printf("%zu entries, no entry could be determined as default.\n", config
->n_entries
);
718 printf("%sDefault Boot Loader Entry:%s\n", ansi_underline(), ansi_normal());
721 boot_config_default_entry(config
),
722 /* show_as_default= */ false,
723 /* show_as_selected= */ false,
724 /* show_discovered= */ false);
726 /* < 0 is already logged by the function itself, let's just emit an extra warning if
727 the default entry is broken */
728 printf("\nWARNING: default boot entry is broken\n");
734 static int compare_product(const char *a
, const char *b
) {
743 return x
< y
? -1 : x
> y
? 1 : 0;
745 return strncmp(a
, b
, x
);
748 static int compare_version(const char *a
, const char *b
) {
752 a
+= strcspn(a
, " ");
754 b
+= strcspn(b
, " ");
757 return strverscmp_improved(a
, b
);
760 static int version_check(int fd_from
, const char *from
, int fd_to
, const char *to
) {
761 _cleanup_free_
char *a
= NULL
, *b
= NULL
;
764 assert(fd_from
>= 0);
769 r
= get_file_version(fd_from
, &a
);
773 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
774 "Source file \"%s\" does not carry version information!",
777 r
= get_file_version(fd_to
, &b
);
780 if (r
== 0 || compare_product(a
, b
) != 0)
781 return log_notice_errno(SYNTHETIC_ERRNO(EREMOTE
),
782 "Skipping \"%s\", since it's owned by another boot loader.",
785 r
= compare_version(a
, b
);
786 log_debug("Comparing versions: \"%s\" %s \"%s", a
, comparison_operator(r
), b
);
788 return log_warning_errno(SYNTHETIC_ERRNO(ESTALE
),
789 "Skipping \"%s\", since newer boot loader version in place already.", to
);
791 return log_info_errno(SYNTHETIC_ERRNO(ESTALE
),
792 "Skipping \"%s\", since same boot loader version in place already.", to
);
797 static int copy_file_with_version_check(const char *from
, const char *to
, bool force
) {
798 _cleanup_close_
int fd_from
= -1, fd_to
= -1;
799 _cleanup_free_
char *t
= NULL
;
802 fd_from
= open(from
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
804 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", from
);
807 fd_to
= open(to
, O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
810 return log_error_errno(errno
, "Failed to open \"%s\" for reading: %m", to
);
812 r
= version_check(fd_from
, from
, fd_to
, to
);
816 if (lseek(fd_from
, 0, SEEK_SET
) == (off_t
) -1)
817 return log_error_errno(errno
, "Failed to seek in \"%s\": %m", from
);
819 fd_to
= safe_close(fd_to
);
823 r
= tempfn_random(to
, NULL
, &t
);
827 RUN_WITH_UMASK(0000) {
828 fd_to
= open(t
, O_WRONLY
|O_CREAT
|O_CLOEXEC
|O_EXCL
|O_NOFOLLOW
, 0644);
830 return log_error_errno(errno
, "Failed to open \"%s\" for writing: %m", t
);
833 r
= copy_bytes(fd_from
, fd_to
, UINT64_MAX
, COPY_REFLINK
);
836 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
839 (void) copy_times(fd_from
, fd_to
, 0);
841 r
= fsync_full(fd_to
);
843 (void) unlink_noerrno(t
);
844 return log_error_errno(r
, "Failed to copy data from \"%s\" to \"%s\": %m", from
, t
);
847 if (renameat(AT_FDCWD
, t
, AT_FDCWD
, to
) < 0) {
848 (void) unlink_noerrno(t
);
849 return log_error_errno(errno
, "Failed to rename \"%s\" to \"%s\": %m", t
, to
);
852 log_info("Copied \"%s\" to \"%s\".", from
, to
);
857 static int mkdir_one(const char *prefix
, const char *suffix
) {
858 _cleanup_free_
char *p
= NULL
;
860 p
= path_join(prefix
, suffix
);
861 if (mkdir(p
, 0700) < 0) {
863 return log_error_errno(errno
, "Failed to create \"%s\": %m", p
);
865 log_info("Created \"%s\".", p
);
870 static const char *const esp_subdirs
[] = {
871 /* The directories to place in the ESP */
879 static const char *const dollar_boot_subdirs
[] = {
880 /* The directories to place in the XBOOTLDR partition or the ESP, depending what exists */
882 "loader/entries", /* Type #1 entries */
884 "EFI/Linux", /* Type #2 entries */
888 static int create_subdirs(const char *root
, const char * const *subdirs
) {
891 STRV_FOREACH(i
, subdirs
) {
892 r
= mkdir_one(root
, *i
);
900 static int copy_one_file(const char *esp_path
, const char *name
, bool force
) {
901 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
902 _cleanup_free_
char *source_path
= NULL
, *dest_path
= NULL
, *p
= NULL
, *q
= NULL
;
907 dest_name
= strdupa_safe(name
);
908 s
= endswith_no_case(dest_name
, ".signed");
912 p
= path_join(BOOTLIBDIR
, name
);
916 r
= chase_symlinks(p
, root
, CHASE_PREFIX_ROOT
, &source_path
, NULL
);
917 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
918 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
919 r
= chase_symlinks(p
, NULL
, CHASE_PREFIX_ROOT
, &source_path
, NULL
);
921 return log_error_errno(r
,
922 "Failed to resolve path %s%s%s: %m",
924 root
? " under directory " : "",
927 q
= path_join("/EFI/systemd/", dest_name
);
931 r
= chase_symlinks(q
, esp_path
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &dest_path
, NULL
);
933 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", q
, esp_path
);
935 /* Note that if this fails we do the second copy anyway, but return this error code,
936 * so we stash it away in a separate variable. */
937 ret
= copy_file_with_version_check(source_path
, dest_path
, force
);
939 e
= startswith(dest_name
, "systemd-boot");
941 _cleanup_free_
char *default_dest_path
= NULL
;
944 /* Create the EFI default boot loader name (specified for removable devices) */
945 v
= strjoina("/EFI/BOOT/BOOT", e
);
946 ascii_strupper(strrchr(v
, '/') + 1);
948 r
= chase_symlinks(v
, esp_path
, CHASE_PREFIX_ROOT
| CHASE_NONEXISTENT
, &default_dest_path
, NULL
);
950 return log_error_errno(r
, "Failed to resolve path %s under directory %s: %m", v
, esp_path
);
952 r
= copy_file_with_version_check(source_path
, default_dest_path
, force
);
953 if (r
< 0 && ret
== 0)
960 static int install_binaries(const char *esp_path
, const char *arch
, bool force
) {
961 char *root
= IN_SET(arg_install_source
, ARG_INSTALL_SOURCE_AUTO
, ARG_INSTALL_SOURCE_IMAGE
) ? arg_root
: NULL
;
962 _cleanup_closedir_
DIR *d
= NULL
;
963 _cleanup_free_
char *path
= NULL
;
966 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, root
, CHASE_PREFIX_ROOT
, &path
, &d
);
967 /* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
968 if (r
== -ENOENT
&& root
&& arg_install_source
== ARG_INSTALL_SOURCE_AUTO
)
969 r
= chase_symlinks_and_opendir(BOOTLIBDIR
, NULL
, CHASE_PREFIX_ROOT
, &path
, &d
);
971 return log_error_errno(r
, "Failed to open boot loader directory %s%s: %m", strempty(root
), BOOTLIBDIR
);
973 const char *suffix
= strjoina(arch
, ".efi");
974 const char *suffix_signed
= strjoina(arch
, ".efi.signed");
976 FOREACH_DIRENT(de
, d
, return log_error_errno(errno
, "Failed to read \"%s\": %m", path
)) {
979 if (!endswith_no_case(de
->d_name
, suffix
) && !endswith_no_case(de
->d_name
, suffix_signed
))
982 /* skip the .efi file, if there's a .signed version of it */
983 if (endswith_no_case(de
->d_name
, ".efi")) {
984 _cleanup_free_
const char *s
= strjoin(de
->d_name
, ".signed");
987 if (faccessat(dirfd(d
), s
, F_OK
, 0) >= 0)
991 k
= copy_one_file(esp_path
, de
->d_name
, force
);
992 /* Don't propagate an error code if no update necessary, installed version already equal or
993 * newer version, or other boot loader in place. */
994 if (arg_graceful
&& IN_SET(k
, -ESTALE
, -EREMOTE
))
1003 static bool same_entry(uint16_t id
, sd_id128_t uuid
, const char *path
) {
1004 _cleanup_free_
char *opath
= NULL
;
1008 r
= efi_get_boot_option(id
, NULL
, &ouuid
, &opath
, NULL
);
1011 if (!sd_id128_equal(uuid
, ouuid
))
1014 /* Some motherboards convert the path to uppercase under certain circumstances
1015 * (e.g. after booting into the Boot Menu in the ASUS ROG STRIX B350-F GAMING),
1016 * so use case-insensitive checking */
1017 if (!strcaseeq_ptr(path
, opath
))
1023 static int find_slot(sd_id128_t uuid
, const char *path
, uint16_t *id
) {
1024 _cleanup_free_
uint16_t *options
= NULL
;
1026 int n
= efi_get_boot_options(&options
);
1030 /* find already existing systemd-boot entry */
1031 for (int i
= 0; i
< n
; i
++)
1032 if (same_entry(options
[i
], uuid
, path
)) {
1037 /* find free slot in the sorted BootXXXX variable list */
1038 for (int i
= 0; i
< n
; i
++)
1039 if (i
!= options
[i
]) {
1044 /* use the next one */
1051 static int insert_into_order(uint16_t slot
, bool first
) {
1052 _cleanup_free_
uint16_t *order
= NULL
;
1056 n
= efi_get_boot_order(&order
);
1058 /* no entry, add us */
1059 return efi_set_boot_order(&slot
, 1);
1061 /* are we the first and only one? */
1062 if (n
== 1 && order
[0] == slot
)
1065 /* are we already in the boot order? */
1066 for (int i
= 0; i
< n
; i
++) {
1067 if (order
[i
] != slot
)
1070 /* we do not require to be the first one, all is fine */
1074 /* move us to the first slot */
1075 memmove(order
+ 1, order
, i
* sizeof(uint16_t));
1077 return efi_set_boot_order(order
, n
);
1081 t
= reallocarray(order
, n
+ 1, sizeof(uint16_t));
1086 /* add us to the top or end of the list */
1088 memmove(order
+ 1, order
, n
* sizeof(uint16_t));
1093 return efi_set_boot_order(order
, n
+ 1);
1096 static int remove_from_order(uint16_t slot
) {
1097 _cleanup_free_
uint16_t *order
= NULL
;
1100 n
= efi_get_boot_order(&order
);
1104 for (int i
= 0; i
< n
; i
++) {
1105 if (order
[i
] != slot
)
1109 memmove(order
+ i
, order
+ i
+1, (n
- i
) * sizeof(uint16_t));
1110 return efi_set_boot_order(order
, n
- 1);
1116 static int install_variables(
1117 const char *esp_path
,
1129 log_info("Acting on %s, skipping EFI variable setup.",
1130 arg_image
? "image" : "root directory");
1134 if (!is_efi_boot()) {
1135 log_warning("Not booted with EFI, skipping EFI variable setup.");
1139 r
= chase_symlinks_and_access(path
, esp_path
, CHASE_PREFIX_ROOT
, F_OK
, NULL
, NULL
);
1143 return log_error_errno(r
, "Cannot access \"%s/%s\": %m", esp_path
, path
);
1145 r
= find_slot(uuid
, path
, &slot
);
1147 return log_error_errno(r
,
1149 "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?" :
1150 "Failed to determine current boot order: %m");
1152 if (first
|| r
== 0) {
1153 r
= efi_add_boot_option(slot
, pick_efi_boot_option_description(),
1154 part
, pstart
, psize
,
1157 return log_error_errno(r
, "Failed to create EFI Boot variable entry: %m");
1159 log_info("Created EFI boot entry \"%s\".", pick_efi_boot_option_description());
1162 return insert_into_order(slot
, first
);
1165 static int remove_boot_efi(const char *esp_path
) {
1166 _cleanup_closedir_
DIR *d
= NULL
;
1167 _cleanup_free_
char *p
= NULL
;
1170 r
= chase_symlinks_and_opendir("/EFI/BOOT", esp_path
, CHASE_PREFIX_ROOT
, &p
, &d
);
1174 return log_error_errno(r
, "Failed to open directory \"%s/EFI/BOOT\": %m", esp_path
);
1176 FOREACH_DIRENT(de
, d
, break) {
1177 _cleanup_close_
int fd
= -1;
1178 _cleanup_free_
char *v
= NULL
;
1180 if (!endswith_no_case(de
->d_name
, ".efi"))
1183 if (!startswith_no_case(de
->d_name
, "boot"))
1186 fd
= openat(dirfd(d
), de
->d_name
, O_RDONLY
|O_CLOEXEC
);
1188 return log_error_errno(errno
, "Failed to open \"%s/%s\" for reading: %m", p
, de
->d_name
);
1190 r
= get_file_version(fd
, &v
);
1193 if (r
> 0 && startswith(v
, "systemd-boot ")) {
1194 r
= unlinkat(dirfd(d
), de
->d_name
, 0);
1196 return log_error_errno(errno
, "Failed to remove \"%s/%s\": %m", p
, de
->d_name
);
1198 log_info("Removed \"%s/%s\".", p
, de
->d_name
);
1207 static int rmdir_one(const char *prefix
, const char *suffix
) {
1210 p
= prefix_roota(prefix
, suffix
);
1212 bool ignore
= IN_SET(errno
, ENOENT
, ENOTEMPTY
);
1214 log_full_errno(ignore
? LOG_DEBUG
: LOG_ERR
, errno
,
1215 "Failed to remove directory \"%s\": %m", p
);
1219 log_info("Removed \"%s\".", p
);
1224 static int remove_subdirs(const char *root
, const char *const *subdirs
) {
1227 /* We use recursion here to destroy the directories in reverse order. Which should be safe given how
1228 * short the array is. */
1230 if (!subdirs
[0]) /* A the end of the list */
1233 r
= remove_subdirs(root
, subdirs
+ 1);
1234 q
= rmdir_one(root
, subdirs
[0]);
1236 return r
< 0 ? r
: q
;
1239 static int remove_entry_directory(const char *root
) {
1241 assert(arg_make_entry_directory
>= 0);
1243 if (!arg_make_entry_directory
|| !arg_entry_token
)
1246 return rmdir_one(root
, arg_entry_token
);
1249 static int remove_binaries(const char *esp_path
) {
1253 p
= prefix_roota(esp_path
, "/EFI/systemd");
1254 r
= rm_rf(p
, REMOVE_ROOT
|REMOVE_PHYSICAL
);
1256 q
= remove_boot_efi(esp_path
);
1257 if (q
< 0 && r
== 0)
1263 static int remove_file(const char *root
, const char *file
) {
1269 p
= prefix_roota(root
, file
);
1270 if (unlink(p
) < 0) {
1271 log_full_errno(errno
== ENOENT
? LOG_DEBUG
: LOG_ERR
, errno
,
1272 "Failed to unlink file \"%s\": %m", p
);
1274 return errno
== ENOENT
? 0 : -errno
;
1277 log_info("Removed \"%s\".", p
);
1281 static int remove_variables(sd_id128_t uuid
, const char *path
, bool in_order
) {
1285 if (arg_root
|| !is_efi_boot())
1288 r
= find_slot(uuid
, path
, &slot
);
1292 r
= efi_remove_boot_option(slot
);
1297 return remove_from_order(slot
);
1302 static int remove_loader_variables(void) {
1305 /* Remove all persistent loader variables we define */
1308 EFI_LOADER_VARIABLE(LoaderConfigTimeout
),
1309 EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
),
1310 EFI_LOADER_VARIABLE(LoaderEntryDefault
),
1311 EFI_LOADER_VARIABLE(LoaderEntryOneShot
),
1312 EFI_LOADER_VARIABLE(LoaderSystemToken
)){
1316 q
= efi_set_variable(var
, NULL
, 0);
1320 log_warning_errno(q
, "Failed to remove EFI variable %s: %m", var
);
1324 log_info("Removed EFI variable %s.", var
);
1330 static int install_loader_config(const char *esp_path
) {
1331 _cleanup_(unlink_and_freep
) char *t
= NULL
;
1332 _cleanup_fclose_
FILE *f
= NULL
;
1336 assert(arg_make_entry_directory
>= 0);
1338 p
= prefix_roota(esp_path
, "/loader/loader.conf");
1339 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
1342 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
1344 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
1346 fprintf(f
, "#timeout 3\n"
1347 "#console-mode keep\n");
1349 if (arg_make_entry_directory
) {
1350 assert(arg_entry_token
);
1351 fprintf(f
, "default %s-*\n", arg_entry_token
);
1354 r
= flink_tmpfile(f
, t
, p
);
1356 return 0; /* Silently skip creation if the file exists now (recheck) */
1358 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
1364 static int install_loader_specification(const char *root
) {
1365 _cleanup_(unlink_and_freep
) char *t
= NULL
;
1366 _cleanup_fclose_
FILE *f
= NULL
;
1367 _cleanup_free_
char *p
= NULL
;
1370 p
= path_join(root
, "/loader/entries.srel");
1374 if (access(p
, F_OK
) >= 0) /* Silently skip creation if the file already exists (early check) */
1377 r
= fopen_tmpfile_linkable(p
, O_WRONLY
|O_CLOEXEC
, &t
, &f
);
1379 return log_error_errno(r
, "Failed to open \"%s\" for writing: %m", p
);
1381 fprintf(f
, "type1\n");
1383 r
= flink_tmpfile(f
, t
, p
);
1385 return 0; /* Silently skip creation if the file exists now (recheck) */
1387 return log_error_errno(r
, "Failed to move \"%s\" into place: %m", p
);
1393 static int install_entry_directory(const char *root
) {
1395 assert(arg_make_entry_directory
>= 0);
1397 if (!arg_make_entry_directory
)
1400 assert(arg_entry_token
);
1401 return mkdir_one(root
, arg_entry_token
);
1404 static int install_entry_token(void) {
1407 assert(arg_make_entry_directory
>= 0);
1408 assert(arg_entry_token
);
1410 /* Let's save the used entry token in /etc/kernel/entry-token if we used it to create the entry
1411 * directory, or if anything else but the machine ID */
1413 if (!arg_make_entry_directory
&& arg_entry_token_type
== ARG_ENTRY_TOKEN_MACHINE_ID
)
1416 r
= write_string_file("/etc/kernel/entry-token", arg_entry_token
, WRITE_STRING_FILE_CREATE
|WRITE_STRING_FILE_ATOMIC
|WRITE_STRING_FILE_MKDIR_0755
);
1418 return log_error_errno(r
, "Failed to write entry token '%s' to /etc/kernel/entry-token", arg_entry_token
);
1423 static int help(int argc
, char *argv
[], void *userdata
) {
1424 _cleanup_free_
char *link
= NULL
;
1427 r
= terminal_urlify_man("bootctl", "1", &link
);
1431 printf("%1$s [OPTIONS...] COMMAND ...\n"
1432 "\n%5$sControl EFI firmware boot settings and manage boot loader.%6$s\n"
1433 "\n%3$sGeneric EFI Firmware/Boot Loader Commands:%4$s\n"
1434 " status Show status of installed boot loader and EFI variables\n"
1435 " reboot-to-firmware [BOOL]\n"
1436 " Query or set reboot-to-firmware EFI flag\n"
1437 " systemd-efi-options [STRING]\n"
1438 " Query or set system options string in EFI variable\n"
1439 "\n%3$sBoot Loader Specification Commands:%4$s\n"
1440 " list List boot loader entries\n"
1441 " set-default ID Set default boot loader entry\n"
1442 " set-oneshot ID Set default boot loader entry, for next boot only\n"
1443 " set-timeout SECONDS Set the menu timeout\n"
1444 " set-timeout-oneshot SECONDS\n"
1445 " Set the menu timeout for the next boot only\n"
1446 "\n%3$ssystemd-boot Commands:%4$s\n"
1447 " install Install systemd-boot to the ESP and EFI variables\n"
1448 " update Update systemd-boot in the ESP and EFI variables\n"
1449 " remove Remove systemd-boot from the ESP and EFI variables\n"
1450 " is-installed Test whether systemd-boot is installed in the ESP\n"
1451 " random-seed Initialize random seed in ESP and EFI variables\n"
1452 "\n%3$sOptions:%4$s\n"
1453 " -h --help Show this help\n"
1454 " --version Print version\n"
1455 " --esp-path=PATH Path to the EFI System Partition (ESP)\n"
1456 " --boot-path=PATH Path to the $BOOT partition\n"
1457 " --root=PATH Operate on an alternate filesystem root\n"
1458 " --image=PATH Operate on disk image as filesystem root\n"
1459 " --install-source=auto|image|host\n"
1460 " Where to pick files when using --root=/--image=\n"
1461 " -p --print-esp-path Print path to the EFI System Partition\n"
1462 " -x --print-boot-path Print path to the $BOOT partition\n"
1463 " --no-variables Don't touch EFI variables\n"
1464 " --no-pager Do not pipe output into a pager\n"
1465 " --graceful Don't fail when the ESP cannot be found or EFI\n"
1466 " variables cannot be written\n"
1467 " -q --quiet Suppress output\n"
1468 " --make-entry-directory=yes|no|auto\n"
1469 " Create $BOOT/ENTRY-TOKEN/ directory\n"
1470 " --entry-token=machine-id|os-id|os-image-id|auto|literal:…\n"
1471 " Entry token to use for this installation\n"
1472 " --json=pretty|short|off\n"
1473 " Generate JSON output\n"
1474 " --all-architectures\n"
1475 " Install all supported EFI architectures\n"
1476 " --efi-boot-option-description=DESCRIPTION\n"
1477 " Description of the entry in the boot option list\n"
1478 "\nSee the %2$s for details.\n",
1479 program_invocation_short_name
,
1489 static int parse_argv(int argc
, char *argv
[]) {
1491 ARG_ESP_PATH
= 0x100,
1500 ARG_MAKE_ENTRY_DIRECTORY
,
1504 ARG_EFI_BOOT_OPTION_DESCRIPTION
,
1507 static const struct option options
[] = {
1508 { "help", no_argument
, NULL
, 'h' },
1509 { "version", no_argument
, NULL
, ARG_VERSION
},
1510 { "esp-path", required_argument
, NULL
, ARG_ESP_PATH
},
1511 { "path", required_argument
, NULL
, ARG_ESP_PATH
}, /* Compatibility alias */
1512 { "boot-path", required_argument
, NULL
, ARG_BOOT_PATH
},
1513 { "root", required_argument
, NULL
, ARG_ROOT
},
1514 { "image", required_argument
, NULL
, ARG_IMAGE
},
1515 { "install-source", required_argument
, NULL
, ARG_INSTALL_SOURCE
},
1516 { "print-esp-path", no_argument
, NULL
, 'p' },
1517 { "print-path", no_argument
, NULL
, 'p' }, /* Compatibility alias */
1518 { "print-boot-path", no_argument
, NULL
, 'x' },
1519 { "no-variables", no_argument
, NULL
, ARG_NO_VARIABLES
},
1520 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1521 { "graceful", no_argument
, NULL
, ARG_GRACEFUL
},
1522 { "quiet", no_argument
, NULL
, 'q' },
1523 { "make-entry-directory", required_argument
, NULL
, ARG_MAKE_ENTRY_DIRECTORY
},
1524 { "make-machine-id-directory", required_argument
, NULL
, ARG_MAKE_ENTRY_DIRECTORY
}, /* Compatibility alias */
1525 { "entry-token", required_argument
, NULL
, ARG_ENTRY_TOKEN
},
1526 { "json", required_argument
, NULL
, ARG_JSON
},
1527 { "all-architectures", no_argument
, NULL
, ARG_ARCH_ALL
},
1528 { "efi-boot-option-description", required_argument
, NULL
, ARG_EFI_BOOT_OPTION_DESCRIPTION
},
1538 while ((c
= getopt_long(argc
, argv
, "hpx", options
, NULL
)) >= 0)
1542 help(0, NULL
, NULL
);
1549 r
= free_and_strdup(&arg_esp_path
, optarg
);
1555 r
= free_and_strdup(&arg_xbootldr_path
, optarg
);
1561 r
= parse_path_argument(optarg
, /* suppress_root= */ true, &arg_root
);
1567 r
= parse_path_argument(optarg
, /* suppress_root= */ false, &arg_image
);
1572 case ARG_INSTALL_SOURCE
:
1573 if (streq(optarg
, "auto"))
1574 arg_install_source
= ARG_INSTALL_SOURCE_AUTO
;
1575 else if (streq(optarg
, "image"))
1576 arg_install_source
= ARG_INSTALL_SOURCE_IMAGE
;
1577 else if (streq(optarg
, "host"))
1578 arg_install_source
= ARG_INSTALL_SOURCE_HOST
;
1580 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1581 "Unexpected parameter for --install-source=: %s", optarg
);
1586 if (arg_print_dollar_boot_path
)
1587 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1588 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
1589 arg_print_esp_path
= true;
1593 if (arg_print_esp_path
)
1594 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1595 "--print-boot-path/-x cannot be combined with --print-esp-path/-p");
1596 arg_print_dollar_boot_path
= true;
1599 case ARG_NO_VARIABLES
:
1600 arg_touch_variables
= false;
1604 arg_pager_flags
|= PAGER_DISABLE
;
1608 arg_graceful
= true;
1615 case ARG_ENTRY_TOKEN
: {
1618 if (streq(optarg
, "machine-id")) {
1619 arg_entry_token_type
= ARG_ENTRY_TOKEN_MACHINE_ID
;
1620 arg_entry_token
= mfree(arg_entry_token
);
1621 } else if (streq(optarg
, "os-image-id")) {
1622 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_IMAGE_ID
;
1623 arg_entry_token
= mfree(arg_entry_token
);
1624 } else if (streq(optarg
, "os-id")) {
1625 arg_entry_token_type
= ARG_ENTRY_TOKEN_OS_ID
;
1626 arg_entry_token
= mfree(arg_entry_token
);
1627 } else if ((e
= startswith(optarg
, "literal:"))) {
1628 arg_entry_token_type
= ARG_ENTRY_TOKEN_LITERAL
;
1630 r
= free_and_strdup_warn(&arg_entry_token
, e
);
1634 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1635 "Unexpected parameter for --entry-token=: %s", optarg
);
1640 case ARG_MAKE_ENTRY_DIRECTORY
:
1641 if (streq(optarg
, "auto")) /* retained for backwards compatibility */
1642 arg_make_entry_directory
= -1; /* yes if machine-id is permanent */
1644 r
= parse_boolean_argument("--make-entry-directory=", optarg
, &b
);
1648 arg_make_entry_directory
= b
;
1653 r
= parse_json_argument(optarg
, &arg_json_format_flags
);
1659 arg_arch_all
= true;
1662 case ARG_EFI_BOOT_OPTION_DESCRIPTION
:
1663 if (isempty(optarg
) || !(string_is_safe(optarg
) && utf8_is_valid(optarg
))) {
1664 _cleanup_free_
char *escaped
= NULL
;
1666 escaped
= cescape(optarg
);
1667 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1668 "Invalid --efi-boot-option-description=: %s", strna(escaped
));
1670 if (strlen(optarg
) > EFI_BOOT_OPTION_DESCRIPTION_MAX
)
1671 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1672 "--efi-boot-option-description= too long: %zu > %zu", strlen(optarg
), EFI_BOOT_OPTION_DESCRIPTION_MAX
);
1673 r
= free_and_strdup_warn(&arg_efi_boot_option_description
, optarg
);
1682 assert_not_reached();
1685 if ((arg_root
|| arg_image
) && argv
[optind
] && !STR_IN_SET(argv
[optind
], "status", "list",
1686 "install", "update", "remove", "is-installed", "random-seed"))
1687 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
1688 "Options --root= and --image= are not supported with verb %s.",
1691 if (arg_root
&& arg_image
)
1692 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Please specify either --root= or --image=, the combination of both is not supported.");
1694 if (arg_install_source
!= ARG_INSTALL_SOURCE_AUTO
&& !arg_root
&& !arg_image
)
1695 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "--install-from-host is only supported with --root= or --image=.");
1700 static void read_efi_var(const char *variable
, char **ret
) {
1703 r
= efi_get_variable_string(variable
, ret
);
1704 if (r
< 0 && r
!= -ENOENT
)
1705 log_warning_errno(r
, "Failed to read EFI variable %s: %m", variable
);
1708 static void print_yes_no_line(bool first
, bool good
, const char *name
) {
1710 first
? " Features: " : " ",
1711 COLOR_MARK_BOOL(good
),
1715 static int are_we_installed(const char *esp_path
) {
1718 /* Tests whether systemd-boot is installed. It's not obvious what to use as check here: we could
1719 * check EFI variables, we could check what binary /EFI/BOOT/BOOT*.EFI points to, or whether the
1720 * loader entries directory exists. Here we opted to check whether /EFI/systemd/ is non-empty, which
1721 * should be a suitable and very minimal check for a number of reasons:
1723 * → The check is architecture independent (i.e. we check if any systemd-boot loader is installed,
1724 * not a specific one.)
1726 * → It doesn't assume we are the only boot loader (i.e doesn't check if we own the main
1727 * /EFI/BOOT/BOOT*.EFI fallback binary.
1729 * → It specifically checks for systemd-boot, not for other boot loaders (which a check for
1730 * /boot/loader/entries would do). */
1732 _cleanup_free_
char *p
= path_join(esp_path
, "/EFI/systemd/");
1736 log_debug("Checking whether %s contains any files%s", p
, special_glyph(SPECIAL_GLYPH_ELLIPSIS
));
1737 r
= dir_is_empty(p
, /* ignore_hidden_or_backup= */ false);
1738 if (r
< 0 && r
!= -ENOENT
)
1739 return log_error_errno(r
, "Failed to check whether %s contains any files: %m", p
);
1744 static int verb_status(int argc
, char *argv
[], void *userdata
) {
1745 sd_id128_t esp_uuid
= SD_ID128_NULL
, xbootldr_uuid
= SD_ID128_NULL
;
1746 dev_t esp_devid
= 0, xbootldr_devid
= 0;
1749 r
= acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL
, NULL
, NULL
, &esp_uuid
, &esp_devid
);
1750 if (arg_print_esp_path
) {
1751 if (r
== -EACCES
) /* If we couldn't acquire the ESP path, log about access errors (which is the only
1752 * error the find_esp_and_warn() won't log on its own) */
1753 return log_error_errno(r
, "Failed to determine ESP location: %m");
1760 r
= acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, &xbootldr_uuid
, &xbootldr_devid
);
1761 if (arg_print_dollar_boot_path
) {
1763 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
1767 const char *path
= arg_dollar_boot_path();
1769 return log_error_errno(SYNTHETIC_ERRNO(EACCES
), "Failed to determine XBOOTLDR location: %m");
1774 if (arg_print_esp_path
|| arg_print_dollar_boot_path
)
1777 r
= 0; /* If we couldn't determine the path, then don't consider that a problem from here on, just
1778 * show what we can show */
1780 pager_open(arg_pager_flags
);
1782 if (!arg_root
&& is_efi_boot()) {
1783 static const struct {
1786 } loader_flags
[] = {
1787 { EFI_LOADER_FEATURE_BOOT_COUNTING
, "Boot counting" },
1788 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT
, "Menu timeout control" },
1789 { EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT
, "One-shot menu timeout control" },
1790 { EFI_LOADER_FEATURE_ENTRY_DEFAULT
, "Default entry control" },
1791 { EFI_LOADER_FEATURE_ENTRY_ONESHOT
, "One-shot entry control" },
1792 { EFI_LOADER_FEATURE_XBOOTLDR
, "Support for XBOOTLDR partition" },
1793 { EFI_LOADER_FEATURE_RANDOM_SEED
, "Support for passing random seed to OS" },
1794 { EFI_LOADER_FEATURE_LOAD_DRIVER
, "Load drop-in drivers" },
1795 { EFI_LOADER_FEATURE_SORT_KEY
, "Support Type #1 sort-key field" },
1796 { EFI_LOADER_FEATURE_SAVED_ENTRY
, "Support @saved pseudo-entry" },
1797 { EFI_LOADER_FEATURE_DEVICETREE
, "Support Type #1 devicetree field" },
1799 static const struct {
1803 { EFI_STUB_FEATURE_REPORT_BOOT_PARTITION
, "Stub sets ESP information" },
1804 { EFI_STUB_FEATURE_PICK_UP_CREDENTIALS
, "Picks up credentials from boot partition" },
1805 { EFI_STUB_FEATURE_PICK_UP_SYSEXTS
, "Picks up system extension images from boot partition" },
1806 { EFI_STUB_FEATURE_THREE_PCRS
, "Measures kernel+command line+sysexts" },
1808 _cleanup_free_
char *fw_type
= NULL
, *fw_info
= NULL
, *loader
= NULL
, *loader_path
= NULL
, *stub
= NULL
;
1809 sd_id128_t loader_part_uuid
= SD_ID128_NULL
;
1810 uint64_t loader_features
= 0, stub_features
= 0;
1814 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareType
), &fw_type
);
1815 read_efi_var(EFI_LOADER_VARIABLE(LoaderFirmwareInfo
), &fw_info
);
1816 read_efi_var(EFI_LOADER_VARIABLE(LoaderInfo
), &loader
);
1817 read_efi_var(EFI_LOADER_VARIABLE(StubInfo
), &stub
);
1818 read_efi_var(EFI_LOADER_VARIABLE(LoaderImageIdentifier
), &loader_path
);
1819 (void) efi_loader_get_features(&loader_features
);
1820 (void) efi_stub_get_features(&stub_features
);
1823 efi_tilt_backslashes(loader_path
);
1825 k
= efi_loader_get_device_part_uuid(&loader_part_uuid
);
1826 if (k
< 0 && k
!= -ENOENT
)
1827 r
= log_warning_errno(k
, "Failed to read EFI variable LoaderDevicePartUUID: %m");
1829 SecureBootMode secure
= efi_get_secure_boot_mode();
1830 printf("%sSystem:%s\n", ansi_underline(), ansi_normal());
1831 printf(" Firmware: %s%s (%s)%s\n", ansi_highlight(), strna(fw_type
), strna(fw_info
), ansi_normal());
1832 printf(" Firmware Arch: %s\n", get_efi_arch());
1833 printf(" Secure Boot: %sd (%s)\n",
1834 enable_disable(IN_SET(secure
, SECURE_BOOT_USER
, SECURE_BOOT_DEPLOYED
)),
1835 secure_boot_mode_to_string(secure
));
1838 printf(" TPM2 Support: %s%s%s\n",
1839 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? ansi_highlight_green() :
1840 (s
& (TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
)) != 0 ? ansi_highlight_red() : ansi_highlight_yellow(),
1841 FLAGS_SET(s
, TPM2_SUPPORT_FIRMWARE
|TPM2_SUPPORT_DRIVER
) ? "yes" :
1842 (s
& TPM2_SUPPORT_FIRMWARE
) ? "firmware only, driver unavailable" :
1843 (s
& TPM2_SUPPORT_DRIVER
) ? "driver only, firmware unavailable" : "no",
1846 k
= efi_get_reboot_to_firmware();
1848 printf(" Boot into FW: %sactive%s\n", ansi_highlight_yellow(), ansi_normal());
1850 printf(" Boot into FW: supported\n");
1851 else if (k
== -EOPNOTSUPP
)
1852 printf(" Boot into FW: not supported\n");
1855 printf(" Boot into FW: %sfailed%s (%m)\n", ansi_highlight_red(), ansi_normal());
1859 printf("%sCurrent Boot Loader:%s\n", ansi_underline(), ansi_normal());
1860 printf(" Product: %s%s%s\n", ansi_highlight(), strna(loader
), ansi_normal());
1862 for (size_t i
= 0; i
< ELEMENTSOF(loader_flags
); i
++)
1863 print_yes_no_line(i
== 0, FLAGS_SET(loader_features
, loader_flags
[i
].flag
), loader_flags
[i
].name
);
1865 sd_id128_t bootloader_esp_uuid
;
1866 bool have_bootloader_esp_uuid
= efi_loader_get_device_part_uuid(&bootloader_esp_uuid
) >= 0;
1868 print_yes_no_line(false, have_bootloader_esp_uuid
, "Boot loader sets ESP information");
1869 if (have_bootloader_esp_uuid
&& !sd_id128_is_null(esp_uuid
) &&
1870 !sd_id128_equal(esp_uuid
, bootloader_esp_uuid
))
1871 printf("WARNING: The boot loader reports a different ESP UUID than detected ("SD_ID128_UUID_FORMAT_STR
" vs. "SD_ID128_UUID_FORMAT_STR
")!\n",
1872 SD_ID128_FORMAT_VAL(bootloader_esp_uuid
),
1873 SD_ID128_FORMAT_VAL(esp_uuid
));
1876 printf(" Stub: %s\n", stub
);
1877 for (size_t i
= 0; i
< ELEMENTSOF(stub_flags
); i
++)
1878 print_yes_no_line(i
== 0, FLAGS_SET(stub_features
, stub_flags
[i
].flag
), stub_flags
[i
].name
);
1880 if (!sd_id128_is_null(loader_part_uuid
))
1881 printf(" ESP: /dev/disk/by-partuuid/" SD_ID128_UUID_FORMAT_STR
"\n",
1882 SD_ID128_FORMAT_VAL(loader_part_uuid
));
1884 printf(" ESP: n/a\n");
1885 printf(" File: %s%s\n", special_glyph(SPECIAL_GLYPH_TREE_RIGHT
), strna(loader_path
));
1888 printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
1889 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed
)), F_OK
) >= 0;
1890 printf(" Passed to OS: %s\n", yes_no(have
));
1891 have
= access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken
)), F_OK
) >= 0;
1892 printf(" System Token: %s\n", have
? "set" : "not set");
1895 _cleanup_free_
char *p
= NULL
;
1897 p
= path_join(arg_esp_path
, "/loader/random-seed");
1901 have
= access(p
, F_OK
) >= 0;
1902 printf(" Exists: %s\n", yes_no(have
));
1907 printf("%sSystem:%s\n"
1908 "Not booted with EFI\n\n",
1909 ansi_underline(), ansi_normal());
1912 k
= status_binaries(arg_esp_path
, esp_uuid
);
1917 if (!arg_root
&& is_efi_boot()) {
1918 k
= status_variables();
1923 if (arg_esp_path
|| arg_xbootldr_path
) {
1924 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
1926 k
= boot_config_load_and_select(&config
,
1927 arg_esp_path
, esp_devid
,
1928 arg_xbootldr_path
, xbootldr_devid
);
1932 k
= status_entries(&config
,
1933 arg_esp_path
, esp_uuid
,
1934 arg_xbootldr_path
, xbootldr_uuid
);
1943 static int verb_list(int argc
, char *argv
[], void *userdata
) {
1944 _cleanup_(boot_config_free
) BootConfig config
= BOOT_CONFIG_NULL
;
1945 dev_t esp_devid
= 0, xbootldr_devid
= 0;
1948 /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two
1949 * things: turn off logging about access errors and turn off potentially privileged device probing.
1950 * Here we're interested in the latter but not the former, hence request the mode, and log about
1953 r
= acquire_esp(/* unprivileged_mode= */ geteuid() != 0, /* graceful= */ false, NULL
, NULL
, NULL
, NULL
, &esp_devid
);
1954 if (r
== -EACCES
) /* We really need the ESP path for this call, hence also log about access errors */
1955 return log_error_errno(r
, "Failed to determine ESP location: %m");
1959 r
= acquire_xbootldr(/* unprivileged_mode= */ geteuid() != 0, NULL
, &xbootldr_devid
);
1961 return log_error_errno(r
, "Failed to determine XBOOTLDR partition: %m");
1965 r
= boot_config_load_and_select(&config
, arg_esp_path
, esp_devid
, arg_xbootldr_path
, xbootldr_devid
);
1969 if (config
.n_entries
== 0 && FLAGS_SET(arg_json_format_flags
, JSON_FORMAT_OFF
)) {
1970 log_info("No boot loader entries found.");
1974 pager_open(arg_pager_flags
);
1975 return show_boot_entries(&config
, arg_json_format_flags
);
1978 static int install_random_seed(const char *esp
) {
1979 _cleanup_(unlink_and_freep
) char *tmp
= NULL
;
1980 _cleanup_free_
void *buffer
= NULL
;
1981 _cleanup_free_
char *path
= NULL
;
1982 _cleanup_close_
int fd
= -1;
1983 size_t sz
, token_size
;
1989 path
= path_join(esp
, "/loader/random-seed");
1993 sz
= random_pool_size();
1995 buffer
= malloc(sz
);
1999 r
= crypto_random_bytes(buffer
, sz
);
2001 return log_error_errno(r
, "Failed to acquire random seed: %m");
2003 /* Normally create_subdirs() should already have created everything we need, but in case "bootctl
2004 * random-seed" is called we want to just create the minimum we need for it, and not the full
2006 r
= mkdir_parents(path
, 0755);
2008 return log_error_errno(r
, "Failed to create parent directory for %s: %m", path
);
2010 r
= tempfn_random(path
, "bootctl", &tmp
);
2014 fd
= open(tmp
, O_CREAT
|O_EXCL
|O_NOFOLLOW
|O_NOCTTY
|O_WRONLY
|O_CLOEXEC
, 0600);
2017 return log_error_errno(fd
, "Failed to open random seed file for writing: %m");
2020 n
= write(fd
, buffer
, sz
);
2022 return log_error_errno(errno
, "Failed to write random seed file: %m");
2023 if ((size_t) n
!= sz
)
2024 return log_error_errno(SYNTHETIC_ERRNO(EIO
), "Short write while writing random seed file.");
2026 if (rename(tmp
, path
) < 0)
2027 return log_error_errno(r
, "Failed to move random seed file into place: %m");
2031 log_info("Random seed file %s successfully written (%zu bytes).", path
, sz
);
2033 if (!arg_touch_variables
)
2036 if (!is_efi_boot()) {
2037 log_notice("Not booted with EFI, skipping EFI variable setup.");
2042 log_warning("Acting on %s, skipping EFI variable setup.",
2043 arg_image
? "image" : "root directory");
2047 r
= getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
2050 log_warning_errno(r
, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
2052 if (detect_vm() > 0) {
2053 /* Let's not write a system token if we detect we are running in a VM
2054 * environment. Why? Our default security model for the random seed uses the system
2055 * token as a mechanism to ensure we are not vulnerable to golden master sloppiness
2056 * issues, i.e. that people initialize the random seed file, then copy the image to
2057 * many systems and end up with the same random seed in each that is assumed to be
2058 * valid but in reality is the same for all machines. By storing a system token in
2059 * the EFI variable space we can make sure that even though the random seeds on disk
2060 * are all the same they will be different on each system under the assumption that
2061 * the EFI variable space is maintained separate from the random seed storage. That
2062 * is generally the case on physical systems, as the ESP is stored on persistent
2063 * storage, and the EFI variables in NVRAM. However in virtualized environments this
2064 * is generally not true: the EFI variable set is typically stored along with the
2065 * disk image itself. For example, using the OVMF EFI firmware the EFI variables are
2066 * stored in a file in the ESP itself. */
2068 log_notice("Not installing system token, since we are running in a virtualized environment.");
2071 } else if (r
== 0) {
2072 log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
2076 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken
), NULL
, NULL
, &token_size
);
2078 log_debug_errno(r
, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
2081 return log_error_errno(r
, "Failed to test system token validity: %m");
2083 if (token_size
>= sz
) {
2084 /* Let's avoid writes if we can, and initialize this only once. */
2085 log_debug("System token already written, not updating.");
2089 log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size
, sz
);
2092 r
= crypto_random_bytes(buffer
, sz
);
2094 return log_error_errno(r
, "Failed to acquire random seed: %m");
2096 /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
2097 * and possibly get identification information or too much insight into the kernel's entropy pool
2099 RUN_WITH_UMASK(0077) {
2100 r
= efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken
), buffer
, sz
);
2103 return log_error_errno(r
, "Failed to write 'LoaderSystemToken' EFI variable: %m");
2106 log_warning_errno(r
, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
2108 log_warning_errno(r
, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
2110 log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz
);
2116 static int sync_everything(void) {
2120 k
= syncfs_path(AT_FDCWD
, arg_esp_path
);
2122 ret
= log_error_errno(k
, "Failed to synchronize the ESP '%s': %m", arg_esp_path
);
2125 if (arg_xbootldr_path
) {
2126 k
= syncfs_path(AT_FDCWD
, arg_xbootldr_path
);
2128 ret
= log_error_errno(k
, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path
);
2134 static int verb_install(int argc
, char *argv
[], void *userdata
) {
2135 sd_id128_t uuid
= SD_ID128_NULL
;
2136 uint64_t pstart
= 0, psize
= 0;
2138 bool install
, graceful
;
2141 /* Invoked for both "update" and "install" */
2143 install
= streq(argv
[0], "install");
2144 graceful
= !install
&& arg_graceful
; /* support graceful mode for updates */
2146 r
= acquire_esp(/* unprivileged_mode= */ false, graceful
, &part
, &pstart
, &psize
, &uuid
, NULL
);
2147 if (graceful
&& r
== -ENOKEY
)
2148 return 0; /* If --graceful is specified and we can't find an ESP, handle this cleanly */
2153 /* If we are updating, don't do anything if sd-boot wasn't actually installed. */
2154 r
= are_we_installed(arg_esp_path
);
2158 log_debug("Skipping update because sd-boot is not installed in the ESP.");
2163 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
2167 r
= settle_make_entry_directory();
2171 const char *arch
= arg_arch_all
? "" : get_efi_arch();
2173 RUN_WITH_UMASK(0002) {
2175 /* Don't create any of these directories when we are just updating. When we update
2176 * we'll drop-in our files (unless there are newer ones already), but we won't create
2177 * the directories for them in the first place. */
2178 r
= create_subdirs(arg_esp_path
, esp_subdirs
);
2182 r
= create_subdirs(arg_dollar_boot_path(), dollar_boot_subdirs
);
2187 r
= install_binaries(arg_esp_path
, arch
, install
);
2192 r
= install_loader_config(arg_esp_path
);
2196 r
= install_entry_directory(arg_dollar_boot_path());
2200 r
= install_entry_token();
2204 r
= install_random_seed(arg_esp_path
);
2209 r
= install_loader_specification(arg_dollar_boot_path());
2214 (void) sync_everything();
2216 if (!arg_touch_variables
)
2220 log_info("Not changing EFI variables with --all-architectures.");
2224 char *path
= strjoina("/EFI/systemd/systemd-boot", arch
, ".efi");
2225 return install_variables(arg_esp_path
, part
, pstart
, psize
, uuid
, path
, install
);
2228 static int verb_remove(int argc
, char *argv
[], void *userdata
) {
2229 sd_id128_t uuid
= SD_ID128_NULL
;
2232 r
= acquire_esp(/* unprivileged_mode= */ false, /* graceful= */ false, NULL
, NULL
, NULL
, &uuid
, NULL
);
2236 r
= acquire_xbootldr(/* unprivileged_mode= */ false, NULL
, NULL
);
2240 r
= settle_make_entry_directory();
2244 r
= remove_binaries(arg_esp_path
);
2246 q
= remove_file(arg_esp_path
, "/loader/loader.conf");
2247 if (q
< 0 && r
>= 0)
2250 q
= remove_file(arg_esp_path
, "/loader/random-seed");
2251 if (q
< 0 && r
>= 0)
2254 q
= remove_file(arg_esp_path
, "/loader/entries.srel");
2255 if (q
< 0 && r
>= 0)
2258 q
= remove_subdirs(arg_esp_path
, esp_subdirs
);
2259 if (q
< 0 && r
>= 0)
2262 q
= remove_subdirs(arg_esp_path
, dollar_boot_subdirs
);
2263 if (q
< 0 && r
>= 0)
2266 q
= remove_entry_directory(arg_esp_path
);
2267 if (q
< 0 && r
>= 0)
2270 if (arg_xbootldr_path
) {
2271 /* Remove a subset of these also from the XBOOTLDR partition if it exists */
2273 q
= remove_file(arg_xbootldr_path
, "/loader/entries.srel");
2274 if (q
< 0 && r
>= 0)
2277 q
= remove_subdirs(arg_xbootldr_path
, dollar_boot_subdirs
);
2278 if (q
< 0 && r
>= 0)
2281 q
= remove_entry_directory(arg_xbootldr_path
);
2282 if (q
< 0 && r
>= 0)
2286 (void) sync_everything();
2288 if (!arg_touch_variables
)
2292 log_info("Not changing EFI variables with --all-architectures.");
2296 char *path
= strjoina("/EFI/systemd/systemd-boot", get_efi_arch(), ".efi");
2297 q
= remove_variables(uuid
, path
, true);
2298 if (q
< 0 && r
>= 0)
2301 q
= remove_loader_variables();
2302 if (q
< 0 && r
>= 0)
2308 static int verb_is_installed(int argc
, char *argv
[], void *userdata
) {
2311 r
= acquire_esp(/* privileged_mode= */ false,
2312 /* graceful= */ arg_graceful
,
2313 NULL
, NULL
, NULL
, NULL
, NULL
);
2317 r
= are_we_installed(arg_esp_path
);
2324 return EXIT_SUCCESS
;
2328 return EXIT_FAILURE
;
2332 static int parse_timeout(const char *arg1
, char16_t
**ret_timeout
, size_t *ret_timeout_size
) {
2333 char utf8
[DECIMAL_STR_MAX(usec_t
)];
2339 assert(ret_timeout
);
2340 assert(ret_timeout_size
);
2342 if (streq(arg1
, "menu-force"))
2343 timeout
= USEC_INFINITY
;
2344 else if (streq(arg1
, "menu-hidden"))
2347 r
= parse_time(arg1
, &timeout
, USEC_PER_SEC
);
2349 return log_error_errno(r
, "Failed to parse timeout '%s': %m", arg1
);
2350 if (timeout
!= USEC_INFINITY
&& timeout
> UINT32_MAX
* USEC_PER_SEC
)
2351 log_warning("Timeout is too long and will be treated as 'menu-force' instead.");
2354 xsprintf(utf8
, USEC_FMT
, MIN(timeout
/ USEC_PER_SEC
, UINT32_MAX
));
2356 encoded
= utf8_to_utf16(utf8
, strlen(utf8
));
2360 *ret_timeout
= encoded
;
2361 *ret_timeout_size
= char16_strlen(encoded
) * 2 + 2;
2365 static int parse_loader_entry_target_arg(const char *arg1
, char16_t
**ret_target
, size_t *ret_target_size
) {
2366 char16_t
*encoded
= NULL
;
2371 assert(ret_target_size
);
2373 if (streq(arg1
, "@current")) {
2374 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntrySelected
), NULL
, (void *) ret_target
, ret_target_size
);
2376 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntrySelected': %m");
2378 } else if (streq(arg1
, "@oneshot")) {
2379 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryOneShot
), NULL
, (void *) ret_target
, ret_target_size
);
2381 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntryOneShot': %m");
2383 } else if (streq(arg1
, "@default")) {
2384 r
= efi_get_variable(EFI_LOADER_VARIABLE(LoaderEntryDefault
), NULL
, (void *) ret_target
, ret_target_size
);
2386 return log_error_errno(r
, "Failed to get EFI variable 'LoaderEntryDefault': %m");
2388 } else if (arg1
[0] == '@' && !streq(arg1
, "@saved"))
2389 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
), "Unsupported special entry identifier: %s", arg1
);
2391 encoded
= utf8_to_utf16(arg1
, strlen(arg1
));
2395 *ret_target
= encoded
;
2396 *ret_target_size
= char16_strlen(encoded
) * 2 + 2;
2402 static int verb_set_efivar(int argc
, char *argv
[], void *userdata
) {
2406 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2407 "Acting on %s, skipping EFI variable setup.",
2408 arg_image
? "image" : "root directory");
2411 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2412 "Not booted with UEFI.");
2414 if (access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderInfo
)), F_OK
) < 0) {
2415 if (errno
== ENOENT
) {
2416 log_error_errno(errno
, "Not booted with a supported boot loader.");
2420 return log_error_errno(errno
, "Failed to detect whether boot loader supports '%s' operation: %m", argv
[0]);
2423 if (detect_container() > 0)
2424 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP
),
2425 "'%s' operation not supported in a container.",
2428 if (!arg_touch_variables
)
2429 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
2430 "'%s' operation cannot be combined with --no-variables.",
2433 const char *variable
;
2434 int (* arg_parser
)(const char *, char16_t
**, size_t *);
2436 if (streq(argv
[0], "set-default")) {
2437 variable
= EFI_LOADER_VARIABLE(LoaderEntryDefault
);
2438 arg_parser
= parse_loader_entry_target_arg
;
2439 } else if (streq(argv
[0], "set-oneshot")) {
2440 variable
= EFI_LOADER_VARIABLE(LoaderEntryOneShot
);
2441 arg_parser
= parse_loader_entry_target_arg
;
2442 } else if (streq(argv
[0], "set-timeout")) {
2443 variable
= EFI_LOADER_VARIABLE(LoaderConfigTimeout
);
2444 arg_parser
= parse_timeout
;
2445 } else if (streq(argv
[0], "set-timeout-oneshot")) {
2446 variable
= EFI_LOADER_VARIABLE(LoaderConfigTimeoutOneShot
);
2447 arg_parser
= parse_timeout
;
2449 assert_not_reached();
2451 if (isempty(argv
[1])) {
2452 r
= efi_set_variable(variable
, NULL
, 0);
2453 if (r
< 0 && r
!= -ENOENT
)
2454 return log_error_errno(r
, "Failed to remove EFI variable '%s': %m", variable
);
2456 _cleanup_free_ char16_t
*value
= NULL
;
2457 size_t value_size
= 0;
2459 r
= arg_parser(argv
[1], &value
, &value_size
);
2462 r
= efi_set_variable(variable
, value
, value_size
);
2464 return log_error_errno(r
, "Failed to update EFI variable '%s': %m", variable
);
2470 static int verb_random_seed(int argc
, char *argv
[], void *userdata
) {
2473 r
= find_esp_and_warn(arg_root
, arg_esp_path
, false, &arg_esp_path
, NULL
, NULL
, NULL
, NULL
, NULL
);
2475 /* find_esp_and_warn() doesn't warn about ENOKEY, so let's do that on our own */
2477 return log_error_errno(r
, "Unable to find ESP.");
2479 log_notice("No ESP found, not initializing random seed.");
2485 r
= install_random_seed(arg_esp_path
);
2489 (void) sync_everything();
2493 static int verb_systemd_efi_options(int argc
, char *argv
[], void *userdata
) {
2497 _cleanup_free_
char *line
= NULL
, *new = NULL
;
2499 r
= systemd_efi_options_variable(&line
);
2501 log_debug("No SystemdOptions EFI variable present in cache.");
2503 return log_error_errno(r
, "Failed to read SystemdOptions EFI variable from cache: %m");
2507 r
= systemd_efi_options_efivarfs_if_newer(&new);
2508 if (r
== -ENODATA
) {
2510 log_notice("Note: SystemdOptions EFI variable has been removed since boot.");
2512 log_warning_errno(r
, "Failed to check SystemdOptions EFI variable in efivarfs, ignoring: %m");
2513 else if (new && !streq_ptr(line
, new))
2514 log_notice("Note: SystemdOptions EFI variable has been modified since boot. New value: %s",
2517 r
= efi_set_variable_string(EFI_SYSTEMD_VARIABLE(SystemdOptions
), argv
[1]);
2519 return log_error_errno(r
, "Failed to set SystemdOptions EFI variable: %m");
2525 static int verb_reboot_to_firmware(int argc
, char *argv
[], void *userdata
) {
2529 r
= efi_get_reboot_to_firmware();
2532 return EXIT_SUCCESS
; /* success */
2536 return 1; /* recognizable error #1 */
2538 if (r
== -EOPNOTSUPP
) {
2539 puts("not supported");
2540 return 2; /* recognizable error #2 */
2543 log_error_errno(r
, "Failed to query reboot-to-firmware state: %m");
2544 return 3; /* other kind of error */
2546 r
= parse_boolean(argv
[1]);
2548 return log_error_errno(r
, "Failed to parse argument: %s", argv
[1]);
2550 r
= efi_set_reboot_to_firmware(r
);
2552 return log_error_errno(r
, "Failed to set reboot-to-firmware option: %m");
2558 static int bootctl_main(int argc
, char *argv
[]) {
2559 static const Verb verbs
[] = {
2560 { "help", VERB_ANY
, VERB_ANY
, 0, help
},
2561 { "status", VERB_ANY
, 1, VERB_DEFAULT
, verb_status
},
2562 { "install", VERB_ANY
, 1, 0, verb_install
},
2563 { "update", VERB_ANY
, 1, 0, verb_install
},
2564 { "remove", VERB_ANY
, 1, 0, verb_remove
},
2565 { "is-installed", VERB_ANY
, 1, 0, verb_is_installed
},
2566 { "list", VERB_ANY
, 1, 0, verb_list
},
2567 { "set-default", 2, 2, 0, verb_set_efivar
},
2568 { "set-oneshot", 2, 2, 0, verb_set_efivar
},
2569 { "set-timeout", 2, 2, 0, verb_set_efivar
},
2570 { "set-timeout-oneshot", 2, 2, 0, verb_set_efivar
},
2571 { "random-seed", VERB_ANY
, 1, 0, verb_random_seed
},
2572 { "systemd-efi-options", VERB_ANY
, 2, 0, verb_systemd_efi_options
},
2573 { "reboot-to-firmware", VERB_ANY
, 2, 0, verb_reboot_to_firmware
},
2577 return dispatch_verb(argc
, argv
, verbs
, NULL
);
2580 static int run(int argc
, char *argv
[]) {
2581 _cleanup_(loop_device_unrefp
) LoopDevice
*loop_device
= NULL
;
2582 _cleanup_(umount_and_rmdir_and_freep
) char *unlink_dir
= NULL
;
2585 log_parse_environment();
2588 /* If we run in a container, automatically turn off EFI file system access */
2589 if (detect_container() > 0)
2590 arg_touch_variables
= false;
2592 r
= parse_argv(argc
, argv
);
2596 /* Open up and mount the image */
2600 r
= mount_image_privately_interactively(
2602 DISSECT_IMAGE_GENERIC_ROOT
|
2603 DISSECT_IMAGE_RELAX_VAR_CHECK
,
2609 arg_root
= strdup(unlink_dir
);
2614 return bootctl_main(argc
, argv
);
2617 DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run
);